diff --git a/rust/crates/api/src/providers/openai_compat.rs b/rust/crates/api/src/providers/openai_compat.rs index 3344765..140d93e 100644 --- a/rust/crates/api/src/providers/openai_compat.rs +++ b/rust/crates/api/src/providers/openai_compat.rs @@ -801,6 +801,10 @@ fn build_chat_completion_request(request: &MessageRequest, config: OpenAiCompatC payload["stop"] = json!(stop); } } + // reasoning_effort for OpenAI-compatible reasoning models (o4-mini, o3, etc.) + if let Some(effort) = &request.reasoning_effort { + payload["reasoning_effort"] = json!(effort); + } payload } @@ -1216,6 +1220,35 @@ mod tests { ); } + #[test] + fn reasoning_effort_is_included_when_set() { + let payload = build_chat_completion_request( + &MessageRequest { + model: "o4-mini".to_string(), + max_tokens: 1024, + messages: vec![InputMessage::user_text("think hard")], + reasoning_effort: Some("high".to_string()), + ..Default::default() + }, + OpenAiCompatConfig::openai(), + ); + assert_eq!(payload["reasoning_effort"], json!("high")); + } + + #[test] + fn reasoning_effort_omitted_when_not_set() { + let payload = build_chat_completion_request( + &MessageRequest { + model: "gpt-4o".to_string(), + max_tokens: 64, + messages: vec![InputMessage::user_text("hello")], + ..Default::default() + }, + OpenAiCompatConfig::openai(), + ); + assert!(payload.get("reasoning_effort").is_none()); + } + #[test] fn openai_streaming_requests_include_usage_opt_in() { let payload = build_chat_completion_request( @@ -1333,6 +1366,7 @@ mod tests { frequency_penalty: Some(0.5), presence_penalty: Some(0.3), stop: Some(vec!["\n".to_string()]), + reasoning_effort: None, }; let payload = build_chat_completion_request(&request, OpenAiCompatConfig::openai()); assert_eq!(payload["temperature"], 0.7); diff --git a/rust/crates/api/src/types.rs b/rust/crates/api/src/types.rs index 830b3de..e136a76 100644 --- a/rust/crates/api/src/types.rs +++ b/rust/crates/api/src/types.rs @@ -26,6 +26,11 @@ pub struct MessageRequest { pub presence_penalty: Option, #[serde(skip_serializing_if = "Option::is_none")] pub stop: Option>, + /// Reasoning effort level for OpenAI-compatible reasoning models (e.g. `o4-mini`). + /// Accepted values: `"low"`, `"medium"`, `"high"`. Omitted when `None`. + /// Silently ignored by backends that do not support it. + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning_effort: Option, } impl MessageRequest {