Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions providers/byteplus-coding.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ input_cost_per_m = 0.44
output_cost_per_m = 2.0
supports_tools = true
supports_streaming = true
reasoning_echo_policy = "empty_string"
aliases = []

[[models]]
Expand Down
7 changes: 7 additions & 0 deletions providers/deepseek.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ supports_tools = true
supports_vision = false
supports_streaming = true
supports_thinking = true
# Thinking mode is on by default and the API rejects multi-turn requests
# where assistant turns containing tool_calls don't echo back the original
# reasoning_content. See librefang/librefang#4842.
reasoning_echo_policy = "echo"
aliases = ["deepseek-flash"]

[[models]]
Expand Down Expand Up @@ -61,4 +65,7 @@ supports_tools = false
supports_vision = false
supports_streaming = true
supports_thinking = true
# R1 returns reasoning_content in responses but the API rejects multi-turn
# requests that carry it on previous assistant messages — drivers must strip.
reasoning_echo_policy = "strip"
aliases = ["deepseek-r1"]
1 change: 1 addition & 0 deletions providers/kimi-coding.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ output_cost_per_m = 0.0
supports_tools = true
supports_vision = true
supports_streaming = true
reasoning_echo_policy = "empty_string"
aliases = []
6 changes: 6 additions & 0 deletions providers/moonshot.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ supports_tools = true
supports_vision = true
supports_streaming = true
supports_thinking = true
# Kimi requires reasoning_content present (empty string) on assistant
# turns with tool_calls, with thinking disabled wire-side for multi-turn
# compatibility. See librefang/librefang openai driver.
reasoning_echo_policy = "empty_string"
aliases = ["kimi", "kimi-k2.6-0420"]

[[models]]
Expand All @@ -34,6 +38,7 @@ supports_tools = true
supports_vision = true
supports_streaming = true
supports_thinking = true
reasoning_echo_policy = "empty_string"
aliases = ["kimi-k2.5-0711"]

[[models]]
Expand All @@ -47,4 +52,5 @@ output_cost_per_m = 2.3
supports_tools = true
supports_vision = true
supports_streaming = true
reasoning_echo_policy = "empty_string"
aliases = []
1 change: 1 addition & 0 deletions providers/novita.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ supports_tools = true
supports_vision = false
supports_streaming = true
supports_thinking = true
reasoning_echo_policy = "empty_string"
aliases = []

[[models]]
Expand Down
7 changes: 7 additions & 0 deletions schema.toml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ required = false
description = "Extended thinking / reasoning support"
default = false

[provider.sections.models.fields.reasoning_echo_policy]
type = "string"
required = false
description = "How the OpenAI-compatible driver must handle the reasoning_content field on historical assistant turns when this model is used. 'none' (default) omits the field entirely. 'strip' is required by DeepSeek-R1 / deepseek-reasoner — the API rejects multi-turn requests that carry reasoning_content from previous turns. 'echo' is required by DeepSeek V4 Flash and other thinking-mode-on models — the original thinking text MUST be round-tripped on assistant turns that contain tool_calls, otherwise the API returns 400. 'empty_string' is required by Moonshot / Kimi K2 — the field must be present (empty string) on tool_calls turns, with thinking disabled wire-side."
options = ["none", "strip", "echo", "empty_string"]
default = "none"

[provider.sections.models.fields.aliases]
type = "array"
required = false
Expand Down
8 changes: 8 additions & 0 deletions scripts/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

VALID_TIERS = {"frontier", "smart", "balanced", "fast", "local"}
VALID_MODALITIES = {"text", "image", "audio", "video", "music"}
VALID_REASONING_ECHO_POLICIES = {"none", "strip", "echo", "empty_string"}
VALID_HAND_CATEGORIES = {
"communication", "content", "data", "development",
"devops", "finance", "productivity", "research", "social",
Expand Down Expand Up @@ -102,6 +103,13 @@ def validate_provider_file(filepath: Path) -> list[str]:
if tier is not None and tier not in VALID_TIERS:
errors.append(f"{filepath.name}: Model '{label}' invalid tier '{tier}'")

policy = model.get("reasoning_echo_policy")
if policy is not None and policy not in VALID_REASONING_ECHO_POLICIES:
errors.append(
f"{filepath.name}: Model '{label}' invalid reasoning_echo_policy "
f"'{policy}' (valid: {', '.join(sorted(VALID_REASONING_ECHO_POLICIES))})"
)

for cost_field in (
"input_cost_per_m",
"output_cost_per_m",
Expand Down
Loading