use locode_protocol::{ContentBlock, Usage};
use super::wire;
use crate::completion::{Completion, StopReason};
use crate::provider::ProviderError;
pub fn response_to_completion(resp: wire::MessagesResponse) -> Result<Completion, ProviderError> {
let mut content: Vec<ContentBlock> = Vec::with_capacity(resp.content.len());
for block in resp.content {
match block {
wire::ContentBlock::Text { text, .. } => {
content.push(ContentBlock::Text { text });
}
wire::ContentBlock::ToolUse { id, name, input } => {
content.push(ContentBlock::ToolUse { id, name, input });
}
wire::ContentBlock::Thinking {
thinking,
signature,
} => {
content.push(ContentBlock::Reasoning {
format: locode_protocol::ReasoningFormat::Anthropic,
text: thinking,
signature: Some(signature),
payload: None,
});
}
wire::ContentBlock::RedactedThinking { data } => {
content.push(ContentBlock::Reasoning {
format: locode_protocol::ReasoningFormat::AnthropicRedacted,
text: String::new(),
signature: None,
payload: Some(serde_json::Value::String(data)),
});
}
wire::ContentBlock::Image { .. } | wire::ContentBlock::ToolResult { .. } => {}
}
}
if matches!(
resp.stop_reason,
Some(wire::StopReason::ModelContextWindowExceeded)
) && content.is_empty()
{
return Err(ProviderError::ContextOverflow);
}
Ok(Completion {
content,
usage: map_usage(&resp.usage),
stop: map_stop_reason(resp.stop_reason),
})
}
fn map_usage(usage: &wire::MessagesUsage) -> Usage {
Usage {
input_tokens: usage.input_tokens.unwrap_or_default(),
output_tokens: usage.output_tokens.unwrap_or_default(),
cache_read_tokens: usage.cache_read_input_tokens,
cache_creation_tokens: usage.cache_creation_input_tokens,
reasoning_tokens: None,
}
}
fn map_stop_reason(stop: Option<wire::StopReason>) -> StopReason {
match stop {
Some(wire::StopReason::EndTurn) => StopReason::EndTurn,
Some(wire::StopReason::MaxTokens) => StopReason::MaxTokens,
Some(wire::StopReason::ToolUse) => StopReason::ToolUse,
Some(wire::StopReason::StopSequence) => StopReason::StopSequence,
Some(wire::StopReason::Refusal) => StopReason::Refusal,
Some(wire::StopReason::PauseTurn) => StopReason::PauseTurn,
Some(wire::StopReason::ModelContextWindowExceeded) => {
StopReason::Unknown("model_context_window_exceeded".to_string())
}
Some(wire::StopReason::Unknown(raw)) => StopReason::Unknown(raw),
None => StopReason::Unknown("(missing stop_reason)".to_string()),
}
}