Polyweave

@polyweave/google (1.0.11)

Published 2026-07-11 15:25:47 +00:00 by Dvorak

Installation

@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/
npm install @polyweave/google@1.0.11
"@polyweave/google": "1.0.11"

About this package

@polyweave/google

Google Gemini adapter for Polyweave — multimodal chat, embeddings, vision, and live audio sessions.

Published as @polyweave/google.

Synopsis

import { createGoogleAdapter } from '@polyweave/google';
import { createTextFrame, createFrameSink } from '@polyweave/core';

var adapter = createGoogleAdapter({ apiKey: process.env.GEMINI_API_KEY });

adapter.source.pipe(createFrameSink(function(frame) {
  if (frame.type === 'TEXT') console.log(frame.content.text);
  if (frame.type === 'RECEIPT') console.log('Cost:', frame.content.totalCost);
  if (frame.type === 'ERROR') console.error(frame.content.message);
}));

// Chat
adapter.sink.write(createTextFrame('Write a haiku about clouds'));

// Vision (URL-based)
adapter.sink.write({ type: 'IMAGE', content: { url: 'https://example.com/photo.png', text: 'Describe this image' } });

// Embeddings
adapter.sink.write({ type: 'PARAM', content: { key: 'embedding_model', value: 'text-embedding-004' } });
adapter.sink.write({ type: 'EMBEDDING', content: { text: 'sample text to embed' } });

// Cost check before sending
adapter.sink.write({ type: 'COST_CHECK', content: { maxBudget: 0.05 } });
adapter.sink.write(createTextFrame('Large prompt...'));

Install

npm install @polyweave/google

Why

This is the Google Gemini adapter for Polyweave. It speaks Google's native API format (contents/parts model), not OpenAI-compatible. Supports multimodal chat with streaming SSE, vision via URL-based fileData.fileUri input, text embeddings via embedContent, and function calling. Also supports the Gemini Live API (WebSocket-based bidirectional audio+text) for models containing live, native-audio, or realtime in their name — routed through createGeminiLiveSession. No external HTTP library dependency — uses Node.js built-in https. API key is passed as a query parameter (not Bearer token).

Models: gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.5-pro, gemini-2.0-flash, gemini-2.0-flash-lite, gemini-2.0-pro, gemini-1.5-flash, gemini-1.5-pro, gemini-1.5-flash-8b, text-embedding-004, gemini-embedding, aqa.

API

createGoogleAdapter(config)

Creates a Google Gemini frame duplex adapter.

Parameter Type Required Default Description
config.apiKey string No process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || null Google API key
config.baseURL string No 'https://generativelanguage.googleapis.com/v1beta' Base URL for the Gemini API
config.model string No 'gemini-2.5-flash' Default model (settable via PARAM model key)
config.budget number No 0 Hard budget cap in dollars. TEXT/IMAGE frames are rejected once totalSpent >= budget
config.name string No null Node name for PARAM targeting (set on duplex.name)
config.timeout number No 120000 Request timeout in ms

Return value: IFrameDuplex with source and sink, plus setReady(key) and name.

  • source.sink — the downstream consumer (set via source.pipe(sink))
  • sink.write(frame) — push frames in
  • duplex.setReady(key) — set/update API key. Flushes buffered frames.
  • duplex.name — the configured name from config.name

Buffering: If apiKey is not set at construction time, frames are buffered in frameBuffer until setReady(key) is called. Once ready, buffered frames are drained.

Budget enforcement: Before TEXT or IMAGE frames are processed, if budgetTotal > 0 and totalSpent >= budgetTotal, the adapter emits ERROR (code: 'BUDGET_EXCEEDED') + END and drops the frame.

Frame Protocol

Sink (input frames):

Frame Type Content Properties Behavior
TEXT content.text (string) Chat completion. For live models, routes through WebSocket createGeminiLiveSession. Otherwise appends to contents array and calls sendGeminiChat.
IMAGE content.data || content.b64_json, content.mimeType (default 'image/png'), content.url, content.text || content.prompt Vision. URL-based: calls sendGeminiVision (non-streaming). Inline data: appends to contents with inlineData and calls sendGeminiChat.
AUDIO content.data (buffer or base64 string) Only routed for live models — creates/uses a WebSocket createGeminiLiveSession. Non-live models: silently dropped (no handler emits error).
EMBEDDING content.text (string) Calls sendEmbedding with model from params.embedding_model ('text-embedding-004' default)
PARAM content.key, content.value Sets params. Special keys: apiKey calls setReady(v), budget sets budgetTotal. All other keys stored in params object.
COST_CHECK content.maxBudget (number, optional) Enters cost check mode. Buffers subsequent frames until non-ESTIMATED_OUTPUT frame arrives, then calls costEstimator.estimate(params, buffer, 'chat'). Emits COST frame + END. If maxBudget exceeded, emits ERROR + END instead.
ESTIMATED_OUTPUT — (silently consumed in cost check mode) In cost check mode, this frame is swallowed without breaking the cost check buffer.
START Silently ignored
END Silently ignored
TOPOLOGY_SCAN content.path (array, optional) Emits TOPOLOGY_REPORT with adapter metadata, then passes through
TEST_INJECT content.frames (array) Recursively writes each frame in the array
TEST_SNAPSHOT Emits TEST_SNAPSHOT back

Source (output frames):

