Fix OpenRouter giving errors for some Anthropic models (#45399)

Fixes #44032

Release Notes:

- Fix OpenRouter giving errors for some Anthropic models
This commit is contained in:
Richard Feldman
2025-12-19 17:04:43 -05:00
committed by GitHub
parent 3f4da03d38
commit 1c576ccf82

View File

@@ -370,8 +370,8 @@ impl LanguageModel for OpenRouterLanguageModel {
LanguageModelCompletionError,
>,
> {
let request = into_open_router(request, &self.model, self.max_output_tokens());
let request = self.stream_completion(request, cx);
let openrouter_request = into_open_router(request, &self.model, self.max_output_tokens());
let request = self.stream_completion(openrouter_request, cx);
let future = self.request_limiter.stream(async move {
let response = request.await?;
Ok(OpenRouterEventMapper::new().map_stream(response))
@@ -385,15 +385,31 @@ pub fn into_open_router(
model: &Model,
max_output_tokens: Option<u64>,
) -> open_router::Request {
// Anthropic models via OpenRouter don't accept reasoning_details being echoed back
// in requests - it's an output-only field for them. However, Gemini models require
// the thought signatures to be echoed back for proper reasoning chain continuity.
// Note: OpenRouter's model API provides an `architecture.tokenizer` field (e.g. "Claude",
// "Gemini") which could replace this ID prefix check, but since this is the only place
// we need this distinction, we're just using this less invasive check instead.
// If we ever have a more formal distionction between the models in the future,
// we should revise this to use that instead.
let is_anthropic_model = model.id().starts_with("anthropic/");
let mut messages = Vec::new();
for message in request.messages {
let reasoning_details = message.reasoning_details.clone();
let reasoning_details_for_message = if is_anthropic_model {
None
} else {
message.reasoning_details.clone()
};
for content in message.content {
match content {
MessageContent::Text(text) => add_message_content_part(
open_router::MessagePart::Text { text },
message.role,
&mut messages,
reasoning_details_for_message.clone(),
),
MessageContent::Thinking { .. } => {}
MessageContent::RedactedThinking(_) => {}
@@ -404,6 +420,7 @@ pub fn into_open_router(
},
message.role,
&mut messages,
reasoning_details_for_message.clone(),
);
}
MessageContent::ToolUse(tool_use) => {
@@ -419,21 +436,15 @@ pub fn into_open_router(
},
};
if let Some(open_router::RequestMessage::Assistant {
tool_calls,
reasoning_details: existing_reasoning,
..
}) = messages.last_mut()
if let Some(open_router::RequestMessage::Assistant { tool_calls, .. }) =
messages.last_mut()
{
tool_calls.push(tool_call);
if existing_reasoning.is_none() && reasoning_details.is_some() {
*existing_reasoning = reasoning_details.clone();
}
} else {
messages.push(open_router::RequestMessage::Assistant {
content: None,
tool_calls: vec![tool_call],
reasoning_details: reasoning_details.clone(),
reasoning_details: reasoning_details_for_message.clone(),
});
}
}
@@ -509,6 +520,7 @@ fn add_message_content_part(
new_part: open_router::MessagePart,
role: Role,
messages: &mut Vec<open_router::RequestMessage>,
reasoning_details: Option<serde_json::Value>,
) {
match (role, messages.last_mut()) {
(Role::User, Some(open_router::RequestMessage::User { content }))
@@ -532,7 +544,7 @@ fn add_message_content_part(
Role::Assistant => open_router::RequestMessage::Assistant {
content: Some(open_router::MessageContent::from(vec![new_part])),
tool_calls: Vec::new(),
reasoning_details: None,
reasoning_details,
},
Role::System => open_router::RequestMessage::System {
content: open_router::MessageContent::from(vec![new_part]),