genai

MoonBit bindings for the Google GenAI (Gemini) JavaScript SDK

genai
gemini
google
ai
llm
sdk
Download zip
Author
Version
0.0.2
License
Apache-2.0
Last updated
6 months ago
Downloads
26

#tiye/genai

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.

MoonBit bindings for the Google Gemini Interactions API (JS backend, Node.js).

Tracked SDK: @google/genai v1.42.0


#Scope

This package covers only the Interactions API (ai.interactions.*). The following surfaces are intentionally excluded and will not be added:

Excluded surfaceReason
generateContent / generateContentStreamLegacy stateless API
Chat helper classWrapper around generateContent
Live (WebSocket streaming)Different transport model
Models, Files, Caches, OperationsOut of scope

If you need those surfaces, use the JS SDK directly or a different binding package.


#Implementation status

#Client

FeatureStatusNotes
GoogleGenAI::new(api_key)✅ done
GoogleGenAI::new_with_base_url(api_key, base_url)✅ doneFor reverse-proxy / internal network

#interactions.* methods

JS methodMoonBit methodStatusNotes
interactions.create(params)ai.interactions_create(params)✅ donenull fields are stripped before sending
interactions.create(stream)ai.interactions_create_stream(params, cb)✅ doneSSE 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 implementedNot yet in SDK stable surface
Tool-result submission❌ not implementedRequired for RequiresAction flow

#CreateParams field coverage

FieldTypeStatusNotes
modelString✅ done
inputInput✅ donePlain / Parts / Turns variants
previous_interaction_idString?✅ done
storeBool?✅ done
system_instructionString?✅ done
generation_configGenerationConfig?✅ donesee below
toolsArray[Tool]?✅ doneFunction / GoogleSearch / CodeExecution / URLContext / ComputerUse / MCPServer / FileSearch
response_modalitiesArray[String]?✅ done"text" / "image" / "audio"
response_formatJson?✅ donepass a JSON Schema
response_mime_typeString?✅ done
backgroundBool?✅ doneFor async long-running interactions + cancel (untested)
streamBool?✅ donePrefer interactions_create_stream instead

#GenerationConfig field coverage

FieldTypeStatus
max_output_tokensInt?✅ done
temperatureDouble?✅ done
top_pDouble?✅ done
seedInt?✅ done
stop_sequencesArray[String]?✅ done
thinking_levelString?✅ done
thinking_summariesString?✅ done
speech_configArray[SpeechConfig]?✅ done
image_configImageConfig?✅ doneaspect_ratio, image_size (untested)
tool_choiceJson?✅ doneFree-form JSON for mode etc. (untested)

#Interaction (response) field coverage

FieldTypeStatusNotes
idString✅ done
statusInteractionStatus✅ done
modelString?✅ done
outputsArray[Content]?✅ done
previous_interaction_idString?✅ done
roleString?✅ done
usageUsage?✅ done
createdString?✅ doneISO 8601
updatedString?✅ doneISO 8601
agentString?✅ doneAgent name (e.g. deep-research) (untested)

#Content block coverage

VariantEncodeDecodeNotes
Textincludes annotations
Imagebase64 data or uri
Audiobase64 data or uri
Documentuntested — PDF/doc input
Videountested — video input
Thoughtsignature + summary
FunctionCall
FunctionResult
CodeExecutionCalluntested
CodeExecutionResultuntested
GoogleSearchCalluntested
GoogleSearchResultuntested
URLContextCalluntested
URLContextResultuntested
MCPServerToolCalluntested
MCPServerToolResultuntested
FileSearchCalluntested
FileSearchResultuntested

#Streaming support

FeatureStatusNotes
interactions_create_stream(params, on_event)✅ doneCallback-based SSE streaming
parse_event(json_str)✅ doneDecodes SSE JSON into InteractionEvent
InteractionEvent enum✅ doneStart / StatusUpdate / ContentStart / Delta / Stop / Complete / Error / Unknown

#Promise / async utilities

FunctionStatus
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

#Tests

AreaStatus
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


#Examples (src/cmd/main/main.mbt)

