llm_gemini 0.7.0
llm_gemini: ^0.7.0 copied to clipboard
Google Gemini API backend for LLM interactions. Provides streaming chat, embeddings, and tool calling via the Gemini API.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased] #
0.7.0 - 2026-09-22 #
Added #
LLMUsage.cachedTokensfromtotal_cached_tokens. It stays onproviderMetadataas well.
Fixed #
service_unavailable, the code Google actually sends when a model is over capacity, was not in the symbolic status map — only the gRPC spellingUNAVAILABLEwas — so a mid-stream capacity failure carried no status and could never be classified as retryable.RetryConfig.retryableStatusCodesnever applied to streaming requests. A non-2xx arrives as a returned response rather than a thrown error, so the retry around the send never saw it and a 429 or 503 from Google failed on the first attempt.- A failure reported in-band —
200, then anerrorevent on the stream — was not retried either, because it arrives after the send has returned. Both are now re-issued while nothing has reached the caller. - Cancelling a
streamChatsubscription aborts the request.
Changed #
- A
function_result'snameis read fromLLMMessage.toolName, falling back tostatus. - All packages bumped to
0.7.0;llm_coreconstraint updated to^0.7.0.
0.6.0 - 2026-09-17 #
Fixed #
- Argument fragments that did not decode were silently replaced with
{}, so the tool ran without arguments. Such calls are now returned ininvalidToolCalls.
0.5.0 - 2026-09-11 #
Fixed #
- An interaction truncated at
max_output_tokensreportedLLMFinishReason.toolCallswhen it had produced function-call steps, hiding the truncation behind a call whose arguments may stop mid-JSON. Truncated interactions now reportlength.
Changed #
- The tool-call classification this package already applied is now the shared
LLMFinishReason.resolverule fromllm_core, so every backend behaves the same way.
0.4.0 - 2026-08-30 #
Fixed #
- Mid-stream error codes are mapped to an HTTP status even when they are not numeric. A live free-tier quota failure reports
"quota_exceeded"as a string, which read as a number leftstatusCodenull — and retry classification works off the status, so a mid-stream quota or capacity error was never recognized as retryable. Numeric codes, numeric strings and Google's symbolic statuses are all handled.
Added #
- Streaming tool calls surface as they arrive on
LLMChunkMessage.toolCallDeltas. The Interactions API reports the tool instep.startbefore any argument fragment, and those fragments were already parsed but never surfaced. extraHeadersonGeminiChatRepositoryand its builder. Protocol headers andx-goog-api-keyalways take precedence.
0.3.2 - 2026-08-18 #
Changed #
- Version bumped to
0.3.2in lockstep with the other packages, and thellm_coreconstraint raised to^0.3.2. No other changes to this package.
0.3.1 - 2026-08-18 #
Added #
example/— a CLI example and README, matching the other backend packages.
Fixed #
streamChatapplies a timeout to the send operation. It previously opted out, so a request that wedged before response headers arrived never recovered.- The default HTTP client is now
createLLMHttpClient(), which appliesTimeoutConfig.connectionTimeoutand bounds the connection pool.
Changed #
- Documented default model is
gemini-3.5-flash-lite; the previously referencedgemini-3.1-flash-litedoes not exist in Google's model list. - Dependency floors raised: Dart SDK
^3.12.0(was^3.8.0),http ^1.6.0,lints ^6.1.0,test ^1.31.0.
0.3.0 - 2026-08-17 #
Added #
reasoningEffortmaps tothinking_level(wins over the budget mapping).- Thought-token usage surfaced as
LLMUsage.reasoningTokens.
Fixed #
- Migrated to the Interactions API's steps-based input format (
user_input/model_output/function_callsteps) — the old turn-based format is now rejected by the live API. - Tool loops echo the model's thought signature (required by the API), carried through the tool-call id.
- Structured output sends the JSON schema inline; the
json_schemawrapper type is rejected by the live API. think: falsesentthinking_summaries: 'off', which the live API rejects with a 400; it now sends'none'.- Empty user messages sent an empty text block (a 400); now a single-space block, the only shape the API accepts for a degenerate empty message.
0.2.0 - 2026-08-12 #
Initial release of the Google Gemini backend for the dart-llm ecosystem.
Added #
GeminiChatRepository— streaming chat via the Interactions API (POST /v1beta/interactions). Google labels the oldergenerateContentendpoint legacy, so this package targets Interactions from the outset. Requests carrymodel,input,stream,store,tools,response_format, andgeneration_config; responses are parsed frominteraction.created,step.start,step.delta,step.stop, andinteraction.completedevents.- The API key is sent in the
x-goog-api-keyheader, for chat and embeddings alike — never appended to the URL askey=…, where it would leak into logs, proxies, and crash reports. - Stateless by default: requests send
store: falseand serialize the whole conversation intoinputas role-tagged turns, matchingLLMChatRepository's stateless contract. Server-side continuation is opt-in viabackendOptions['previous_interaction_id']. - Tool/function calling with automatic tool-loop execution
(
autoExecuteTools: true). Declarations are sent as a flattoolsarray of{"type": "function", "name", "description", "parameters"}entries. Tool calls carry the server-provided call id, and argument JSON is assembled by concatenatingarguments_deltafragments. - Thinking, with
thought_summarydeltas populatingLLMChunkMessage.thinkingandtextdeltas populatingLLMChunkMessage.content— the two are kept separate rather than reasoning being emitted as ordinary assistant output.LLMChatOptions.reasoningBudgetmaps onto the discretegeneration_config.thinking_level(minimal/low/medium/high); the Interactions API has no raw token budget. Override withbackendOptions['thinking_level']. thought_signaturevalues are accumulated per step index and surfaced viaLLMChunk.providerMetadata['thought_signatures'].- Structured output via
LLMChatOptions.responseFormat, mapped onto theresponse_formatarray. Standard lowercase JSON Schema is forwarded unchanged. Propagated through tool-call loops so the constraint holds across every turn. - Embeddings via
embed()andbatchEmbed()(embedContent/batchEmbedContents). - Usage mapping for the Interactions field names (
total_tokens,total_input_tokens,total_output_tokens,total_cached_tokens,total_thought_tokens,total_tool_use_tokens); thought, cached and total counts are surfaced throughLLMChunk.providerMetadataalongsideinteraction_id. GeminiChatRepository.builder()for fluent configuration, with retry, timeout, rate limiting, response cache, and metrics support.
Notes #
topP,topKandstopSequencesare not sent: the Interactionsgeneration_configdocuments no equivalent. Pass them throughbackendOptions['generation_config']if your account supports them.- Defaults and docs use
gemini-3.5-flash-lite. Thegemini-3.1-flash-liteid referenced during development does not appear in Google's published model list.
Verification status #
This package has not been exercised against the live Gemini API — there was no API key available. Coverage is unit tests asserting the serialized request body and parsed stream events; the integration suites are key-gated and unrun.
Two shapes are inferred from prose rather than read from a specification and are the first things to check if requests fail with HTTP 400:
- The role-tagged
inputturn envelope ({"role": "user"|"model", "content": [ … ]}), together with thetext,function_callandimageinput blocks and the placement offunction_resultblocks on auserturn. All of this is confined toGeminiMessageConverter.buildStatelessInput, which documents each assumption individually. - The
response_formatelement shape ([{"type": "json_object"}]/[{"type": "json_schema", "name", "schema"}]); only the fact that it is an array is documented.
Known limitation: thought_signature values are surfaced on the response, but
Google documents no input block for echoing them back, so multi-turn function
calling may still hit "Function call is missing a thought_signature".