LemonAi. Providers. GoogleShared
(lemon_ai v0.1.0)
View Source
Shared utilities for Google Generative AI, Vertex AI, and Gemini CLI providers.
This module handles:
- Content conversion to Gemini format
- Tool declaration formatting
- Stop reason mapping
- Thought signature validation
- Unicode sanitization
Summary
Functions
Calculate cost for usage based on model pricing.
Clamp thinking level (remove :xhigh).
Convert internal messages to Gemini Content[] format.
Convert tools to Gemini function declarations format.
Get default thinking budgets for Gemini 2.5 Flash models.
Get default thinking budgets for Gemini 2.5 Pro models.
Extract a clean, user-friendly error message from Google API error response.
Extract retry delay from error response (in milliseconds). Parses patterns like
Check if model is Gemini 3 Flash.
Check if model is Gemini 3 Pro.
Get thinking level for Gemini 3 models based on effort.
Get thinking budget for a model and effort level.
Map Gemini FinishReason string to our stop_reason.
Map tool choice to Gemini FunctionCallingConfigMode string.
Normalize error response bodies from Req into a plain string.
Check if a model requires explicit tool call IDs in function calls/responses. Claude and GPT-OSS models via Google APIs require explicit IDs.
Only keep signatures from the same provider/model and with valid base64.
Retain thought signatures during streaming.
Check if an error is retryable (rate limit, server error, network error, etc.)
Sanitize unpaired surrogates from a string. These can cause issues with some APIs.
Determines whether a streamed Gemini Part should be treated as "thinking".
Check if a thought signature is valid base64.
Types
@type google_api_type() :: :google_generative_ai | :google_vertex | :google_gemini_cli
@type part() :: %{ optional(:text) => String.t(), optional(:thought) => boolean(), optional(:thought_signature) => String.t(), optional(:inline_data) => %{mime_type: String.t(), data: String.t()}, optional(:function_call) => %{ :name => String.t(), :args => map(), optional(:id) => String.t() }, optional(:function_response) => %{ :name => String.t(), :response => map(), optional(:id) => String.t(), optional(:parts) => [part()] } }
@type stop_reason() :: :stop | :length | :tool_use | :error | :aborted
@type thinking_level() :: :minimal | :low | :medium | :high
@type tool_choice() :: :auto | :none | :any
Functions
@spec calculate_cost(LemonAi.Types.Model.t(), map()) :: map()
Calculate cost for usage based on model pricing.
@spec clamp_reasoning(atom() | nil) :: thinking_level() | nil
Clamp thinking level (remove :xhigh).
@spec convert_messages(LemonAi.Types.Model.t(), LemonAi.Types.Context.t()) :: [ content() ]
Convert internal messages to Gemini Content[] format.
@spec convert_tools([LemonAi.Types.Tool.t()]) :: [%{functionDeclarations: [function_declaration()]}] | nil
Convert tools to Gemini function declarations format.
@spec default_budgets_2_5_flash() :: %{ required(thinking_level()) => non_neg_integer() }
Get default thinking budgets for Gemini 2.5 Flash models.
@spec default_budgets_2_5_pro() :: %{required(thinking_level()) => non_neg_integer()}
Get default thinking budgets for Gemini 2.5 Pro models.
Extract a clean, user-friendly error message from Google API error response.
@spec extract_retry_delay(String.t(), map()) :: non_neg_integer() | nil
Extract retry delay from error response (in milliseconds). Parses patterns like:
- "Your quota will reset after 39s"
- "Your quota will reset after 18h31m10s"
- "Please retry in Xs" or "Please retry in Xms"
- "retryDelay": "34.074824224s" (JSON field)
Check if model is Gemini 3 Flash.
Check if model is Gemini 3 Pro.
@spec get_gemini_3_thinking_level(thinking_level(), String.t()) :: String.t()
Get thinking level for Gemini 3 models based on effort.
@spec get_thinking_budget(LemonAi.Types.Model.t(), thinking_level(), map()) :: integer()
Get thinking budget for a model and effort level.
@spec map_stop_reason(String.t()) :: stop_reason()
Map Gemini FinishReason string to our stop_reason.
@spec map_tool_choice(tool_choice()) :: String.t()
Map tool choice to Gemini FunctionCallingConfigMode string.
Normalize error response bodies from Req into a plain string.
Handles plain binaries, maps, and Req.Response.Async bodies produced by
streaming requests (into: :self) so provider logs include the real upstream
error JSON instead of an async struct dump.
@spec normalize_http_error_body(term(), non_neg_integer()) :: String.t()
Check if a model requires explicit tool call IDs in function calls/responses. Claude and GPT-OSS models via Google APIs require explicit IDs.
Only keep signatures from the same provider/model and with valid base64.
Retain thought signatures during streaming.
Some backends only send thoughtSignature on the first delta for a given part/block;
later deltas may omit it. This helper preserves the last non-empty signature.
@spec retryable_error?(non_neg_integer(), String.t()) :: boolean()
Check if an error is retryable (rate limit, server error, network error, etc.)
Sanitize unpaired surrogates from a string. These can cause issues with some APIs.
Determines whether a streamed Gemini Part should be treated as "thinking".
Protocol note (Gemini / Vertex AI thought signatures):
thought: trueis the definitive marker for thinking content (thought summaries).thoughtSignatureis an encrypted representation of the model's internal thought process used to preserve reasoning context across multi-turn interactions.thoughtSignaturecan appear on ANY part type (text, functionCall, etc.) - it does NOT indicate the part itself is thinking content.
Check if a thought signature is valid base64.