ExampleCovered featuresStatus
Single-turn textInput::Plain, basic create + parse✅ verified
Stateful multi-turnprevious_interaction_id, store✅ verified
System instructionsystem_instruction✅ verified
Google Search toolTool::GoogleSearch, grounded responses✅ verified
Structured JSON outputresponse_format / response_mime_type✅ verified
Thinking (extended)thinking_level, Thought content parsing✅ verified
Streaminginteractions_create_stream, parse_event✅ verified
Input::Parts (multimodal)Image, Audio content blocks❌ missing
Input::Turns (stateless history)client-managed conversation❌ missing
Function callingTool::Function, RequiresAction flow❌ missing
Background interactionbackground: true + interactions_cancel❌ missing
interactions_getretrieve by id❌ missing
interactions_deletedelete by id❌ missing


#Known gaps (priority order for contributors)

  1. JSPromise rejection binding — add promise_catch FFI so MoonBit code can handle rejected promises in a typed way.
  2. Function-call round-trip exampleRequiresAction → submit tool results → Completed.
  3. Tests — encode/decode round-trips for all content types; snapshot tests for encode_create_params.
  4. interactions.list — once the SDK stabilises a list endpoint.
  5. Runtime verification of untested features — ComputerUse, MCPServer, FileSearch tools; Document/Video content; ImageConfig; multi-speaker SpeechConfig.speaker; background interactions.


#SDK version tracking

When upgrading @google/genai, check the following in node_modules/@google/genai/dist/index.d.ts (or the yarn cache zip):

  • BaseCreateModelInteractionParams — new fields → add to CreateParams + encoder
  • GenerationConfig_2 — new fields → add to GenerationConfig + encoder
  • Interaction response type — new fields → add to struct + decoder
  • Content_2 union — new variants → add to Content enum + encode/decode

Reference: /tmp/genai.d.ts was extracted from @google/genai@1.42.0.


#Requirements

  • MoonBit build target: js (Node.js)
  • npm package: @google/genai ≥ 1.42.0

yarn add @google/genai # Yarn PnP (used in this repo) # or npm install @google/genai


#Quick start

let 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()
},
))

Build and run:

moon build --target js GEMINI_API_KEY=<key> node --require ./.pnp.cjs \ ./_build/js/debug/build/cmd/main/main.js

GenaiError

pub(all) suberror GenaiError {
ParseFailed(String)
MissingField(String)
InvalidFormat(String)
}

Error raised when parsing or decoding fails.
impl Show for GenaiError

Annotation

pub(all) struct Annotation {
source : String?
start_index : Int?
end_index : Int?
}

An annotation (citation) attached to a [TextContent] block.
impl Show for Annotation

AudioContent

pub(all) struct AudioContent {
data : String?
mime_type : String?
uri : String?
}

An audio content block (type: "audio").

Provide either data (base-64) together with mime_type, or a uri.

CodeExecutionCallContent

pub(all) struct CodeExecutionCallContent {
id : String
arguments : Json
}

A code-execution call content block (type: "code_execution_call"). Untested — not yet verified at runtime.

CodeExecutionResultContent

pub(all) struct CodeExecutionResultContent {
id : String
output : String
}

A code-execution result content block (type: "code_execution_result"). Untested — not yet verified at runtime.

ComputerUseTool

pub(all) struct ComputerUseTool {
environment : String?
excluded_predefined_functions : Array[String]?
}

Configuration for the Computer-Use tool. Untested — not yet verified at runtime.

Content

pub(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)
}

Discriminated union of all content-block variants produced by / sent to the model via the Interactions API.
impl Show for Content

CreateParams

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?
}

Parameters for interactions.create.

DocumentContent

pub(all) struct DocumentContent {
data : String?
mime_type : String?
uri : String?
}

A document content block (type: "document"). Untested — not yet verified at runtime.

FileSearchCallContent

pub(all) struct FileSearchCallContent {
id : String
arguments : Json
}

A file-search call content block (type: "file_search_call"). Untested — not yet verified at runtime.

FileSearchResultContent

pub(all) struct FileSearchResultContent {
id : String
result : Json
}

A file-search result content block (type: "file_search_result"). Untested — not yet verified at runtime.

FileSearchTool

pub(all) struct FileSearchTool {
file_search_store_names : Array[String]?
top_k : Int?
metadata_filter : String?
}

Configuration for the File Search tool. Untested — not yet verified at runtime.

FunctionCallContent

pub(all) struct FunctionCallContent {
id : String
name : String
arguments : Json
}

A function-call content block (type: "function_call").

FunctionResultContent

pub(all) struct FunctionResultContent {
call_id : String
result : Json
name : String?
is_error : Bool?
}

A function-result content block (type: "function_result").

FunctionTool

