1use std::collections::BTreeMap;
7
8use serde::{Deserialize, Serialize};
9use serde_json::{Map, Value};
10
11use crate::format::FormatId;
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
15pub enum Role {
16 System,
17 Developer,
18 User,
19 Assistant,
20 Tool,
21}
22
23#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
25pub struct InstructionBlock {
26 pub role: Role,
27 pub content: Vec<ContentBlock>,
28}
29
30#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
32pub struct Message {
33 pub role: Role,
34 pub content: Vec<ContentBlock>,
35}
36
37impl Message {
38 pub fn text(role: Role, text: impl Into<String>) -> Self {
40 Self {
41 role,
42 content: vec![ContentBlock::Text { text: text.into() }],
43 }
44 }
45
46 pub fn text_content(&self, separator: &str) -> Option<String> {
48 let parts = self
49 .content
50 .iter()
51 .filter_map(|block| match block {
52 ContentBlock::Text { text } => Some(text.as_str()),
53 ContentBlock::Refusal { text } => Some(text.as_str()),
54 _ => None,
55 })
56 .collect::<Vec<_>>();
57 if parts.is_empty() {
58 None
59 } else {
60 Some(parts.join(separator))
61 }
62 }
63}
64
65#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
67pub enum ContentBlock {
68 Text {
69 text: String,
70 },
71 Reasoning {
72 text: String,
73 signature: Option<String>,
74 },
75 Image {
76 source: ImageSource,
77 },
78 Audio {
79 source: MediaSource,
80 },
81 Video {
82 source: MediaSource,
83 },
84 File {
85 source: FileSource,
86 },
87 ToolCall(ToolCall),
88 ToolResult(ToolResult),
89 Refusal {
90 text: String,
91 },
92 Unknown {
93 provider: FormatId,
94 raw: Value,
95 },
96}
97
98#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
100pub enum ImageSource {
101 Url {
102 url: String,
103 detail: Option<String>,
104 },
105 Base64 {
106 media_type: Option<String>,
107 data: String,
108 },
109 Raw(Value),
110}
111
112#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
114pub enum FileSource {
115 FileId(String),
116 FileData {
117 data: String,
118 filename: Option<String>,
119 },
120 Raw(Value),
121}
122
123#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
125pub enum MediaSource {
126 Url {
127 url: String,
128 media_type: Option<String>,
129 },
130 Base64 {
131 media_type: Option<String>,
132 data: String,
133 },
134 Raw(Value),
135}
136
137#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
139pub struct ToolCall {
140 pub id: String,
141 pub name: String,
142 pub arguments: Value,
143}
144
145#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
147pub struct ToolResult {
148 pub tool_call_id: String,
149 pub content: Vec<ContentBlock>,
150 pub is_error: Option<bool>,
151}
152
153#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
155pub struct ToolDefinition {
156 pub name: String,
157 pub description: Option<String>,
158 pub parameters: Value,
159 pub strict: Option<bool>,
160}
161
162#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
164pub enum ToolChoice {
165 Auto,
166 Required,
167 None,
168 Tool { name: String },
169 Raw(Value),
170}
171
172#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
174pub struct SamplingParams {
175 pub temperature: Option<f64>,
176 pub top_p: Option<f64>,
177 pub top_k: Option<i64>,
178}
179
180#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
182pub struct OutputParams {
183 pub max_output_tokens: Option<u64>,
184 pub response_format: Option<Value>,
185}
186
187#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
189pub struct ReasoningParams {
190 pub effort: Option<String>,
191 pub raw: Option<Value>,
192}
193
194#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
196pub struct ProviderExtensions {
197 pub fields: Map<String, Value>,
198}
199
200#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
202pub struct PreservationMetadata {
203 pub requests: BTreeMap<FormatId, Value>,
204 pub responses: BTreeMap<FormatId, Value>,
205}
206
207#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
209pub struct LlmRequest {
210 pub model: Option<String>,
211 pub instructions: Vec<InstructionBlock>,
212 pub messages: Vec<Message>,
213 pub tools: Vec<ToolDefinition>,
214 pub tool_choice: Option<ToolChoice>,
215 pub sampling: SamplingParams,
216 pub output: OutputParams,
217 pub reasoning: ReasoningParams,
218 pub stream: bool,
219 pub extensions: ProviderExtensions,
220 pub preservation: PreservationMetadata,
221}
222
223#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
225pub struct Usage {
226 pub input_tokens: Option<u64>,
227 pub output_tokens: Option<u64>,
228 pub total_tokens: Option<u64>,
229 pub reasoning_tokens: Option<u64>,
230}
231
232#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
234pub enum LlmStreamEvent {
235 MessageStart {
236 id: Option<String>,
237 model: Option<String>,
238 },
239 TextDelta {
240 index: usize,
241 text: String,
242 },
243 ReasoningDelta {
244 index: usize,
245 text: String,
246 },
247 ToolCallDelta {
248 index: usize,
249 id: Option<String>,
250 name: Option<String>,
251 arguments_delta: Option<String>,
252 },
253 Usage(Usage),
254 MessageStop {
255 reason: Option<String>,
256 },
257 Error {
258 message: String,
259 },
260}
261
262#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
264pub enum StopReason {
265 EndTurn,
266 MaxTokens,
267 ToolUse,
268 ContentFilter,
269 Error,
270 Unknown,
271}
272
273#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
275pub struct ResponseOutput {
276 pub role: Role,
277 pub content: Vec<ContentBlock>,
278 pub stop_reason: Option<StopReason>,
279}
280
281#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
283pub struct LlmResponse {
284 pub id: Option<String>,
285 pub model: Option<String>,
286 pub outputs: Vec<ResponseOutput>,
287 pub usage: Usage,
288 pub extensions: ProviderExtensions,
289 pub preservation: PreservationMetadata,
290}
291
292impl LlmResponse {
293 pub fn first_output(&self) -> Option<&ResponseOutput> {
295 self.outputs.first()
296 }
297}