Frame Type Emitted When Content
TEXT Chat stream chunks or batch response text { text: deltaText } with metadata { model, usage?, finishReason? }
TOOL_CALL Function call detected in stream or batch { name, arguments: { toolName, args } } with id = toolCallId
EMBEDDING Embedding response { vector, dimensions, model }
RECEIPT Stream progress (every 50 tokens), stream end, batch end, error (partial), embedding completion { inputTokens, outputTokens, inputCost, outputCost, totalCost } with provider/model metadata
COST After COST_CHECK completes { bounds, accuracy, estimateSource, forwardEstimate, inputBounds, maxOutputTokens, ... } with provider/model/confidence metadata
ERROR HTTP errors, parse errors, budget exceeded, stream errors { message, code } (code: 'BUDGET_EXCEEDED' or 'ADAPTER_ERROR')
END After every completion or error { id, streamId, metadata: { model, error?, ... } }
TOPOLOGY_REPORT After TOPOLOGY_SCAN Adapter topology metadata

PARAM keys:

model, max_tokens, temperature, top_p, stream, system, tools, tool_choice, embedding_model, task_type, budget, apiKey, voice (live), instructions (live), modalities (live), input_audio_format (live)

Error conditions:

Condition Error code Frame type Description
Budget exceeded (pre-check) 'BUDGET_EXCEEDED' ERROR + END TEXT/IMAGE dropped when totalSpent >= budgetTotal
Estimated cost exceeds budget 'BUDGET_EXCEEDED' ERROR + END COST_CHECK with maxBudget fails estimate
HTTP error from Gemini API 'ADAPTER_ERROR' ERROR + END Status >= 400, message from response body error.message
Request timeout 'ADAPTER_ERROR' ERROR + END req.on('timeout') fires
Connection error 'ADAPTER_ERROR' ERROR + END req.on('error') fires, err.message propagated
JSON parse error 'ADAPTER_ERROR' ERROR + END JSON.parse fails on response, message: 'Parse: ...'
Stream error 'ADAPTER_ERROR' ERROR + END event.error.message from SSE stream
WebSocket error (live) ErrorCode.PROVIDER_ERROR ERROR + END Live session transport failure
WebSocket close (non-1000, live) ErrorCode.PROVIDER_ERROR ERROR + END Live session unclean close

On any error, if duplex._outputTokenCount > 0, the adapter emits a final progressive RECEIPT for tokens accumulated so far, updates totalSpent, then emits ERROR + END.

createGeminiLiveSession(params, duplex, requestConfig)

Creates a Gemini Live API WebSocket session for bidirectional audio+text streaming.

Parameter Type Description
params object Model params — model, system/instructions, voice, temperature, tools, modalities, input_audio_format
duplex IFrameDuplex The adapter duplex to emit frames through
requestConfig object { apiKey, baseURL } — API key and optional base URL

Returns: function handleFrame(frame) — call this with incoming frames (TEXT, AUDIO, PARAM, END).

Frame routing within live session:

Frame type Action
AUDIO Sends as realtimeInput JSON message with base64-encoded PCM chunks. If not yet connected, buffers in pendingAudio.
TEXT Sends as clientContent JSON message with user turn. If not yet connected, buffers as function in pendingFrames.
PARAM (voice, instructions, system, modalities) Updates params for next session setup
END Calls close() — destroys WebSocket, marks sessionEnded = true

Server events → outbound frames:

Server event Outbound frame
serverContent.modelTurn.parts[].text TEXT
serverContent.modelTurn.parts[].inlineData AUDIO (buffer from base64)
serverContent.outputTranscription.text TEXT with metadata.source === 'output_transcription'
toolCall.functionCalls[] TOOL_CALL for each function call
serverContent.turnComplete RECEIPT + END (with turnComplete: true)
error ERROR (code: ErrorCode.PROVIDER_ERROR) + END (with error: true)
WebSocket close (non-1000) ERROR + END
WebSocket error ERROR + END

isLiveModel(model)

Returns true if the model name contains 'live', 'native-audio', or 'realtime'.

getModelCost(model)

Returns the cost config object { mtoksInput, mtoksOutput } for a Gemini model, or null if unknown. Does case-insensitive key match and substring match on known models.

calculateTokenCost(model, inputTokens, outputTokens)

Returns { inputCost, outputCost, totalCost } in dollars. Uses per-million-token rates from GEMINI_COSTS. Returns zeros for unknown models.

GEMINI_COSTS

Static cost table mapping model names to { mtoksInput, mtoksOutput }. Includes: gemini-2.5-flash (0.15/0.60), gemini-2.5-flash-lite (0.075/0.30), gemini-2.5-pro (1.25/10.00), gemini-2.0-flash (0.10/0.40), gemini-2.0-flash-lite (0.075/0.30), gemini-2.0-pro (1.25/5.00), gemini-1.5-flash (0.075/0.30), gemini-1.5-pro (1.25/5.00), gemini-1.5-flash-8b (0.0375/0.15), text-embedding-004 (0.02/0), gemini-embedding (0.02/0), aqa (0.075/0.30).

Tests

npm test

Runs node --test test/rigor.test.js test/smoke.test.js test/live.test.js. Covers: adapter construction, PARAM accumulation, frame buffering before API key set, budget enforcement, COST_CHECK flow, calculateTokenCost monotonicity for all known models, getModelCost lookup accuracy, live session creation for live-model routing, and topology scan/response.

Requirements

Node.js 22 or later. ESM only.

Peer dependencies: @polyweave/core, @polyweave/costs, @polyweave/errors, @polyweave/push-websocket.

Dependencies

Development Dependencies

ID Version
@rigor/core *

Peer Dependencies

ID Version
@polyweave/core *
@polyweave/costs *
@polyweave/errors *
@polyweave/push-websocket *

Keywords

polyweave google gemini llm multimodal adapter
Details
npm
2026-07-11 15:25:47 +00:00
4
John Dvorak
SEE LICENSE IN LICENSE
latest
11 KiB
Assets (1)
Versions (3) View all
1.0.11 2026-07-11
1.0.8 2026-07-10
1.0.7 2026-07-10