Current section

Files

Jump to
gemini_ex lib gemini validation thinking_config.ex
Raw

lib/gemini/validation/thinking_config.ex

defmodule Gemini.Validation.ThinkingConfig do
@moduledoc """
Validation for thinking configuration parameters based on model capabilities.
## Gemini 3 Models
Use `thinking_level` for Gemini 3 models:
- `:minimal` - Minimal thinking (Flash only). Model may still think for complex tasks.
- `:low` - Minimizes latency and cost
- `:medium` - Balanced thinking (Flash only)
- `:high` - Maximizes reasoning depth (default)
## Model Support
- **Gemini 3 Pro**: `:low`, `:high`
- **Gemini 3 Flash**: `:minimal`, `:low`, `:medium`, `:high`
## Gemini 2.5 Models
Gemini 2.5 series models support thinking budgets with model-specific ranges:
- **2.5 Pro**: 128-32,768 tokens (cannot disable with 0)
- **2.5 Flash**: 0-24,576 tokens (can disable)
- **2.5 Flash Lite**: 0 or 512-24,576 tokens
Special value `-1` enables dynamic thinking (model decides budget) for all models.
## Important
You cannot use both `thinking_level` and `thinking_budget` in the same request.
Doing so will return a 400 error from the API.
See: https://ai.google.dev/gemini-api/docs/gemini-3
"""
@type validation_result :: :ok | {:error, String.t()}
@type thinking_level :: :unspecified | :minimal | :low | :medium | :high
@doc """
Validate thinking level for Gemini 3 models.
## Parameters
- `level`: Thinking level atom (`:minimal`, `:low`, `:medium`, `:high`)
- `model`: Optional model name for Flash-only validation
## Returns
- `:ok` if valid
- `{:error, message}` if invalid
## Examples
iex> Gemini.Validation.ThinkingConfig.validate_level(:low, "gemini-3-pro-preview")
:ok
iex> Gemini.Validation.ThinkingConfig.validate_level(:medium, "gemini-3-pro-preview")
{:error, "Thinking level :medium is only supported on Gemini 3 Flash models"}
"""
@spec validate_level(thinking_level(), String.t() | nil) :: validation_result()
def validate_level(level, model \\ nil)
def validate_level(:unspecified, _model), do: :ok
def validate_level(:low, _model), do: :ok
def validate_level(:high, _model), do: :ok
def validate_level(:minimal, model) do
if model && String.contains?(model, "gemini-3-pro") && !String.contains?(model, "flash") do
{:error, "Thinking level :minimal is only supported on Gemini 3 Flash models"}
else
:ok
end
end
def validate_level(:medium, model) do
if model && String.contains?(model, "gemini-3-pro") && !String.contains?(model, "flash") do
{:error, "Thinking level :medium is only supported on Gemini 3 Flash models"}
else
:ok
end
end
def validate_level(level, _model) do
{:error, "Invalid thinking level: #{inspect(level)}. Use :minimal, :low, :medium, or :high."}
end
@doc """
Validate thinking budget for a specific model.
## Parameters
- `budget`: Integer budget value
- `model`: Model name string
## Returns
- `:ok` if valid
- `{:error, message}` with helpful error message
## Examples
iex> Gemini.Validation.ThinkingConfig.validate_budget(1024, "gemini-2.5-flash")
:ok
iex> Gemini.Validation.ThinkingConfig.validate_budget(0, "gemini-2.5-pro")
{:error, "Gemini 2.5 Pro cannot disable thinking (minimum budget: 128)"}
"""
@spec validate_budget(integer(), String.t()) :: validation_result()
def validate_budget(budget, model) when is_integer(budget) and is_binary(model) do
cond do
budget == -1 ->
# Dynamic thinking allowed for all models
:ok
String.contains?(model, "gemini-2.5-pro") or String.contains?(model, "gemini-pro-2.5") ->
validate_pro_budget(budget)
String.contains?(model, "gemini-2.5-flash-lite") ->
validate_flash_lite_budget(budget)
String.contains?(model, "gemini-2.5-flash") or String.contains?(model, "gemini-flash-2.5") ->
validate_flash_budget(budget)
true ->
# Unknown model, allow any value (let API validate)
:ok
end
end
@doc """
Validate complete thinking config including budget, level, and include_thoughts.
## Parameters
- `config`: Map or ThinkingConfig struct
- `model`: Model name string
## Returns
- `:ok` if valid
- `{:error, message}` if invalid
## Examples
iex> Gemini.Validation.ThinkingConfig.validate(%{thinking_level: :low}, "gemini-3-pro-preview")
:ok
iex> Gemini.Validation.ThinkingConfig.validate(%{thinking_budget: 1024, thinking_level: :low}, "gemini-3-pro-preview")
{:error, "Cannot use both thinking_level and thinking_budget in the same request"}
"""
@spec validate(map() | struct(), String.t()) :: validation_result()
def validate(%{thinking_budget: budget, thinking_level: level}, _model)
when not is_nil(budget) and not is_nil(level) do
{:error, "Cannot use both thinking_level and thinking_budget in the same request"}
end
def validate(%{thinking_level: level}, model) when not is_nil(level) do
validate_level(level, model)
end
def validate(%{thinking_budget: budget}, model) when is_integer(budget) do
validate_budget(budget, model)
end
def validate(_config, _model), do: :ok
# Private validation functions for each model type
defp validate_pro_budget(0) do
{:error, "Gemini 2.5 Pro cannot disable thinking (minimum budget: 128)"}
end
defp validate_pro_budget(budget) when budget >= 128 and budget <= 32_768 do
:ok
end
defp validate_pro_budget(budget) do
{:error, "Gemini 2.5 Pro thinking budget must be between 128 and 32,768, got: #{budget}"}
end
defp validate_flash_budget(budget) when budget >= 0 and budget <= 24_576 do
:ok
end
defp validate_flash_budget(budget) do
{:error, "Gemini 2.5 Flash thinking budget must be between 0 and 24,576, got: #{budget}"}
end
defp validate_flash_lite_budget(0), do: :ok
defp validate_flash_lite_budget(budget)
when budget >= 512 and budget <= 24_576 do
:ok
end
defp validate_flash_lite_budget(budget) do
{:error,
"Gemini 2.5 Flash Lite thinking budget must be 0 or between 512 and 24,576, got: #{budget}"}
end
end