From ae9e4b4b24368403a1cdc94d3be9ef2c9d9d4088 Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Thu, 14 May 2026 14:48:24 -0500 Subject: [PATCH] fix(client): omit strict OpenAI-incompatible fields --- crates/tui/src/client.rs | 46 ++++++++++++++++++++++++++++++++--- crates/tui/src/client/chat.rs | 9 ------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/crates/tui/src/client.rs b/crates/tui/src/client.rs index 4744b6b0..50998854 100644 --- a/crates/tui/src/client.rs +++ b/crates/tui/src/client.rs @@ -887,10 +887,10 @@ pub(super) fn apply_reasoning_effort( | ApiProvider::DeepseekCN | ApiProvider::Openrouter | ApiProvider::Novita - | ApiProvider::Fireworks | ApiProvider::Sglang => { body["thinking"] = json!({ "type": "disabled" }); } + ApiProvider::Fireworks => {} // vLLM is an OpenAI-protocol server, not an Anthropic-protocol one. // For Qwen3 / DeepSeek-R1 / other reasoning models hosted via vLLM, // the canonical OpenAI extension to disable thinking is @@ -917,11 +917,13 @@ pub(super) fn apply_reasoning_effort( | ApiProvider::DeepseekCN | ApiProvider::Openrouter | ApiProvider::Novita - | ApiProvider::Fireworks | ApiProvider::Sglang => { body["reasoning_effort"] = json!("high"); body["thinking"] = json!({ "type": "enabled" }); } + ApiProvider::Fireworks => { + body["reasoning_effort"] = json!("high"); + } ApiProvider::Vllm => { body["chat_template_kwargs"] = json!({ "enable_thinking": true, @@ -941,11 +943,13 @@ pub(super) fn apply_reasoning_effort( | ApiProvider::DeepseekCN | ApiProvider::Openrouter | ApiProvider::Novita - | ApiProvider::Fireworks | ApiProvider::Sglang => { body["reasoning_effort"] = json!("max"); body["thinking"] = json!({ "type": "enabled" }); } + ApiProvider::Fireworks => { + body["reasoning_effort"] = json!("max"); + } ApiProvider::Vllm => { body["chat_template_kwargs"] = json!({ "enable_thinking": true, @@ -1918,6 +1922,21 @@ mod tests { ); } + #[test] + fn reasoning_effort_uses_openai_compatible_shape_for_fireworks() { + let mut body = json!({}); + apply_reasoning_effort(&mut body, Some("max"), ApiProvider::Fireworks); + + assert_eq!( + body.get("reasoning_effort").and_then(Value::as_str), + Some("max") + ); + assert!( + body.get("thinking").is_none(), + "Fireworks strict-validates OpenAI-compatible requests and rejects top-level thinking" + ); + } + #[test] fn chat_parser_accepts_nvidia_nim_reasoning_field() -> Result<()> { let response = parse_chat_message(&json!({ @@ -2055,6 +2074,27 @@ mod tests { } } + #[test] + fn chat_tool_wire_shape_omits_anthropic_only_metadata() { + let tool = Tool { + tool_type: Some("function".to_string()), + name: "mcp_read_resource".to_string(), + description: "Read resource".to_string(), + input_schema: json!({"type": "object", "properties": {}}), + allowed_callers: Some(vec!["direct".to_string()]), + defer_loading: Some(false), + input_examples: Some(vec![json!({"uri": "file://example"})]), + strict: None, + cache_control: None, + }; + + let encoded = tool_to_chat_for_base_url(&tool, "https://api.fireworks.ai/inference/v1"); + + assert!(encoded.get("allowed_callers").is_none()); + assert!(encoded.get("defer_loading").is_none()); + assert!(encoded.get("input_examples").is_none()); + } + #[test] fn chat_messages_drop_thinking_only_assistant_for_non_reasoning_model() { let message = Message { diff --git a/crates/tui/src/client/chat.rs b/crates/tui/src/client/chat.rs index 9f3b8e10..770cbdef 100644 --- a/crates/tui/src/client/chat.rs +++ b/crates/tui/src/client/chat.rs @@ -1396,15 +1396,6 @@ pub(super) fn tool_to_chat(tool: &Tool) -> Value { "parameters": tool.input_schema, } }); - if let Some(allowed_callers) = &tool.allowed_callers { - value["allowed_callers"] = json!(allowed_callers); - } - if let Some(defer_loading) = tool.defer_loading { - value["defer_loading"] = json!(defer_loading); - } - if let Some(input_examples) = &tool.input_examples { - value["input_examples"] = json!(input_examples); - } if let Some(strict) = tool.strict && let Some(function) = value.get_mut("function") {