pub(all) struct FunctionTool {
name : String
description : String?
parameters : Json?
}

A function-type tool declaration.

GenerationConfig

pub(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?
}

Optional generation configuration for an interaction.

GoogleGenAI

pub(all) struct GoogleGenAI {
// private fields
}

A GoogleGenAI client wrapping the @google/genai JS SDK.

Only the Interactions API surface is exposed here. Example:

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 Interaction

GoogleGenAI::interactions_cancel

fn GoogleGenAI::interactions_cancel(self : GoogleGenAI, id : String) -> JSPromise[String]

Cancel a background interaction that is still running.

Only applies to interactions started with background: true in [GenerationConfig]. Returns a JSPromise resolving to the updated [Interaction].

GoogleGenAI::interactions_create

fn GoogleGenAI::interactions_create(self : GoogleGenAI, params : CreateParams) -> JSPromise[String]

Create a new interaction and return a JSPromise that resolves to a JSON-encoded [Interaction] string.

Use [parse_interaction] to convert the resolved string to a typed [Interaction] value.

Example (stateless, single turn)

let 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,
})

Example (stateful multi-turn)

let p2 = ai.interactions_create({
...,
input: Input::Plain("What about sunsets?"),
previous_interaction_id: Some(first_interaction_id),
})

GoogleGenAI::interactions_create_stream

fn GoogleGenAI::interactions_create_stream(self : GoogleGenAI, params : CreateParams, on_event : (String) -> Unit) -> JSPromise[Unit]

Create an interaction with streaming. Each SSE event is delivered to on_event as a JSON string; use [parse_event] to decode it.

Untested — not yet verified at runtime.

Example

let p = ai.interactions_create_stream(
{
model: "gemini-2.5-flash",
input: Input::Plain("Hello!"),
..CreateParams::default()
},
fn(event_json) {
// handle each SSE event
},
)

GoogleGenAI::interactions_delete

fn GoogleGenAI::interactions_delete(self : GoogleGenAI, id : String) -> JSPromise[Unit]

Delete an interaction by its id.

GoogleGenAI::interactions_get

fn GoogleGenAI::interactions_get(self : GoogleGenAI, id : String) -> JSPromise[String]

Retrieve a previously created interaction by its id.

Returns a JSPromise that resolves to a JSON-encoded [Interaction].

GoogleGenAI::new

fn GoogleGenAI::new(api_key : String) -> GoogleGenAI

Create a new GoogleGenAI client.

let ai = GoogleGenAI::new("YOUR_GEMINI_API_KEY")

GoogleGenAI::new_with_base_url

fn GoogleGenAI::new_with_base_url(api_key : String, base_url : String) -> GoogleGenAI

Create a GoogleGenAI client that routes all requests through a custom base_url (e.g. an internal reverse-proxy).

let ai = GoogleGenAI::new_with_base_url(
"YOUR_GEMINI_API_KEY", "https://ja3.chenyong.life",
)

GoogleSearchCallContent

pub(all) struct GoogleSearchCallContent {
id : String
arguments : Json
}

A Google Search call content block (type: "google_search_call"). Untested — not yet verified at runtime.

GoogleSearchResultContent

pub(all) struct GoogleSearchResultContent {
id : String
result : Json
}

A Google Search result content block (type: "google_search_result"). Untested — not yet verified at runtime.

ImageConfig

pub(all) struct ImageConfig {
aspect_ratio : String?
image_size : String?
}

Configuration for image generation. Untested — not yet verified at runtime.
impl Show for ImageConfig

ImageContent

pub(all) struct ImageContent {
data : String?
mime_type : String?
resolution : String?
uri : String?
}

An image content block (type: "image").

Provide either data (base-64) together with mime_type, or a uri.

Input

pub(all) enum Input {
Plain(String)
Parts(Array[Content])
Turns(Array[Turn])
}

Input accepted by [GoogleGenAI::interactions_create].

  • Plain(text) — a single text string.
  • Parts(contents) — one or more typed content blocks.
  • Turns(turns) — a full conversation history (for stateless multi-turn).
impl Show for Input

Interaction

pub(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?
}

The Interaction resource returned by the API.
impl Show for Interaction

InteractionEvent

pub(all) enum InteractionEvent {
InteractionStart(Json)
StatusUpdate(String)
ContentStart(Int, String)
ContentDelta(Int, Json)
ContentStop(Int)
InteractionComplete(Json)
Error(Json)
Unknown(String, Json)
}

