alith_interface/llms/api/anthropic/completion/
res.rs

1use crate::requests::completion::*;
2use serde::{Deserialize, Serialize};
3use tool::{Function, ToolCall};
4
5impl CompletionResponse {
6    pub fn new_from_anthropic(
7        req: &CompletionRequest,
8        res: AnthropicCompletionResponse,
9    ) -> Result<Self, CompletionError> {
10        let finish_reason = match res.stop_reason {
11            StopReason::EndTurn => CompletionFinishReason::Eos,
12            StopReason::StopSequence => {
13                if let Some(stopping_string) = &res.stop_sequence {
14                    if let Some(stop_sequence) =
15                        req.stop_sequences.parse_string_response(stopping_string)
16                    {
17                        CompletionFinishReason::MatchingStoppingSequence(stop_sequence)
18                    } else {
19                        CompletionFinishReason::NonMatchingStoppingSequence(Some(
20                            stopping_string.clone(),
21                        ))
22                    }
23                } else {
24                    CompletionFinishReason::NonMatchingStoppingSequence(None)
25                }
26            }
27            StopReason::MaxTokens => CompletionFinishReason::StopLimit,
28            StopReason::ToolUse => {
29                return Err(CompletionError::StopReasonUnsupported(
30                    "StopReason::ToolUse is not supported".to_owned(),
31                ));
32            }
33        };
34
35        if res.content.is_empty() {
36            return Err(CompletionError::ResponseContentEmpty);
37        }
38
39        if res.content.len() > 1 {
40            return Err(CompletionError::ResponseContentEmpty);
41        }
42        let content = res
43            .content
44            .first()
45            .ok_or_else(|| CompletionError::ResponseContentEmpty)?
46            .text()
47            .to_owned();
48
49        Ok(Self {
50            id: res.id.to_owned(),
51            index: None,
52            content,
53            finish_reason,
54            completion_probabilities: None,
55            truncated: false,
56            generation_settings: GenerationSettings::new_from_anthropic(req, &res),
57            timing_usage: TimingUsage::new_from_generic(req.start_time),
58            token_usage: TokenUsage::new_from_anthropic(&res),
59            tool_calls: match res.content.as_slice() {
60                [
61                    CompletionContent::ToolUse {
62                        name,
63                        input,
64                        id,
65                        r#type,
66                    },
67                    ..,
68                ] => Some(vec![ToolCall {
69                    id: id.to_owned(),
70                    r#type: r#type.to_owned(),
71                    function: Function {
72                        name: name.to_owned(),
73                        arguments: serde_json::to_string(input)?,
74                    },
75                }]),
76                _ => None,
77            },
78        })
79    }
80}
81
82/// Represents a chat completion response returned by model, based on the provided input.
83#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
84pub struct AnthropicCompletionResponse {
85    /// Unique object identifier.
86    ///
87    /// The format and length of IDs may change over time.
88    pub id: String,
89    /// Content generated by the model.
90    ///
91    /// This is an array of content blocks, each of which has a type that determines its shape. Currently, the only type in responses is "text".
92    pub content: Vec<CompletionContent>,
93    /// The model that handled the request.
94    pub model: String,
95    /// The reason that we stopped.
96    ///
97    /// This may be one of the following values:
98    ///
99    /// "end_turn": the model reached a natural stopping point
100    /// "max_tokens": we exceeded the requested max_tokens or the model's maximum
101    /// "stop_sequence": one of your provided custom stop_sequences was generated
102    pub stop_reason: StopReason,
103    /// Which custom stop sequence was generated, if any.
104    ///
105    /// This value will be a non-null string if one of your custom stop sequences was generated.
106    pub stop_sequence: Option<String>,
107    /// Billing and rate-limit usage.
108    ///
109    /// Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems.
110    ///
111    /// Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in usage will not match one-to-one with the exact visible content of an API request or response.
112    ///
113    /// For example, output_tokens will be non-zero, even for an empty string response from Claude.
114    pub usage: CompletionUsage,
115}
116
117#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
118#[serde(untagged)]
119pub enum CompletionContent {
120    /// The single text content.
121    String(String),
122    Text {
123        r#type: String,
124        text: String,
125    },
126    ToolUse {
127        r#type: String,
128        id: String,
129        name: String,
130        input: serde_json::Value,
131    },
132}
133
134impl CompletionContent {
135    pub fn text(&self) -> String {
136        match self {
137            CompletionContent::String(text) => text.to_string(),
138            CompletionContent::Text {
139                r#type: _, text, ..
140            } => text.to_string(),
141            CompletionContent::ToolUse { .. } => "".to_string(),
142        }
143    }
144}
145
146/// Usage statistics for the completion request.
147#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
148pub struct CompletionUsage {
149    /// The number of input tokens which were used.
150    pub input_tokens: u32,
151    /// The number of output tokens which were used.
152    pub output_tokens: u32,
153}
154
155#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
156#[serde(rename_all = "snake_case")]
157pub enum StopReason {
158    /// The model reached a natural stopping point.
159    EndTurn,
160    /// We exceeded the requested max_tokens or the model's maximum.
161    MaxTokens,
162    /// One of your provided custom stop_sequences was generated.
163    StopSequence,
164    /// Claude wants to use an external tool.
165    ToolUse,
166}