fix(client): omit strict OpenAI-incompatible fields

This commit is contained in:
Hunter Bown
2026-05-14 14:48:24 -05:00
parent ab8d71a378
commit ae9e4b4b24
2 changed files with 43 additions and 12 deletions
+43 -3
View File
@@ -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 {
-9
View File
@@ -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")
{