MoonBit bindings for the Google GenAI (Gemini) JavaScript SDK
This project is AI-generated. The code and documentation were produced with GitHub Copilot and are maintained with AI assistance. PRs are welcome — please include a runnable example in src/cmd/main/main.mbt that exercises the new feature, so the AI agent can verify correctness by running it.
| Excluded surface | Reason |
|---|---|
| generateContent / generateContentStream | Legacy stateless API |
| Chat helper class | Wrapper around generateContent |
| Live (WebSocket streaming) | Different transport model |
| Models, Files, Caches, Operations | Out of scope |
| Feature | Status | Notes |
|---|---|---|
| GoogleGenAI::new(api_key) | ✅ done | |
| GoogleGenAI::new_with_base_url(api_key, base_url) | ✅ done | For reverse-proxy / internal network |
| JS method | MoonBit method | Status | Notes |
|---|---|---|---|
| interactions.create(params) | ai.interactions_create(params) | ✅ done | null fields are stripped before sending |
| interactions.create(stream) | ai.interactions_create_stream(params, cb) | ✅ done | SSE callback-based streaming |
| interactions.get(id) | ai.interactions_get(id) | ✅ done | |
| interactions.cancel(id) | ai.interactions_cancel(id) | ✅ done | |
| interactions.delete(id) | ai.interactions_delete(id) | ✅ done | |
| interactions.list(...) | — | ❌ not implemented | Not yet in SDK stable surface |
| Tool-result submission | — | ❌ not implemented | Required for RequiresAction flow |
| Field | Type | Status | Notes |
|---|---|---|---|
| model | String | ✅ done | |
| input | Input | ✅ done | Plain / Parts / Turns variants |
| previous_interaction_id | String? | ✅ done | |
| store | Bool? | ✅ done | |
| system_instruction | String? | ✅ done | |
| generation_config | GenerationConfig? | ✅ done | see below |
| tools | Array[Tool]? | ✅ done | Function / GoogleSearch / CodeExecution / URLContext / ComputerUse / MCPServer / FileSearch |
| response_modalities | Array[String]? | ✅ done | "text" / "image" / "audio" |
| response_format | Json? | ✅ done | pass a JSON Schema |
| response_mime_type | String? | ✅ done | |
| background | Bool? | ✅ done | For async long-running interactions + cancel (untested) |
| stream | Bool? | ✅ done | Prefer interactions_create_stream instead |
| Field | Type | Status | |
|---|---|---|---|
| max_output_tokens | Int? | ✅ done | |
| temperature | Double? | ✅ done | |
| top_p | Double? | ✅ done | |
| seed | Int? | ✅ done | |
| stop_sequences | Array[String]? | ✅ done | |
| thinking_level | String? | ✅ done | |
| thinking_summaries | String? | ✅ done | |
| speech_config | Array[SpeechConfig]? | ✅ done | |
| image_config | ImageConfig? | ✅ done | aspect_ratio, image_size (untested) |
| tool_choice | Json? | ✅ done | Free-form JSON for mode etc. (untested) |
| Field | Type | Status | Notes |
|---|---|---|---|
| id | String | ✅ done | |
| status | InteractionStatus | ✅ done | |
| model | String? | ✅ done | |
| outputs | Array[Content]? | ✅ done | |
| previous_interaction_id | String? | ✅ done | |
| role | String? | ✅ done | |
| usage | Usage? | ✅ done | |
| created | String? | ✅ done | ISO 8601 |
| updated | String? | ✅ done | ISO 8601 |
| agent | String? | ✅ done | Agent name (e.g. deep-research) (untested) |
| Variant | Encode | Decode | Notes |
|---|---|---|---|
| Text | ✅ | ✅ | includes annotations |
| Image | ✅ | ✅ | base64 data or uri |
| Audio | ✅ | ✅ | base64 data or uri |
| Document | ✅ | ✅ | untested — PDF/doc input |
| Video | ✅ | ✅ | untested — video input |
| Thought | ✅ | ✅ | signature + summary |
| FunctionCall | ✅ | ✅ | |
| FunctionResult | ✅ | ✅ | |
| CodeExecutionCall | ✅ | ✅ | untested |
| CodeExecutionResult | ✅ | ✅ | untested |
| GoogleSearchCall | ✅ | ✅ | untested |
| GoogleSearchResult | ✅ | ✅ | untested |
| URLContextCall | ✅ | ✅ | untested |
| URLContextResult | ✅ | ✅ | untested |
| MCPServerToolCall | ✅ | ✅ | untested |
| MCPServerToolResult | ✅ | ✅ | untested |
| FileSearchCall | ✅ | ✅ | untested |
| FileSearchResult | ✅ | ✅ | untested |
| Feature | Status | Notes |
|---|---|---|
| interactions_create_stream(params, on_event) | ✅ done | Callback-based SSE streaming |
| parse_event(json_str) | ✅ done | Decodes SSE JSON into InteractionEvent |
| InteractionEvent enum | ✅ done | Start / StatusUpdate / ContentStart / Delta / Stop / Complete / Error / Unknown |
| Function | Status |
|---|---|
| promise_and_then(p, f) | ✅ done |
| promise_seq(p, f) | ✅ done |
| promise_unit() | ✅ done |
| run_async(p) | ✅ done |
| get_env(key) | ✅ done |
| Promise error handling in MoonBit | ❌ missing — JSPromise rejection is untyped; no catch binding yet |
| Area | Status |
|---|---|
| Unit tests (genai_test.mbt) | ❌ empty |
| White-box tests (genai_wbtest.mbt) | ❌ empty |
| Encode round-trip tests | ❌ missing |
| Decode round-trip tests | ❌ missing |
| Snapshot tests | ❌ missing |
| Example | Covered features | Status |
|---|---|---|
| Single-turn text | Input::Plain, basic create + parse | ✅ verified |
| Stateful multi-turn | previous_interaction_id, store | ✅ verified |
| System instruction | system_instruction | ✅ verified |
| Google Search tool | Tool::GoogleSearch, grounded responses | ✅ verified |
| Structured JSON output | response_format / response_mime_type | ✅ verified |
| Thinking (extended) | thinking_level, Thought content parsing | ✅ verified |
| Streaming | interactions_create_stream, parse_event | ✅ verified |
| Input::Parts (multimodal) | Image, Audio content blocks | ❌ missing |
| Input::Turns (stateless history) | client-managed conversation | ❌ missing |
| Function calling | Tool::Function, RequiresAction flow | ❌ missing |
| Background interaction | background: true + interactions_cancel | ❌ missing |
| interactions_get | retrieve by id | ❌ missing |
| interactions_delete | delete by id | ❌ missing |
yarn add @google/genai # Yarn PnP (used in this repo)
# or
npm install @google/genailet ai = GoogleGenAI::new(get_env("GEMINI_API_KEY"))
// or, via a reverse proxy:
// let ai = GoogleGenAI::new_with_base_url(key, "https://your-proxy.example.com")
run_async(promise_and_then(
ai.interactions_create({
model: "gemini-2.5-flash",
input: Input::Plain("Why is the sky blue?"),
previous_interaction_id: None,
generation_config: None,
tools: None,
response_modalities: None,
response_format: None,
response_mime_type: None,
system_instruction: None,
store: None,
background: None,
stream: None,
}),
fn(json) {
let ia = parse_interaction(json) catch { e => { println(e); return promise_unit() } }
println(ia.id)
promise_unit()
},
))moon build --target js
GEMINI_API_KEY=<key> node --require ./.pnp.cjs \
./_build/js/debug/build/cmd/main/main.jspub(all) suberror GenaiError {
ParseFailed(String)
MissingField(String)
InvalidFormat(String)
}impl Show for GenaiErrorpub(all) struct Annotation {
source : String?
start_index : Int?
end_index : Int?
}impl Show for Annotationpub(all) struct AudioContent {
data : String?
mime_type : String?
uri : String?
}impl Show for AudioContentimpl Show for CodeExecutionCallContentpub(all) struct CodeExecutionResultContent {
id : String
output : String
}impl Show for CodeExecutionResultContentpub(all) struct ComputerUseTool {
environment : String?
excluded_predefined_functions : Array[String]?
}impl Show for ComputerUseToolpub(all) enum Content {
Text(TextContent)
Image(ImageContent)
Audio(AudioContent)
Document(DocumentContent)
Video(VideoContent)
Thought(ThoughtContent)
FunctionCall(FunctionCallContent)
FunctionResult(FunctionResultContent)
CodeExecutionCall(CodeExecutionCallContent)
CodeExecutionResult(CodeExecutionResultContent)
GoogleSearchCall(GoogleSearchCallContent)
GoogleSearchResult(GoogleSearchResultContent)
URLContextCall(URLContextCallContent)
URLContextResult(URLContextResultContent)
MCPServerToolCall(MCPServerToolCallContent)
MCPServerToolResult(MCPServerToolResultContent)
FileSearchCall(FileSearchCallContent)
FileSearchResult(FileSearchResultContent)
}pub(all) struct CreateParams {
model : String
input : Input
previous_interaction_id : String?
generation_config : GenerationConfig?
tools : Array[Tool]?
response_modalities : Array[String]?
response_format : Json?
response_mime_type : String?
system_instruction : String?
store : Bool?
background : Bool?
stream : Bool?
}impl Show for CreateParamspub(all) struct DocumentContent {
data : String?
mime_type : String?
uri : String?
}impl Show for DocumentContentimpl Show for FileSearchCallContentimpl Show for FileSearchResultContentpub(all) struct FileSearchTool {
file_search_store_names : Array[String]?
top_k : Int?
metadata_filter : String?
}impl Show for FileSearchToolimpl Show for FunctionCallContentpub(all) struct FunctionResultContent {
call_id : String
result : Json
name : String?
is_error : Bool?
}impl Show for FunctionResultContentpub(all) struct GenerationConfig {
max_output_tokens : Int?
temperature : Double?
top_p : Double?
seed : Int?
stop_sequences : Array[String]?
thinking_level : String?
thinking_summaries : String?
speech_config : Array[SpeechConfig]?
image_config : ImageConfig?
tool_choice : Json?
}impl Show for GenerationConfigpub(all) struct GoogleGenAI {
// private fields
}let ai = GoogleGenAI::new("YOUR_GEMINI_API_KEY")
let p = ai.interactions_create({
model: "gemini-2.5-flash",
input: Input::Plain("Hello!"),
previous_interaction_id: None,
generation_config: None,
tools: None,
response_modalities: None,
response_format: None,
response_mime_type: None,
system_instruction: None,
store: None,
})
// `p` is `JSPromise[String]` — resolves to a JSON-encoded Interactionlet p = ai.interactions_create({
model: "gemini-2.5-flash",
input: Input::Plain("Why is the sky blue?"),
previous_interaction_id: None,
generation_config: None,
tools: None,
response_modalities: None,
response_format: None,
response_mime_type: None,
system_instruction: None,
store: None,
})let p2 = ai.interactions_create({
...,
input: Input::Plain("What about sunsets?"),
previous_interaction_id: Some(first_interaction_id),
})fn GoogleGenAI::interactions_create_stream(self : GoogleGenAI, params : CreateParams, on_event : (String) -> Unit) -> JSPromise[Unit]let p = ai.interactions_create_stream(
{
model: "gemini-2.5-flash",
input: Input::Plain("Hello!"),
..CreateParams::default()
},
fn(event_json) {
// handle each SSE event
},
)let ai = GoogleGenAI::new("YOUR_GEMINI_API_KEY")let ai = GoogleGenAI::new_with_base_url(
"YOUR_GEMINI_API_KEY", "https://ja3.chenyong.life",
)impl Show for GoogleSearchCallContentimpl Show for GoogleSearchResultContentpub(all) struct ImageConfig {
aspect_ratio : String?
image_size : String?
}impl Show for ImageConfigpub(all) struct ImageContent {
data : String?
mime_type : String?
resolution : String?
uri : String?
}impl Show for ImageContentpub(all) struct Interaction {
id : String
status : InteractionStatus
model : String?
outputs : Array[Content]?
previous_interaction_id : String?
role : String?
usage : Usage?
created : String?
updated : String?
agent : String?
}impl Show for Interactionimpl Show for InteractionEventpub(all) enum InteractionStatus {
InProgress
RequiresAction
Completed
Failed
Cancelled
Incomplete
}impl Eq for InteractionStatusimpl Show for InteractionStatus#external
pub(all) type JSGoogleGenAI#external
pub(all) type JSPromise[A]impl Show for MCPServerToolimpl Show for MCPServerToolCallContentimpl Show for MCPServerToolResultContentpub(all) struct SpeechConfig {
voice : String?
language : String?
speaker : String?
}impl Show for SpeechConfigimpl Show for ThoughtContentpub(all) enum Tool {
Function(FunctionTool)
GoogleSearch
CodeExecution
URLContext
ComputerUse(ComputerUseTool)
MCPServer(MCPServerTool)
FileSearch(FileSearchTool)
}impl Show for TurnContentimpl Show for URLContextCallContentimpl Show for URLContextResultContentpub(all) struct Usage {
total_input_tokens : Int?
total_output_tokens : Int?
total_cached_tokens : Int?
}pub(all) struct VideoContent {
data : String?
mime_type : String?
resolution : String?
uri : String?
}impl Show for VideoContentfn get_env(key : String) -> StringInstall
Download zipMoonBit bindings for the Google GenAI (Gemini) JavaScript SDK