1use super::tools::{CallToolResult, ToolCall};
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct AiResponse {
12 pub request_id: Uuid,
13 pub content: String,
14 pub provider: String,
15 pub model: String,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub tokens_used: Option<u32>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub input_tokens: Option<u32>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub output_tokens: Option<u32>,
22 pub latency_ms: u64,
23 #[serde(default, skip_serializing_if = "Vec::is_empty")]
24 pub tool_calls: Vec<ToolCall>,
25 #[serde(default, skip_serializing_if = "Vec::is_empty")]
26 pub tool_results: Vec<CallToolResult>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub finish_reason: Option<String>,
29 pub cache_hit: bool,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub cache_read_tokens: Option<u32>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub cache_creation_tokens: Option<u32>,
34 pub is_streaming: bool,
35}
36
37impl Default for AiResponse {
38 fn default() -> Self {
39 Self {
40 request_id: Uuid::nil(),
41 content: String::new(),
42 provider: String::new(),
43 model: String::new(),
44 tokens_used: None,
45 input_tokens: None,
46 output_tokens: None,
47 latency_ms: 0,
48 tool_calls: Vec::new(),
49 tool_results: Vec::new(),
50 finish_reason: None,
51 cache_hit: false,
52 cache_read_tokens: None,
53 cache_creation_tokens: None,
54 is_streaming: false,
55 }
56 }
57}
58
59impl AiResponse {
60 pub fn new(request_id: Uuid, content: String, provider: String, model: String) -> Self {
61 Self {
62 request_id,
63 content,
64 provider,
65 model,
66 ..Default::default()
67 }
68 }
69
70 pub const fn with_tokens(mut self, tokens_used: u32) -> Self {
71 self.tokens_used = Some(tokens_used);
72 self
73 }
74
75 pub const fn with_latency(mut self, latency_ms: u64) -> Self {
76 self.latency_ms = latency_ms;
77 self
78 }
79
80 pub const fn with_streaming(mut self, is_streaming: bool) -> Self {
81 self.is_streaming = is_streaming;
82 self
83 }
84
85 pub fn with_tool_calls(mut self, tool_calls: Vec<ToolCall>) -> Self {
86 self.tool_calls = tool_calls;
87 self
88 }
89
90 pub fn with_tool_results(mut self, tool_results: Vec<CallToolResult>) -> Self {
91 self.tool_results = tool_results;
92 self
93 }
94
95 pub const fn has_tool_calls(&self) -> bool {
96 !self.tool_calls.is_empty()
97 }
98
99 pub const fn has_tool_results(&self) -> bool {
100 !self.tool_results.is_empty()
101 }
102}
103
104#[derive(Debug, Clone)]
105pub enum StreamChunk {
106 Text(String),
107 Usage {
108 input_tokens: Option<u32>,
109 output_tokens: Option<u32>,
110 tokens_used: Option<u32>,
111 cache_read_tokens: Option<u32>,
112 cache_creation_tokens: Option<u32>,
113 finish_reason: Option<String>,
114 },
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct WebSource {
119 pub title: String,
120 pub uri: String,
121 pub relevance: f32,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct UrlMetadata {
126 pub retrieved_url: String,
127 pub url_retrieval_status: String,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct SearchGroundedResponse {
132 pub content: String,
133 pub sources: Vec<WebSource>,
134 pub confidence_scores: Vec<f32>,
135 pub web_search_queries: Vec<String>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub url_context_metadata: Option<Vec<UrlMetadata>>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub tokens_used: Option<u32>,
140 pub latency_ms: u64,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub finish_reason: Option<String>,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub safety_ratings: Option<Vec<serde_json::Value>>,
145}