SSE event types emitted during a streamed interaction. Untested — not yet verified at runtime.

InteractionStatus

pub(all) enum InteractionStatus {
InProgress
RequiresAction
Completed
Failed
Cancelled
Incomplete
}

Possible states of an [Interaction] returned by the API.

JSGoogleGenAI

#external
pub(all) type JSGoogleGenAI

Opaque handle to the native GoogleGenAI JS object.

JSPromise

#external
pub(all) type JSPromise[A]

Opaque handle to a JavaScript Promise<A>.

The type parameter A is phantom — it tracks what type the promise resolves to in MoonBit's type system, but carries no runtime information.

MCPServerTool

pub(all) struct MCPServerTool {
url : String?
name : String?
headers : Json?
allowed_tools : Json?
}

Configuration for a remote MCP server tool. Untested — not yet verified at runtime.

MCPServerToolCallContent

pub(all) struct MCPServerToolCallContent {
id : String
name : String
arguments : Json
}

An MCP server tool call content block (type: "mcp_server_tool_call"). Untested — not yet verified at runtime.

MCPServerToolResultContent

pub(all) struct MCPServerToolResultContent {
id : String
result : Json
}

An MCP server tool result content block (type: "mcp_server_tool_result"). Untested — not yet verified at runtime.

SpeechConfig

pub(all) struct SpeechConfig {
voice : String?
language : String?
speaker : String?
}

A voice+language pair for speech output.

TextContent

pub(all) struct TextContent {
text : String?
annotations : Array[Annotation]?
}

A text content block (type: "text").
impl Show for TextContent

ThoughtContent

pub(all) struct ThoughtContent {
signature : String?
summary : Json?
}

A thought content block (type: "thought"). Untested — not yet verified at runtime.

Tool

pub(all) enum Tool {
Function(FunctionTool)
GoogleSearch
CodeExecution
URLContext
ComputerUse(ComputerUseTool)
MCPServer(MCPServerTool)
FileSearch(FileSearchTool)
}

Built-in tool types that the model can use.
impl Show for Tool

Turn

pub(all) struct Turn {
content : TurnContent?
role : String?
}

A single conversation turn (user or model).
impl Show for Turn

TurnContent

pub(all) enum TurnContent {
Plain(String)
Parts(Array[Content])
}

Content within a [Turn] — either a plain string or structured blocks.
impl Show for TurnContent

URLContextCallContent

pub(all) struct URLContextCallContent {
id : String
arguments : Json
}

A URL-context call content block (type: "url_context_call"). Untested — not yet verified at runtime.

URLContextResultContent

pub(all) struct URLContextResultContent {
id : String
result : Json
}

A URL-context result content block (type: "url_context_result"). Untested — not yet verified at runtime.

Usage

pub(all) struct Usage {
total_input_tokens : Int?
total_output_tokens : Int?
total_cached_tokens : Int?
}

Token-usage counters attached to a completed [Interaction].
impl Show for Usage

VideoContent

pub(all) struct VideoContent {
data : String?
mime_type : String?
resolution : String?
uri : String?
}

A video content block (type: "video"). Untested — not yet verified at runtime.

get_env

fn get_env(key : String) -> String

Read a Node.js environment variable. Returns "" if not set.

parse_event

fn parse_event(json_str : String) -> InteractionEvent raise GenaiError

Parse a JSON-encoded SSE event string into an [InteractionEvent]. Untested — not yet verified at runtime.

parse_interaction

fn parse_interaction(json_str : String) -> Interaction raise GenaiError

Parse a JSON string (as returned by the raw FFI) into an [Interaction]. Returns an error string if parsing fails.

promise_and_then

fn promise_and_then(p : JSPromise[String], f : (String) -> JSPromise[Unit]) -> JSPromise[Unit]

Chain a JSPromise[String] — calls f with the resolved string and returns a new JSPromise[Unit]. Mirrors .then() in JavaScript.

promise_seq

fn promise_seq(p : JSPromise[Unit], f : () -> JSPromise[Unit]) -> JSPromise[Unit]

Sequence two JSPromise[Unit] values — run f after p resolves.

promise_unit

fn promise_unit() -> JSPromise[Unit]

A JSPromise that is already resolved to Unit.

run_async

fn run_async(p : JSPromise[Unit]) -> Unit

Run a JSPromise[Unit] as the top-level async operation. Logs errors to stderr and exits with code 1 on rejection.

Source Files