Compare commits

...
7 Commits
25 changed files with 457 additions and 6 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ agents while handling coordination and final reporting.
## Pro-Tip: Use an IDE MCP Server for Improved Performance ## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
server to your config (see the [MCP Server docs](../../../docs/function-calling/MCP-SERVERS.md) to see how to configure server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) to see how to configure
them), and modify the agent definition to look like this: them), and modify the agent definition to look like this:
```yaml ```yaml
+1 -1
View File
@@ -16,7 +16,7 @@ one file while communicating with sibling agents to catch issues that span multi
## Pro-Tip: Use an IDE MCP Server for Improved Performance ## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
server to your config (see the [MCP Server docs](../../../docs/function-calling/MCP-SERVERS.md) to see how to configure server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) to see how to configure
them), and modify the agent definition to look like this: them), and modify the agent definition to look like this:
```yaml ```yaml
+2
View File
@@ -13,6 +13,8 @@
model: openai:gpt-4o # Specify the LLM to use model: openai:gpt-4o # Specify the LLM to use
temperature: null # Set default temperature parameter, range (0, 1) temperature: null # Set default temperature parameter, range (0, 1)
top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2) depending on the model top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2) depending on the model
reasoning_effort: null # Reasoning effort level for models that support it (e.g. low, medium, high).
# Only valid when the agent's model declares reasoning_levels.
agent_session: null # Set a session to use when starting the agent. (e.g. temp, default); defaults to globally set agent_session agent_session: null # Set a session to use when starting the agent. (e.g. temp, default); defaults to globally set agent_session
name: <agent-name> # Name of the agent, used in the UI and logs name: <agent-name> # Name of the agent, used in the UI and logs
description: <description> # Description of the agent, used in the UI description: <description> # Description of the agent, used in the UI
+2
View File
@@ -2,6 +2,8 @@
model: openai:gpt-4o # Specify the LLM to use model: openai:gpt-4o # Specify the LLM to use
temperature: null # Set default temperature parameter (0, 1) temperature: null # Set default temperature parameter (0, 1)
top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2) depending on the model top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2) depending on the model
reasoning_effort: null # Reasoning effort level for models that support it (e.g. low, medium, high).
# Only valid when the active model declares reasoning_levels. See the Clients docs.
# ---- Behavior ---- # ---- Behavior ----
stream: true # Controls whether to use the stream-style APIs when querying for completions from LLM clients. stream: true # Controls whether to use the stream-style APIs when querying for completions from LLM clients.
+2
View File
@@ -8,6 +8,8 @@ name: <role-name> # The name of the role
model: openai:gpt-4o # The model to use for this role model: openai:gpt-4o # The model to use for this role
temperature: 0.2 # The temperature to use for this role when querying the model temperature: 0.2 # The temperature to use for this role when querying the model
top_p: 0 # The top_p to use for this role when querying the model top_p: 0 # The top_p to use for this role when querying the model
reasoning_effort: null # Reasoning effort level for models that support it (e.g. low, medium, high).
# Only valid when the role's model declares reasoning_levels.
enabled_tools: # Tools to enable for this role. Accepts a YAML list (preferred) enabled_tools: # Tools to enable for this role. Accepts a YAML list (preferred)
- fs_ls # or a comma-separated string (e.g. `enabled_tools: fs_ls,fs_cat`). - fs_ls # or a comma-separated string (e.g. `enabled_tools: fs_ls,fs_cat`).
- fs_cat # Use `all` to enable every visible tool. - fs_cat # Use `all` to enable every visible tool.
+3
View File
@@ -33,6 +33,8 @@ version: "1.0" # Graph schema version. Only "1.0" is accepte
model: claude:claude-sonnet-4-6 # Default model for `llm` nodes that don't override it model: claude:claude-sonnet-4-6 # Default model for `llm` nodes that don't override it
temperature: 0.0 # Default sampling temperature for `llm` nodes temperature: 0.0 # Default sampling temperature for `llm` nodes
top_p: null # Default sampling top-p for `llm` nodes top_p: null # Default sampling top-p for `llm` nodes
reasoning_effort: null # Default reasoning effort for `llm` nodes that don't override it.
# Only valid when the model declares reasoning_levels.
global_tools: # Tool universe an `llm` node's `tools:` whitelist draws from global_tools: # Tool universe an `llm` node's `tools:` whitelist draws from
- web_search_coyote.sh - web_search_coyote.sh
@@ -394,6 +396,7 @@ nodes:
- mcp:ddg-search # `mcp:<server>` includes that server's functions - mcp:ddg-search # `mcp:<server>` includes that server's functions
model: claude:claude-haiku-4-5 # Optional per-node model override model: claude:claude-haiku-4-5 # Optional per-node model override
temperature: 0.3 # Optional per-node sampling override temperature: 0.3 # Optional per-node sampling override
reasoning_effort: null # Optional per-node reasoning effort override (e.g. low, medium, high)
max_attempts: 2 # Retry count on transient errors only. Default 1. max_attempts: 2 # Retry count on transient errors only. Default 1.
max_iterations: 10 # Tool-call-loop turn cap. Default 10. max_iterations: 10 # Tool-call-loop turn cap. Default 10.
fallback: review # Route here if all attempts fail fallback: review # Route here if all attempts fail
+201
View File
@@ -10,6 +10,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh, max]
default_reasoning_effort: medium
- name: gpt-5.6-terra - name: gpt-5.6-terra
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -17,6 +19,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh, max]
default_reasoning_effort: medium
- name: gpt-5.6-luna - name: gpt-5.6-luna
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -24,6 +28,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh, max]
default_reasoning_effort: medium
- name: gpt-5.5 - name: gpt-5.5
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -31,6 +37,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: medium
- name: gpt-5.5-pro - name: gpt-5.5-pro
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -38,6 +46,8 @@
output_price: 180 output_price: 180
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [medium, high, xhigh]
default_reasoning_effort: high
- name: gpt-5.4 - name: gpt-5.4
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -45,6 +55,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: gpt-5.4-pro - name: gpt-5.4-pro
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -52,6 +64,8 @@
output_price: 180 output_price: 180
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [medium, high, xhigh]
default_reasoning_effort: medium
- name: gpt-5.4-mini - name: gpt-5.4-mini
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -59,6 +73,8 @@
output_price: 4.5 output_price: 4.5
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: gpt-5.4-nano - name: gpt-5.4-nano
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -66,6 +82,8 @@
output_price: 1.25 output_price: 1.25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: gpt-5.3-codex - name: gpt-5.3-codex
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -73,6 +91,8 @@
output_price: 14 output_price: 14
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh]
default_reasoning_effort: medium
- name: chat-latest - name: chat-latest
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -87,6 +107,17 @@
output_price: 14 output_price: 14
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: gpt-5.2-pro
max_input_tokens: 400000
max_output_tokens: 128000
input_price: 21
output_price: 168
supports_vision: true
supports_function_calling: true
reasoning_levels: [medium, high, xhigh]
default_reasoning_effort: medium
- name: gpt-5.1 - name: gpt-5.1
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -94,6 +125,8 @@
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high]
default_reasoning_effort: none
- name: gpt-5.1-chat-latest - name: gpt-5.1-chat-latest
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -101,6 +134,8 @@
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high]
default_reasoning_effort: none
- name: gpt-5 - name: gpt-5
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -108,6 +143,8 @@
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: medium
- name: gpt-5-chat-latest - name: gpt-5-chat-latest
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -115,6 +152,8 @@
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: medium
- name: gpt-5-mini - name: gpt-5-mini
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -172,6 +211,8 @@
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
system_prompt_prefix: Formatting re-enabled system_prompt_prefix: Formatting re-enabled
reasoning_levels: [low, medium, high]
default_reasoning_effort: medium
patch: patch:
body: body:
max_tokens: null max_tokens: null
@@ -285,18 +326,24 @@
input_price: 0.2 input_price: 0.2
output_price: 1.5 output_price: 1.5
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: medium
- name: gemini-3-flash-preview - name: gemini-3-flash-preview
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
input_price: 0.2 input_price: 0.2
output_price: 1.5 output_price: 1.5
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: high
- name: gemini-3.1-flash-lite - name: gemini-3.1-flash-lite
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
input_price: 0.2 input_price: 0.2
output_price: 1.5 output_price: 1.5
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: minimal
- name: gemini-3.1-pro-preview - name: gemini-3.1-pro-preview
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65535 max_output_tokens: 65535
@@ -304,6 +351,8 @@
output_price: 2.5 output_price: 2.5
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: high
- name: gemini-2.5-flash - name: gemini-2.5-flash
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
@@ -311,6 +360,8 @@
output_price: 0 output_price: 0
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: low
- name: gemini-2.5-pro - name: gemini-2.5-pro
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
@@ -318,6 +369,8 @@
output_price: 0 output_price: 0
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: high
- name: gemini-2.5-flash-lite - name: gemini-2.5-flash-lite
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 64000 max_output_tokens: 64000
@@ -329,10 +382,14 @@
max_input_tokens: 1048576 max_input_tokens: 1048576
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, high]
default_reasoning_level: high
- name: gemini-3-flash-preview - name: gemini-3-flash-preview
max_input_tokens: 1048576 max_input_tokens: 1048576
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_level: high
- name: gemma-3-27b-it - name: gemma-3-27b-it
max_input_tokens: 131072 max_input_tokens: 131072
max_output_tokens: 8192 max_output_tokens: 8192
@@ -358,6 +415,8 @@
output_price: 50 output_price: 50
supports_function_calling: true supports_function_calling: true
supports_vision: true supports_vision: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-opus-4-8 - name: claude-opus-4-8
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -366,6 +425,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-opus-4-7 - name: claude-opus-4-7
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -374,6 +435,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-opus-4-6 - name: claude-opus-4-6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -382,6 +445,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: claude-opus-4-6:thinking - name: claude-opus-4-6:thinking
real_name: claude-opus-4-6 real_name: claude-opus-4-6
max_input_tokens: 200000 max_input_tokens: 200000
@@ -406,6 +471,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-sonnet-4-6 - name: claude-sonnet-4-6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -414,6 +481,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: claude-sonnet-4-6:thinking - name: claude-sonnet-4-6:thinking
real_name: claude-sonnet-4-6 real_name: claude-sonnet-4-6
max_input_tokens: 200000 max_input_tokens: 200000
@@ -856,18 +925,24 @@
input_price: 0.2 input_price: 0.2
output_price: 1.5 output_price: 1.5
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: medium
- name: gemini-3-flash-preview - name: gemini-3-flash-preview
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
input_price: 0.2 input_price: 0.2
output_price: 1.5 output_price: 1.5
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: high
- name: gemini-3.1-flash-lite - name: gemini-3.1-flash-lite
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
input_price: 0.2 input_price: 0.2
output_price: 1.5 output_price: 1.5
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, high]
default_reasoning_effort: minimal
- name: gemini-3.1-pro-preview - name: gemini-3.1-pro-preview
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
@@ -875,6 +950,8 @@
output_price: 12 output_price: 12
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: high
- name: gemini-2.5-flash - name: gemini-2.5-flash
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65535 max_output_tokens: 65535
@@ -882,6 +959,8 @@
output_price: 2.5 output_price: 2.5
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: medium
- name: gemini-2.5-pro - name: gemini-2.5-pro
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
@@ -889,6 +968,8 @@
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: high
- name: gemini-2.5-flash-lite - name: gemini-2.5-flash-lite
max_input_tokens: 1048576 max_input_tokens: 1048576
max_output_tokens: 65536 max_output_tokens: 65536
@@ -900,10 +981,14 @@
max_input_tokens: 1048576 max_input_tokens: 1048576
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, high]
default_reasoning_effort: high
- name: gemini-3-flash-preview - name: gemini-3-flash-preview
max_input_tokens: 1048576 max_input_tokens: 1048576
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: high
- name: claude-fable-5 - name: claude-fable-5
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -912,6 +997,8 @@
output_price: 50 output_price: 50
supports_function_calling: true supports_function_calling: true
supports_vision: true supports_vision: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-opus-4-8 - name: claude-opus-4-8
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -920,6 +1007,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-opus-4-7 - name: claude-opus-4-7
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -928,6 +1017,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-opus-4-6 - name: claude-opus-4-6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -959,6 +1050,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: claude-sonnet-4-6 - name: claude-sonnet-4-6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -967,6 +1060,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: claude-sonnet-4-6:thinking - name: claude-sonnet-4-6:thinking
real_name: claude-sonnet-4-6 real_name: claude-sonnet-4-6
max_input_tokens: 200000 max_input_tokens: 200000
@@ -1099,6 +1194,8 @@
output_price: 50 output_price: 50
supports_function_calling: true supports_function_calling: true
supports_vision: true supports_vision: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: us.anthropic.claude-opus-4-8 - name: us.anthropic.claude-opus-4-8
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1107,6 +1204,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: us.anthropic.claude-opus-4-7 - name: us.anthropic.claude-opus-4-7
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1115,6 +1214,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: us.anthropic.claude-opus-4-6-v1 - name: us.anthropic.claude-opus-4-6-v1
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -1123,6 +1224,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: us.anthropic.claude-opus-4-6-v1:thinking - name: us.anthropic.claude-opus-4-6-v1:thinking
real_name: us.anthropic.claude-opus-4-6-v1 real_name: us.anthropic.claude-opus-4-6-v1
max_input_tokens: 200000 max_input_tokens: 200000
@@ -1148,6 +1251,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: us.anthropic.claude-sonnet-4-6 - name: us.anthropic.claude-sonnet-4-6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -1156,6 +1261,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: us.anthropic.claude-sonnet-4-6:thinking - name: us.anthropic.claude-sonnet-4-6:thinking
real_name: us.anthropic.claude-sonnet-4-6 real_name: us.anthropic.claude-sonnet-4-6
max_input_tokens: 200000 max_input_tokens: 200000
@@ -1672,6 +1779,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh, max]
default_reasoning_effort: medium
- name: openai/gpt-5.6-terra - name: openai/gpt-5.6-terra
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1679,6 +1788,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh, max]
default_reasoning_effort: medium
- name: openai/gpt-5.6-luna - name: openai/gpt-5.6-luna
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1686,6 +1797,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh, max]
default_reasoning_effort: medium
- name: openai/gpt-5.5 - name: openai/gpt-5.5
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1693,6 +1806,8 @@
output_price: 30 output_price: 30
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: medium
- name: openai/gpt-5.5-pro - name: openai/gpt-5.5-pro
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1700,6 +1815,8 @@
output_price: 180 output_price: 180
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [medium, high, xhigh]
default_reasoning_effort: high
- name: openai/gpt-5.4 - name: openai/gpt-5.4
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1707,6 +1824,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: openai/gpt-5.4-pro - name: openai/gpt-5.4-pro
max_input_tokens: 1050000 max_input_tokens: 1050000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1714,6 +1833,8 @@
output_price: 180 output_price: 180
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [medium, high, xhigh]
default_reasoning_effort: medium
- name: openai/gpt-5.4-mini - name: openai/gpt-5.4-mini
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1721,6 +1842,8 @@
output_price: 4.5 output_price: 4.5
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: openai/gpt-5.4-nano - name: openai/gpt-5.4-nano
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1728,6 +1851,8 @@
output_price: 1.25 output_price: 1.25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: openai/gpt-5.3-codex - name: openai/gpt-5.3-codex
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1735,6 +1860,8 @@
output_price: 14 output_price: 14
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh]
default_reasoning_effort: medium
- name: openai/gpt-5.2 - name: openai/gpt-5.2
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1742,6 +1869,17 @@
output_price: 14 output_price: 14
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [none, low, medium, high, xhigh]
default_reasoning_effort: none
- name: openai/gpt-5.2-pro
max_input_tokens: 400000
max_output_tokens: 128000
input_price: 21
output_price: 168
supports_vision: true
supports_function_calling: true
reasoning_levels: [medium, high, xhigh]
default_reasoning_effort: medium
- name: openai/gpt-5 - name: openai/gpt-5
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1749,6 +1887,8 @@
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: medium
- name: openai/gpt-5-mini - name: openai/gpt-5-mini
max_input_tokens: 400000 max_input_tokens: 400000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1786,18 +1926,67 @@
input_price: 0.04 input_price: 0.04
output_price: 0.16 output_price: 0.16
supports_function_calling: true supports_function_calling: true
- name: google/gemini-3.5-flash
max_input_tokens: 1048576
max_output_tokens: 65536
input_price: 0.2
output_price: 1.5
supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: medium
- name: google/gemini-3-flash-preview
max_input_tokens: 1048576
max_output_tokens: 65536
input_price: 0.2
output_price: 1.5
supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: high
- name: google/gemini-3.1-flash-lite
max_input_tokens: 1048576
max_output_tokens: 65536
input_price: 0.2
output_price: 1.5
supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_effort: minimal
- name: google/gemini-3.1-pro-preview
max_input_tokens: 1048576
max_output_tokens: 65535
input_price: 0.3
output_price: 2.5
supports_vision: true
supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: high
- name: google/gemini-3-pro-preview
max_input_tokens: 1048576
supports_vision: true
supports_function_calling: true
reasoning_levels: [low, high]
default_reasoning_level: high
- name: google/gemini-3-flash-preview
max_input_tokens: 1048576
supports_vision: true
supports_function_calling: true
reasoning_levels: [minimal, low, medium, high]
default_reasoning_level: high
- name: google/gemini-2.5-flash - name: google/gemini-2.5-flash
max_input_tokens: 1048576 max_input_tokens: 1048576
input_price: 0.3 input_price: 0.3
output_price: 2.5 output_price: 2.5
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: low
- name: google/gemini-2.5-pro - name: google/gemini-2.5-pro
max_input_tokens: 1048576 max_input_tokens: 1048576
input_price: 1.25 input_price: 1.25
output_price: 10 output_price: 10
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high]
default_reasoning_effort: high
- name: google/gemini-2.5-flash-lite - name: google/gemini-2.5-flash-lite
max_input_tokens: 1048576 max_input_tokens: 1048576
input_price: 0.3 input_price: 0.3
@@ -1827,6 +2016,8 @@
output_price: 50 output_price: 50
supports_function_calling: true supports_function_calling: true
supports_vision: true supports_vision: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: anthropic/claude-opus-4-8 - name: anthropic/claude-opus-4-8
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1835,6 +2026,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: anthropic/claude-opus-4-7 - name: anthropic/claude-opus-4-7
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1843,6 +2036,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: anthropic/claude-opus-4.6 - name: anthropic/claude-opus-4.6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -1851,6 +2046,8 @@
output_price: 25 output_price: 25
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: anthropic/claude-sonnet-5 - name: anthropic/claude-sonnet-5
max_input_tokens: 1000000 max_input_tokens: 1000000
max_output_tokens: 128000 max_output_tokens: 128000
@@ -1859,6 +2056,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, xhigh, max]
default_reasoning_effort: high
- name: anthropic/claude-sonnet-4.6 - name: anthropic/claude-sonnet-4.6
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
@@ -1867,6 +2066,8 @@
output_price: 15 output_price: 15
supports_vision: true supports_vision: true
supports_function_calling: true supports_function_calling: true
reasoning_levels: [low, medium, high, max]
default_reasoning_effort: high
- name: anthropic/claude-opus-4.5 - name: anthropic/claude-opus-4.5
max_input_tokens: 200000 max_input_tokens: 200000
max_output_tokens: 8192 max_output_tokens: 8192
+4
View File
@@ -325,6 +325,7 @@ fn build_chat_completions_body(data: ChatCompletionsData, model: &Model) -> Resu
mut messages, mut messages,
temperature, temperature,
top_p, top_p,
reasoning_effort,
functions, functions,
stream: _, stream: _,
} = data; } = data;
@@ -457,6 +458,9 @@ fn build_chat_completions_body(data: ChatCompletionsData, model: &Model) -> Resu
if let Some(v) = top_p { if let Some(v) = top_p {
body["inferenceConfig"]["topP"] = v.into(); body["inferenceConfig"]["topP"] = v.into();
} }
if let Some(v) = reasoning_effort {
body["additionalModelRequestFields"] = json!({ "output_config": { "effort": v } });
}
if let Some(functions) = functions { if let Some(functions) = functions {
let tools: Vec<_> = functions let tools: Vec<_> = functions
.iter() .iter()
+4
View File
@@ -251,6 +251,7 @@ pub fn claude_build_chat_completions_body(
mut messages, mut messages,
temperature, temperature,
top_p, top_p,
reasoning_effort,
functions, functions,
stream, stream,
} = data; } = data;
@@ -369,6 +370,9 @@ pub fn claude_build_chat_completions_body(
if let Some(v) = top_p { if let Some(v) = top_p {
body["top_p"] = v.into(); body["top_p"] = v.into();
} }
if let Some(v) = reasoning_effort {
body["output_config"] = json!({ "effort": v });
}
if stream { if stream {
body["stream"] = true.into(); body["stream"] = true.into();
} }
+1
View File
@@ -286,6 +286,7 @@ pub struct ChatCompletionsData {
pub messages: Vec<Message>, pub messages: Vec<Message>,
pub temperature: Option<f64>, pub temperature: Option<f64>,
pub top_p: Option<f64>, pub top_p: Option<f64>,
pub reasoning_effort: Option<String>,
pub functions: Option<Vec<FunctionDeclaration>>, pub functions: Option<Vec<FunctionDeclaration>>,
pub stream: bool, pub stream: bool,
} }
+12
View File
@@ -289,6 +289,14 @@ impl Model {
} }
Ok(()) Ok(())
} }
pub fn reasoning_levels(&self) -> &[String] {
&self.data.reasoning_levels
}
pub fn default_reasoning_effort(&self) -> Option<&str> {
self.data.default_reasoning_effort.as_deref()
}
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
@@ -316,6 +324,10 @@ pub struct ModelData {
pub supports_vision: bool, pub supports_vision: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")] #[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub supports_function_calling: bool, pub supports_function_calling: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub reasoning_levels: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_reasoning_effort: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")] #[serde(default, skip_serializing_if = "std::ops::Not::not")]
no_stream: bool, no_stream: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")] #[serde(default, skip_serializing_if = "std::ops::Not::not")]
+8
View File
@@ -356,6 +356,7 @@ pub fn openai_build_chat_completions_body(data: ChatCompletionsData, model: &Mod
messages, messages,
temperature, temperature,
top_p, top_p,
reasoning_effort,
functions, functions,
stream, stream,
} = data; } = data;
@@ -454,6 +455,9 @@ pub fn openai_build_chat_completions_body(data: ChatCompletionsData, model: &Mod
if let Some(v) = top_p { if let Some(v) = top_p {
body["top_p"] = v.into(); body["top_p"] = v.into();
} }
if let Some(v) = reasoning_effort {
body["reasoning_effort"] = v.into();
}
if stream { if stream {
body["stream"] = true.into(); body["stream"] = true.into();
} }
@@ -534,6 +538,7 @@ pub fn openai_build_responses_body(data: ChatCompletionsData, model: &Model) ->
messages, messages,
temperature, temperature,
top_p, top_p,
reasoning_effort,
functions, functions,
stream, stream,
} = data; } = data;
@@ -590,6 +595,9 @@ pub fn openai_build_responses_body(data: ChatCompletionsData, model: &Model) ->
if let Some(v) = top_p { if let Some(v) = top_p {
body["top_p"] = v.into(); body["top_p"] = v.into();
} }
if let Some(v) = reasoning_effort {
body["reasoning"] = json!({ "effort": v });
}
if stream { if stream {
body["stream"] = true.into(); body["stream"] = true.into();
} }
+4
View File
@@ -334,6 +334,7 @@ pub fn gemini_build_chat_completions_body(
mut messages, mut messages,
temperature, temperature,
top_p, top_p,
reasoning_effort,
functions, functions,
stream: _, stream: _,
} = data; } = data;
@@ -426,6 +427,9 @@ pub fn gemini_build_chat_completions_body(
if let Some(v) = top_p { if let Some(v) = top_p {
body["generationConfig"]["topP"] = v.into(); body["generationConfig"]["topP"] = v.into();
} }
if let Some(v) = reasoning_effort {
body["generation_config"]["thinking_level"] = v.into();
}
if let Some(functions) = functions { if let Some(functions) = functions {
// Gemini doesn't support functions with parameters that have empty properties, so we need to patch it. // Gemini doesn't support functions with parameters that have empty properties, so we need to patch it.
+14
View File
@@ -575,6 +575,10 @@ impl RoleLike for Agent {
self.config.top_p self.config.top_p
} }
fn reasoning_effort(&self) -> Option<String> {
self.config.reasoning_effort.clone()
}
fn enabled_tools(&self) -> Option<Vec<String>> { fn enabled_tools(&self) -> Option<Vec<String>> {
None None
} }
@@ -596,6 +600,10 @@ impl RoleLike for Agent {
self.config.top_p = value; self.config.top_p = value;
} }
fn set_reasoning_effort(&mut self, value: Option<String>) {
self.config.reasoning_effort = value;
}
fn set_enabled_tools(&mut self, value: Option<Vec<String>>) { fn set_enabled_tools(&mut self, value: Option<Vec<String>>) {
match value { match value {
Some(tools) => { Some(tools) => {
@@ -637,6 +645,8 @@ pub struct AgentConfig {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>, pub top_p: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_session: Option<String>, pub agent_session: Option<String>,
#[serde(default)] #[serde(default)]
pub auto_continue: bool, pub auto_continue: bool,
@@ -732,6 +742,7 @@ impl AgentConfig {
model_id: graph.model.clone(), model_id: graph.model.clone(),
temperature: graph.temperature, temperature: graph.temperature,
top_p: graph.top_p, top_p: graph.top_p,
reasoning_effort: graph.reasoning_effort.clone(),
description: graph.description.clone(), description: graph.description.clone(),
global_tools: graph.global_tools.clone(), global_tools: graph.global_tools.clone(),
mcp_servers: graph.mcp_servers.clone(), mcp_servers: graph.mcp_servers.clone(),
@@ -766,6 +777,9 @@ impl AgentConfig {
if let Some(v) = read_env_value::<f64>(&with_prefix("top_p")) { if let Some(v) = read_env_value::<f64>(&with_prefix("top_p")) {
self.top_p = v; self.top_p = v;
} }
if let Some(v) = read_env_value::<String>(&with_prefix("reasoning_effort")) {
self.reasoning_effort = v;
}
if let Ok(v) = env::var(with_prefix("global_tools")) if let Ok(v) = env::var(with_prefix("global_tools"))
&& let Ok(v) = serde_json::from_str(&v) && let Ok(v) = serde_json::from_str(&v)
{ {
+33 -1
View File
@@ -1,4 +1,4 @@
use crate::client::{ClientConfig, list_models}; use crate::client::{ClientConfig, Model, ModelType, list_models};
use crate::render::{MarkdownRender, RenderOptions}; use crate::render::{MarkdownRender, RenderOptions};
use crate::utils::{IS_STDOUT_TERMINAL, NO_COLOR, decode_bin, get_env_name}; use crate::utils::{IS_STDOUT_TERMINAL, NO_COLOR, decode_bin, get_env_name};
@@ -21,6 +21,7 @@ pub struct AppConfig {
pub model_id: String, pub model_id: String,
pub temperature: Option<f64>, pub temperature: Option<f64>,
pub top_p: Option<f64>, pub top_p: Option<f64>,
pub reasoning_effort: Option<String>,
pub dry_run: bool, pub dry_run: bool,
pub stream: bool, pub stream: bool,
@@ -100,6 +101,7 @@ impl Default for AppConfig {
model_id: Default::default(), model_id: Default::default(),
temperature: None, temperature: None,
top_p: None, top_p: None,
reasoning_effort: None,
dry_run: false, dry_run: false,
stream: true, stream: true,
@@ -177,6 +179,7 @@ impl AppConfig {
model_id: config.model_id, model_id: config.model_id,
temperature: config.temperature, temperature: config.temperature,
top_p: config.top_p, top_p: config.top_p,
reasoning_effort: None,
dry_run: config.dry_run, dry_run: config.dry_run,
stream: config.stream, stream: config.stream,
@@ -253,6 +256,7 @@ impl AppConfig {
app_config.setup_document_loaders(); app_config.setup_document_loaders();
app_config.setup_user_agent(); app_config.setup_user_agent();
app_config.resolve_model()?; app_config.resolve_model()?;
app_config.validate_reasoning_effort()?;
Ok(app_config) Ok(app_config)
} }
@@ -273,6 +277,31 @@ impl AppConfig {
Ok(()) Ok(())
} }
fn validate_reasoning_effort(&self) -> Result<()> {
let Some(ref effort) = self.reasoning_effort else {
return Ok(());
};
let model = Model::retrieve_model(self, &self.model_id, ModelType::Chat)?;
let levels = model.reasoning_levels();
if levels.is_empty() {
bail!(
"reasoning_effort '{}' is configured but the model does not support reasoning effort",
effort
);
}
if !levels.iter().any(|l| l == effort) {
bail!(
"reasoning_effort '{}' is not valid for the model. Supported levels: {}",
effort,
levels.join(", ")
);
}
Ok(())
}
pub fn resolve_model(&mut self) -> Result<()> { pub fn resolve_model(&mut self) -> Result<()> {
if self.model_id.is_empty() { if self.model_id.is_empty() {
let models = list_models(self, crate::client::ModelType::Chat); let models = list_models(self, crate::client::ModelType::Chat);
@@ -426,6 +455,9 @@ impl AppConfig {
if let Some(v) = super::read_env_value::<f64>(&get_env_name("top_p")) { if let Some(v) = super::read_env_value::<f64>(&get_env_name("top_p")) {
self.top_p = v; self.top_p = v;
} }
if let Some(v) = super::read_env_value::<String>(&get_env_name("reasoning_effort")) {
self.reasoning_effort = v;
}
if let Some(Some(v)) = super::read_env_bool(&get_env_name("dry_run")) { if let Some(Some(v)) = super::read_env_bool(&get_env_name("dry_run")) {
self.dry_run = v; self.dry_run = v;
+5
View File
@@ -253,6 +253,10 @@ impl Input {
patch_messages(&mut messages, model); patch_messages(&mut messages, model);
model.guard_max_input_tokens(&messages)?; model.guard_max_input_tokens(&messages)?;
let (temperature, top_p) = (self.role().temperature(), self.role().top_p()); let (temperature, top_p) = (self.role().temperature(), self.role().top_p());
let reasoning_effort = self
.role()
.reasoning_effort()
.or_else(|| model.default_reasoning_effort().map(|s| s.to_string()));
let functions = if model.supports_function_calling() { let functions = if model.supports_function_calling() {
let fns = self.functions.clone(); let fns = self.functions.clone();
if let Some(vec) = &fns { if let Some(vec) = &fns {
@@ -268,6 +272,7 @@ impl Input {
messages, messages,
temperature, temperature,
top_p, top_p,
reasoning_effort,
functions, functions,
stream, stream,
}) })
+93
View File
@@ -969,6 +969,16 @@ impl RequestContext {
} }
} }
pub fn set_reasoning_effort_on_role_like(&mut self, value: Option<String>) -> bool {
match self.role_like_mut() {
Some(role_like) => {
role_like.set_reasoning_effort(value);
true
}
None => false,
}
}
pub fn set_enabled_tools_on_role_like(&mut self, value: Option<Vec<String>>) -> bool { pub fn set_enabled_tools_on_role_like(&mut self, value: Option<Vec<String>>) -> bool {
match self.role_like_mut() { match self.role_like_mut() {
Some(role_like) => { Some(role_like) => {
@@ -1121,6 +1131,10 @@ impl RequestContext {
super::format_option_value(&role.temperature()), super::format_option_value(&role.temperature()),
), ),
("top_p", super::format_option_value(&role.top_p())), ("top_p", super::format_option_value(&role.top_p())),
(
"reasoning_effort",
super::format_option_value(&role.reasoning_effort()),
),
( (
"enabled_tools", "enabled_tools",
super::format_option_value(&role.enabled_tools().map(|v| v.join(","))), super::format_option_value(&role.enabled_tools().map(|v| v.join(","))),
@@ -2009,6 +2023,24 @@ impl RequestContext {
self.update_app_config(|app| app.top_p = value); self.update_app_config(|app| app.top_p = value);
} }
} }
"reasoning_effort" => {
let value: Option<String> = super::parse_value(value)?;
if let Some(ref level) = value {
let levels = self.current_model().reasoning_levels();
if levels.is_empty() {
bail!("The current model does not support reasoning effort configuration");
}
if !levels.iter().any(|l| l == level) {
bail!(
"Invalid reasoning effort '{level}'. Supported levels for this model: {}",
levels.join(", ")
);
}
}
if !self.set_reasoning_effort_on_role_like(value.clone()) {
self.update_app_config(|app| app.reasoning_effort = value);
}
}
"enabled_tools" => { "enabled_tools" => {
let raw: Option<String> = super::parse_value(value)?; let raw: Option<String> = super::parse_value(value)?;
let parsed: Option<Vec<String>> = raw.map(|s| super::csv_to_vec(&s)); let parsed: Option<Vec<String>> = raw.map(|s| super::csv_to_vec(&s));
@@ -2303,6 +2335,10 @@ impl RequestContext {
super::map_completion_values(values) super::map_completion_values(values)
} }
".macro" => super::map_completion_values(paths::list_macros()), ".macro" => super::map_completion_values(paths::list_macros()),
".reasoning" => {
let levels = self.current_model().reasoning_levels();
levels.iter().map(|v| (v.clone(), None)).collect()
}
".starter" => match &self.agent { ".starter" => match &self.agent {
Some(agent) => agent Some(agent) => agent
.conversation_starters() .conversation_starters()
@@ -2338,6 +2374,9 @@ impl RequestContext {
"save", "save",
"highlight", "highlight",
]; ];
if !self.current_model().reasoning_levels().is_empty() {
values.push("reasoning_effort");
}
values.sort_unstable(); values.sort_unstable();
values values
.into_iter() .into_iter()
@@ -2507,6 +2546,10 @@ impl RequestContext {
} }
"skill_instructions" => vec!["null".to_string()], "skill_instructions" => vec!["null".to_string()],
"memory" => super::complete_bool(self.should_inject_memory()), "memory" => super::complete_bool(self.should_inject_memory()),
"reasoning_effort" => {
let levels = self.current_model().reasoning_levels();
levels.to_vec()
}
_ => vec![], _ => vec![],
}; };
values = candidates.into_iter().map(|v| (v, None)).collect(); values = candidates.into_iter().map(|v| (v, None)).collect();
@@ -2704,6 +2747,22 @@ impl RequestContext {
None None
}; };
if let Some(ref effort) = role.reasoning_effort() {
let levels = role.model().reasoning_levels();
if levels.is_empty() {
bail!(
"Role has reasoning_effort '{}' configured but the model does not support reasoning effort",
effort
);
}
if !levels.iter().any(|l| l == effort) {
bail!(
"Role's reasoning_effort '{}' is not valid for the model. Supported levels: {}",
effort,
levels.join(", ")
);
}
}
self.use_role_obj(role)?; self.use_role_obj(role)?;
self.rebuild_tool_scope(app, mcp_servers, abort_signal) self.rebuild_tool_scope(app, mcp_servers, abort_signal)
.await .await
@@ -2759,6 +2818,23 @@ impl RequestContext {
None None
}; };
if let Some(ref effort) = session.reasoning_effort() {
let levels = session.model().reasoning_levels();
if levels.is_empty() {
bail!(
"Session has reasoning_effort '{}' configured but the model does not support reasoning effort",
effort
);
}
if !levels.iter().any(|l| l == effort) {
bail!(
"Session's reasoning_effort '{}' is not valid for the model. Supported levels: {}",
effort,
levels.join(", ")
);
}
}
self.rebuild_tool_scope(app, mcp_servers, abort_signal.clone()) self.rebuild_tool_scope(app, mcp_servers, abort_signal.clone())
.await?; .await?;
@@ -2813,6 +2889,23 @@ impl RequestContext {
) )
.await?; .await?;
if let Some(ref effort) = agent.reasoning_effort() {
let levels = agent.model().reasoning_levels();
if levels.is_empty() {
bail!(
"Agent has reasoning_effort '{}' configured but the model does not support reasoning effort",
effort
);
}
if !levels.iter().any(|l| l == effort) {
bail!(
"Agent's reasoning_effort '{}' is not valid for the model. Supported levels: {}",
effort,
levels.join(", ")
);
}
}
let is_graph_agent = graph::agent_has_graph(agent_name); let is_graph_agent = graph::agent_has_graph(agent_name);
if is_graph_agent && session_name.is_some() { if is_graph_agent && session_name.is_some() {
bail!( bail!(
+21
View File
@@ -32,7 +32,9 @@ pub trait RoleLike {
fn enabled_mcp_servers(&self) -> Option<Vec<String>>; fn enabled_mcp_servers(&self) -> Option<Vec<String>>;
fn set_model(&mut self, model: Model); fn set_model(&mut self, model: Model);
fn set_temperature(&mut self, value: Option<f64>); fn set_temperature(&mut self, value: Option<f64>);
fn reasoning_effort(&self) -> Option<String>;
fn set_top_p(&mut self, value: Option<f64>); fn set_top_p(&mut self, value: Option<f64>);
fn set_reasoning_effort(&mut self, value: Option<String>);
fn set_enabled_tools(&mut self, value: Option<Vec<String>>); fn set_enabled_tools(&mut self, value: Option<Vec<String>>);
fn set_enabled_mcp_servers(&mut self, value: Option<Vec<String>>); fn set_enabled_mcp_servers(&mut self, value: Option<Vec<String>>);
} }
@@ -51,6 +53,8 @@ pub struct Role {
temperature: Option<f64>, temperature: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
top_p: Option<f64>, top_p: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
reasoning_effort: Option<String>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
@@ -116,6 +120,9 @@ impl Role {
"model" => role.model_id = value.as_str().map(|v| v.to_string()), "model" => role.model_id = value.as_str().map(|v| v.to_string()),
"temperature" => role.temperature = value.as_f64(), "temperature" => role.temperature = value.as_f64(),
"top_p" => role.top_p = value.as_f64(), "top_p" => role.top_p = value.as_f64(),
"reasoning_effort" => {
role.reasoning_effort = value.as_str().map(|v| v.to_string())
}
"enabled_tools" => role.enabled_tools = parse_string_or_array(value), "enabled_tools" => role.enabled_tools = parse_string_or_array(value),
"enabled_mcp_servers" => { "enabled_mcp_servers" => {
role.enabled_mcp_servers = parse_string_or_array(value) role.enabled_mcp_servers = parse_string_or_array(value)
@@ -170,6 +177,9 @@ impl Role {
if let Some(top_p) = self.top_p() { if let Some(top_p) = self.top_p() {
metadata.push(format!("top_p: {top_p}")); metadata.push(format!("top_p: {top_p}"));
} }
if let Some(reasoning_effort) = self.reasoning_effort() {
metadata.push(format!("reasoning_effort: {reasoning_effort}"));
}
if let Some(enabled_tools) = &self.enabled_tools { if let Some(enabled_tools) = &self.enabled_tools {
let inline = serde_json::to_string(enabled_tools).unwrap_or_else(|_| "[]".to_string()); let inline = serde_json::to_string(enabled_tools).unwrap_or_else(|_| "[]".to_string());
metadata.push(format!("enabled_tools: {inline}")); metadata.push(format!("enabled_tools: {inline}"));
@@ -256,6 +266,9 @@ impl Role {
enabled_tools, enabled_tools,
enabled_mcp_servers, enabled_mcp_servers,
); );
if let Some(v) = role_like.reasoning_effort() {
self.set_reasoning_effort(Some(v));
}
} }
pub fn batch_set( pub fn batch_set(
@@ -410,6 +423,10 @@ impl RoleLike for Role {
self.top_p self.top_p
} }
fn reasoning_effort(&self) -> Option<String> {
self.reasoning_effort.clone()
}
fn enabled_tools(&self) -> Option<Vec<String>> { fn enabled_tools(&self) -> Option<Vec<String>> {
self.enabled_tools.clone() self.enabled_tools.clone()
} }
@@ -433,6 +450,10 @@ impl RoleLike for Role {
self.top_p = value; self.top_p = value;
} }
fn set_reasoning_effort(&mut self, value: Option<String>) {
self.reasoning_effort = value;
}
fn set_enabled_tools(&mut self, value: Option<Vec<String>>) { fn set_enabled_tools(&mut self, value: Option<Vec<String>>) {
self.enabled_tools = value; self.enabled_tools = value;
} }
+14
View File
@@ -24,6 +24,8 @@ pub struct Session {
temperature: Option<f64>, temperature: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
top_p: Option<f64>, top_p: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
reasoning_effort: Option<String>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
@@ -401,6 +403,7 @@ impl Session {
self.model_id = role.model().id(); self.model_id = role.model().id();
self.temperature = role.temperature(); self.temperature = role.temperature();
self.top_p = role.top_p(); self.top_p = role.top_p();
self.reasoning_effort = role.reasoning_effort();
self.enabled_tools = role.enabled_tools(); self.enabled_tools = role.enabled_tools();
self.enabled_mcp_servers = role.enabled_mcp_servers(); self.enabled_mcp_servers = role.enabled_mcp_servers();
self.model = role.model().clone(); self.model = role.model().clone();
@@ -792,6 +795,10 @@ impl RoleLike for Session {
self.top_p self.top_p
} }
fn reasoning_effort(&self) -> Option<String> {
self.reasoning_effort.clone()
}
fn enabled_tools(&self) -> Option<Vec<String>> { fn enabled_tools(&self) -> Option<Vec<String>> {
self.enabled_tools.clone() self.enabled_tools.clone()
} }
@@ -823,6 +830,13 @@ impl RoleLike for Session {
} }
} }
fn set_reasoning_effort(&mut self, value: Option<String>) {
if self.reasoning_effort != value {
self.reasoning_effort = value;
self.dirty = true;
}
}
fn set_enabled_tools(&mut self, value: Option<Vec<String>>) { fn set_enabled_tools(&mut self, value: Option<Vec<String>>) {
if self.enabled_tools != value { if self.enabled_tools != value {
self.enabled_tools = value; self.enabled_tools = value;
+4
View File
@@ -329,6 +329,9 @@ fn build_inline_role(
if let Some(p) = node.top_p { if let Some(p) = node.top_p {
role.set_top_p(Some(p)); role.set_top_p(Some(p));
} }
if let Some(v) = &node.reasoning_effort {
role.set_reasoning_effort(Some(v.clone()));
}
if node.tools.as_deref().unwrap_or_default().is_empty() { if node.tools.as_deref().unwrap_or_default().is_empty() {
role.set_enabled_tools(Some(Vec::new())); role.set_enabled_tools(Some(Vec::new()));
@@ -499,6 +502,7 @@ mod tests {
model: None, model: None,
temperature: None, temperature: None,
top_p: None, top_p: None,
reasoning_effort: None,
fallback: None, fallback: None,
max_attempts: 1, max_attempts: 1,
max_iterations: 10, max_iterations: 10,
+6
View File
@@ -25,6 +25,9 @@ pub struct Graph {
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>, pub top_p: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<String>,
#[serde(default)] #[serde(default)]
pub global_tools: Vec<String>, pub global_tools: Vec<String>,
@@ -288,6 +291,9 @@ pub struct LlmNode {
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>, pub top_p: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub fallback: Option<String>, pub fallback: Option<String>,
+2
View File
@@ -946,6 +946,7 @@ mod tests {
model: None, model: None,
temperature: None, temperature: None,
top_p: None, top_p: None,
reasoning_effort: None,
global_tools: Vec::new(), global_tools: Vec::new(),
mcp_servers: Vec::new(), mcp_servers: Vec::new(),
skills_enabled: None, skills_enabled: None,
@@ -1048,6 +1049,7 @@ mod tests {
model: None, model: None,
temperature: None, temperature: None,
top_p: None, top_p: None,
reasoning_effort: None,
fallback: fallback.map(String::from), fallback: fallback.map(String::from),
max_attempts: 1, max_attempts: 1,
max_iterations: 10, max_iterations: 10,
+1
View File
@@ -362,6 +362,7 @@ pub async fn extract_entities(
messages, messages,
temperature: Some(0.0), temperature: Some(0.0),
top_p: None, top_p: None,
reasoning_effort: None,
functions: None, functions: None,
stream: false, stream: false,
}; };
+2
View File
@@ -31,6 +31,7 @@ impl Completer for ReplCompleter {
let ctx = self.ctx.read(); let ctx = self.ctx.read();
let state = ctx.state(); let state = ctx.state();
let model_has_reasoning = !ctx.current_model().reasoning_levels().is_empty();
let command_filter = parts let command_filter = parts
.iter() .iter()
@@ -44,6 +45,7 @@ impl Completer for ReplCompleter {
.filter(|cmd| { .filter(|cmd| {
cmd.is_valid(state) cmd.is_valid(state)
&& (command_filter.len() == 1 || cmd.name.starts_with(&command_filter[..2])) && (command_filter.len() == 1 || cmd.name.starts_with(&command_filter[..2]))
&& (cmd.name != ".reasoning" || model_has_reasoning)
}) })
.collect(); .collect();
let commands = fuzzy_filter(commands, |v| v.name, &command_filter); let commands = fuzzy_filter(commands, |v| v.name, &command_filter);
+17 -3
View File
@@ -52,7 +52,7 @@ pub const DEFAULT_CONTINUATION_PROMPT: &str = indoc! {"
4. Continue with the next pending item now. Call tools immediately." 4. Continue with the next pending item now. Call tools immediately."
}; };
static REPL_COMMANDS: LazyLock<[ReplCommand; 51]> = LazyLock::new(|| { static REPL_COMMANDS: LazyLock<[ReplCommand; 52]> = LazyLock::new(|| {
[ [
ReplCommand::new(".help", "Show this help guide", AssertState::pass()), ReplCommand::new(".help", "Show this help guide", AssertState::pass()),
ReplCommand::new(".info", "Show system info", AssertState::pass()), ReplCommand::new(".info", "Show system info", AssertState::pass()),
@@ -259,6 +259,11 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 51]> = LazyLock::new(|| {
), ),
ReplCommand::new(".copy", "Copy last response", AssertState::pass()), ReplCommand::new(".copy", "Copy last response", AssertState::pass()),
ReplCommand::new(".set", "Modify runtime settings", AssertState::pass()), ReplCommand::new(".set", "Modify runtime settings", AssertState::pass()),
ReplCommand::new(
".reasoning",
"Set the reasoning effort level for the current model",
AssertState::pass(),
),
ReplCommand::new( ReplCommand::new(
".delete", ".delete",
"Delete roles, sessions, RAGs, or agents", "Delete roles, sessions, RAGs, or agents",
@@ -1070,6 +1075,15 @@ pub async fn run_repl_command(
println!("Usage: .set <key> <value>...") println!("Usage: .set <key> <value>...")
} }
}, },
".reasoning" => match args {
Some(level) => {
let set_args = format!("reasoning_effort {level}");
ctx.update(&set_args, abort_signal).await?;
}
None => {
println!("Usage: .reasoning <level>")
}
},
".delete" => match args { ".delete" => match args {
Some(args) => { Some(args) => {
ctx.delete(args)?; ctx.delete(args)?;
@@ -1600,8 +1614,8 @@ mod tests {
} }
#[test] #[test]
fn repl_commands_has_50_entries() { fn repl_commands_has_52_entries() {
assert_eq!(REPL_COMMANDS.len(), 50); assert_eq!(REPL_COMMANDS.len(), 52);
} }
#[test] #[test]