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