Gemini
fromGemini adapter
fromGemini wraps the raw Gemini client, not a pre-configured model instance, since the adapter passes model through on every call rather than baking it in up front.
GeminiClient is the public client type exported by VernLLM and accepted by fromGemini. It allows TypeScript users to type their Gemini client without depending on adapter internals. It covers both the callable model methods directly and the whole top-level SDK client (via an optional models field), so both of the forms below type-check with no cast.
The Gemini SDK can retry on its own too
@google/genai only retries transient failures, including 429s, when httpOptions.retryOptions
is explicitly configured on the client. With it unset, the SDK makes a single attempt and every
retry is VernLLM's own. See SDK-internal retries.
New SDK (@google/genai)
The current SDK's ai.models.generateContent({ model, ... }) takes model per call, which lines up directly with how fromGemini forwards it. Pass the whole client straight through, fromGemini unwraps .models internally:
import { GoogleGenAI } from '@google/genai';
import { VernLLM, fromGemini } from 'vern-llm';
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const llm = new VernLLM({
client: fromGemini(ai),
model: 'gemini-3.1-flash-lite',
});fromGemini(ai.models) still works exactly as before, if you'd rather pass the narrower client:
const llm = new VernLLM({
client: fromGemini(ai.models),
model: 'gemini-3.1-flash-lite',
});jsonSchema uses Gemini's native responseSchema + responseMimeType: 'application/json'.
Gemini supports only its own JSON Schema subset rather than the full JSON Schema specification.
Use the client-side schema option when validation guarantees are required. On Gemini 3 series
models and later, reasoningEffort/budgetTokens map onto Gemini's native
thinkingConfig.thinkingLevel; on Gemini 2.5 and earlier, they map onto
thinkingConfig.thinkingBudget instead. fromGemini detects which path a model needs. See the
Call Params reference for the full behavior,
fromGemini's second argument for reasoningEffortTokens/thinkingLevelModels to override
either. thinkingConfig.thinkingLevel on GeminiClient is typed any, not a string literal
union, since @google/genai's real ThinkingLevel is a TypeScript enum a matching string type
can't satisfy; VernLLM builds the value internally through typed helpers regardless.
Tool calling
tools and tool_calls map onto Gemini's native functionCall / functionResponse parts, and
toolChoice maps onto toolConfig.functionCallingConfig. Gemini 3 and later provide a native id
for each functionCall; on earlier models, fromGemini synthesizes a unique id for repeated calls
to the same tool. Function responses use the original function name associated with each tool call.
Consecutive function response messages from a multi-tool turn are merged back into a single user
content entry. See Tool Calling for the full reference.
Streaming
fromGemini implements createStream on top of the SDK's own streaming method. Text and function
call parts arriving on each streamed candidate map onto text-delta and tool_call_delta chunks
respectively, no extra setup is needed beyond the client already passed to fromGemini:
const { chunks, finalResult } = await llm.call({
userContent: 'Write a short poem about the ocean.',
stream: true,
});
for await (const chunk of chunks) {
if (chunk.type === 'text-delta') process.stdout.write(chunk.delta);
}Gemini's API can't stream function-call arguments incrementally, each functionCall part arrives
whole in a single chunk. fromGemini marks these tool_call_delta chunks with complete: true,
so argsDelta is the entire set of arguments, not a fragment. See The chunk
shape.
See Streaming for the full stream: true contract.
Multimodal input
fromGemini supports VernLLM's provider-independent ContentBlock[] format for image input.
Image blocks are translated into Gemini's native inlineData parts automatically. The selected
Gemini model must support image input for multimodal requests to succeed.
const result = await llm.call({
userContent: [
{
type: 'text',
text: 'Describe this image.',
},
{
type: 'image',
mimeType: 'image/png',
data: imageBase64,
},
],
});Image data must be base64-encoded bytes without a data: URL prefix. Supported image types are
image/png, image/jpeg, image/gif, and image/webp.
Plain string userContent remains unchanged for text-only requests.