Skip to content

Google GenAI JS SDK ​

The official @google/genai package, on plugboard's Gemini API.

Connect ​

The SDK adds /v1beta itself, so the base URL is the plugboard origin:

ts
import { GoogleGenAI } from "@google/genai"

const ai = new GoogleGenAI({ apiKey: process.env.PLUGBOARD_KEY, httpOptions: { baseUrl: "http://127.0.0.1:4010" } })

Chat ​

ts
const response = await ai.models.generateContent({ model, contents: "Reply with exactly: pong", config: { maxOutputTokens: 1024 } })

Tool calls ​

Gemini calls carry no id: the function name pairs a functionResponse with its call.

ts
const tools = [
  {
    functionDeclarations: [
      {
        name: "get_weather",
        description: "Current weather for a city",
        parametersJsonSchema: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
      },
    ],
  },
]
const contents: Content[] = [{ role: "user", parts: [{ text: "What is the weather in Paris? Use the tool." }] }]
const first = await ai.models.generateContent({ model, contents, config: { tools, maxOutputTokens: 1024 } })
const call = first.functionCalls?.[0]
// Send the model's turn back as it came, then the result as a functionResponse. Gemini calls carry no id: the name pairs them.
const turn: Content[] = [...contents, ...(first.candidates?.[0]?.content ? [first.candidates[0].content] : [])]
if (call) turn.push({ role: "user", parts: [{ functionResponse: { name: call.name, response: { temp_c: 18, sky: "cloudy" } } }] })
const second = await ai.models.generateContent({ model, contents: turn, config: { tools, maxOutputTokens: 1024 } })

Streaming ​

ts
const stream = await ai.models.generateContentStream({ model, contents: "Reply with exactly: pong", config: { maxOutputTokens: 1024 } })
let text = ""
let finish: string | null = null
let usage: unknown = null
for await (const chunk of stream) {
  text += chunk.text ?? "" // each chunk carries the next text delta
  finish = chunk.candidates?.[0]?.finishReason?.toLowerCase() ?? finish
  usage = chunk.usageMetadata ?? usage
}

A streamed tool call arrives whole, in one chunk with its full args:

ts
const tools = [
  {
    functionDeclarations: [
      {
        name: "get_weather",
        description: "Current weather for a city",
        parametersJsonSchema: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
      },
    ],
  },
]
const stream = await ai.models.generateContentStream({
  model,
  contents: "What is the weather in Paris? Use the tool.",
  config: { tools, maxOutputTokens: 1024 },
})
let call: { name?: string; args?: Record<string, unknown>; id?: string } | undefined
let finish: string | null = null
for await (const chunk of stream) {
  call = chunk.functionCalls?.[0] ?? call
  finish = chunk.candidates?.[0]?.finishReason?.toLowerCase() ?? finish
}

Images ​

ts
// A 32px red PNG (examples/_shared/red32.dataurl.txt). Gemini takes the base64 bytes and the mime type, not a data URL.
const dataUrl = readFileSync(new URL("../_shared/red32.dataurl.txt", import.meta.url), "utf8").trim()
const base64 = dataUrl.slice(dataUrl.indexOf(",") + 1)
const response = await ai.models.generateContent({
  model,
  contents: [
    {
      role: "user",
      parts: [
        { text: "What color is this image? One word." },
        { inlineData: { mimeType: "image/png", data: base64 } },
      ],
    },
  ],
  config: { maxOutputTokens: 1024 },
})

Structured output ​

Pass a JSON schema as responseJsonSchema with responseMimeType: "application/json":

ts
// Gemini enforces responseJsonSchema on the model's output, unlike plugboard's forced-tool route for other APIs.
const schema = {
  type: "object",
  properties: { calories: { type: "number" }, protein_g: { type: "number" } },
  required: ["calories", "protein_g"],
  additionalProperties: false,
}
const response = await ai.models.generateContent({
  model,
  contents: "Estimate: 2 boiled eggs and a slice of toast.",
  config: { responseMimeType: "application/json", responseJsonSchema: schema, maxOutputTokens: 1024 },
})

Files ​

ai.files.upload({ file: "doc.pdf" }) uploads through plugboard; pass the returned file's uri in a fileData part. See Files.

Model names ​

A # in a model name must be sent as %23 (openai/gpt-5.6-luna%23low); a / goes as it is. Aliases avoid both. See Gemini model names.

Limits ​

  • A wrong key or an unknown model throws ApiError with the HTTP status; the body names Google's status (UNAUTHENTICATED, NOT_FOUND).
  • Gemini reports STOP on a turn with a function call too; check the parts for a functionCall.
  • The schema in structured output is not enforced by the provider. Validate the answer.