agent_io/llm/types/
response.rs1use serde::{Deserialize, Serialize};
4
5use super::tool::ToolCall;
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct Usage {
10 pub prompt_tokens: u64,
11 pub completion_tokens: u64,
12 pub total_tokens: u64,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub prompt_cached_tokens: Option<u64>,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub prompt_cache_creation_tokens: Option<u64>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub prompt_image_tokens: Option<u64>,
19}
20
21impl Usage {
22 pub fn new(prompt_tokens: u64, completion_tokens: u64) -> Self {
23 Self {
24 prompt_tokens,
25 completion_tokens,
26 total_tokens: prompt_tokens + completion_tokens,
27 ..Default::default()
28 }
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34#[serde(rename_all = "snake_case")]
35pub enum StopReason {
36 EndTurn,
37 StopSequence,
38 ToolUse,
39 MaxTokens,
40 #[serde(other)]
41 Unknown,
42}
43
44#[derive(Debug, Clone)]
46pub struct ChatCompletion {
47 pub content: Option<String>,
49 pub thinking: Option<String>,
51 pub redacted_thinking: Option<String>,
53 pub tool_calls: Vec<ToolCall>,
55 pub usage: Option<Usage>,
57 pub stop_reason: Option<StopReason>,
59}
60
61impl ChatCompletion {
62 pub fn text(content: impl Into<String>) -> Self {
63 Self {
64 content: Some(content.into()),
65 thinking: None,
66 redacted_thinking: None,
67 tool_calls: Vec::new(),
68 usage: None,
69 stop_reason: None,
70 }
71 }
72
73 pub fn with_tool_calls(tool_calls: Vec<ToolCall>) -> Self {
74 Self {
75 content: None,
76 thinking: None,
77 redacted_thinking: None,
78 tool_calls,
79 usage: None,
80 stop_reason: Some(StopReason::ToolUse),
81 }
82 }
83
84 pub fn has_tool_calls(&self) -> bool {
85 !self.tool_calls.is_empty()
86 }
87
88 pub fn has_content(&self) -> bool {
89 self.content.is_some() && self.content.as_ref().is_some_and(|c| !c.is_empty())
90 }
91}