1use serde::Deserialize;
8use serde::Serialize;
9
10use crate::json::JsonValue;
11use crate::shared::ApprovalId;
12use crate::shared::FileData;
13use crate::shared::MediaType;
14use crate::shared::ProviderOptions;
15use crate::shared::ToolCallId;
16use crate::shared::ToolName;
17
18use super::content::CustomKind;
19
20pub type Prompt = Vec<PromptMessage>;
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25#[serde(tag = "role", rename_all = "lowercase")]
26#[non_exhaustive]
27pub enum PromptMessage {
28 System {
30 content: String,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
34 provider_options: Option<ProviderOptions>,
35 },
36 User {
38 content: Vec<UserPromptPart>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
42 provider_options: Option<ProviderOptions>,
43 },
44 Assistant {
46 content: Vec<AssistantPromptPart>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 provider_options: Option<ProviderOptions>,
51 },
52 Tool {
54 content: Vec<ToolPromptPart>,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
58 provider_options: Option<ProviderOptions>,
59 },
60}
61
62impl PromptMessage {
63 #[must_use]
65 pub fn system(content: impl Into<String>) -> Self {
66 Self::System {
67 content: content.into(),
68 provider_options: None,
69 }
70 }
71
72 #[must_use]
74 pub fn user_text(text: impl Into<String>) -> Self {
75 Self::User {
76 content: vec![UserPromptPart::Text(TextPart::new(text))],
77 provider_options: None,
78 }
79 }
80
81 #[must_use]
83 pub fn user(content: Vec<UserPromptPart>) -> Self {
84 Self::User {
85 content,
86 provider_options: None,
87 }
88 }
89
90 #[must_use]
92 pub fn assistant_text(text: impl Into<String>) -> Self {
93 Self::Assistant {
94 content: vec![AssistantPromptPart::Text(TextPart::new(text))],
95 provider_options: None,
96 }
97 }
98
99 #[must_use]
101 pub fn assistant(content: Vec<AssistantPromptPart>) -> Self {
102 Self::Assistant {
103 content,
104 provider_options: None,
105 }
106 }
107
108 #[must_use]
110 pub fn tool(content: Vec<ToolPromptPart>) -> Self {
111 Self::Tool {
112 content,
113 provider_options: None,
114 }
115 }
116
117 #[must_use]
119 pub fn role(&self) -> &'static str {
120 match self {
121 Self::System { .. } => "system",
122 Self::User { .. } => "user",
123 Self::Assistant { .. } => "assistant",
124 Self::Tool { .. } => "tool",
125 }
126 }
127
128 #[must_use]
130 pub fn provider_options(&self) -> Option<&ProviderOptions> {
131 match self {
132 Self::System {
133 provider_options, ..
134 }
135 | Self::User {
136 provider_options, ..
137 }
138 | Self::Assistant {
139 provider_options, ..
140 }
141 | Self::Tool {
142 provider_options, ..
143 } => provider_options.as_ref(),
144 }
145 }
146}
147
148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
150#[serde(tag = "type", rename_all = "kebab-case")]
151#[non_exhaustive]
152pub enum UserPromptPart {
153 Text(TextPart),
155 File(FilePart),
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161#[serde(tag = "type", rename_all = "kebab-case")]
162#[non_exhaustive]
163pub enum AssistantPromptPart {
164 Text(TextPart),
166 File(FilePart),
168 Reasoning(ReasoningPart),
170 ReasoningFile(ReasoningFilePart),
172 Custom(CustomPart),
174 ToolCall(ToolCallPart),
176 ToolResult(ToolResultPart),
178}
179
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182#[serde(tag = "type", rename_all = "kebab-case")]
183#[non_exhaustive]
184pub enum ToolPromptPart {
185 ToolResult(ToolResultPart),
187 ToolApprovalResponse(ToolApprovalResponsePart),
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct TextPart {
194 pub text: String,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub provider_options: Option<ProviderOptions>,
199}
200
201impl TextPart {
202 #[must_use]
204 pub fn new(text: impl Into<String>) -> Self {
205 Self {
206 text: text.into(),
207 provider_options: None,
208 }
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
214pub struct ReasoningPart {
215 pub text: String,
217 #[serde(default, skip_serializing_if = "Option::is_none")]
219 pub provider_options: Option<ProviderOptions>,
220}
221
222impl ReasoningPart {
223 #[must_use]
225 pub fn new(text: impl Into<String>) -> Self {
226 Self {
227 text: text.into(),
228 provider_options: None,
229 }
230 }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub struct ReasoningFilePart {
236 pub data: FileData,
238 pub media_type: MediaType,
240 #[serde(default, skip_serializing_if = "Option::is_none")]
242 pub provider_options: Option<ProviderOptions>,
243}
244
245#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
247pub struct CustomPart {
248 pub kind: CustomKind,
250 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub provider_options: Option<ProviderOptions>,
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
257pub struct FilePart {
258 pub data: FileData,
260 pub media_type: MediaType,
262 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub filename: Option<String>,
265 #[serde(default, skip_serializing_if = "Option::is_none")]
267 pub provider_options: Option<ProviderOptions>,
268}
269
270impl FilePart {
271 #[must_use]
273 pub fn new(data: impl Into<FileData>, media_type: impl Into<MediaType>) -> Self {
274 Self {
275 data: data.into(),
276 media_type: media_type.into(),
277 filename: None,
278 provider_options: None,
279 }
280 }
281
282 #[must_use]
284 pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
285 self.filename = Some(filename.into());
286 self
287 }
288}
289
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
292pub struct ToolCallPart {
293 pub tool_call_id: ToolCallId,
295 pub tool_name: ToolName,
297 pub input: JsonValue,
299 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
301 pub provider_executed: bool,
302 #[serde(default, skip_serializing_if = "Option::is_none")]
304 pub provider_options: Option<ProviderOptions>,
305}
306
307#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309pub struct ToolResultPart {
310 pub tool_call_id: ToolCallId,
312 pub tool_name: ToolName,
314 pub output: ToolResultOutput,
316 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub provider_options: Option<ProviderOptions>,
319}
320
321#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
323pub struct ToolApprovalResponsePart {
324 pub approval_id: ApprovalId,
326 pub approved: bool,
328 #[serde(default, skip_serializing_if = "Option::is_none")]
330 pub reason: Option<String>,
331 #[serde(default, skip_serializing_if = "Option::is_none")]
333 pub provider_options: Option<ProviderOptions>,
334}
335
336#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
338#[serde(tag = "type", rename_all = "kebab-case")]
339#[non_exhaustive]
340pub enum ToolResultOutput {
341 Text {
343 value: String,
345 #[serde(default, skip_serializing_if = "Option::is_none")]
347 provider_options: Option<ProviderOptions>,
348 },
349 Json {
351 value: JsonValue,
353 #[serde(default, skip_serializing_if = "Option::is_none")]
355 provider_options: Option<ProviderOptions>,
356 },
357 ExecutionDenied {
359 #[serde(default, skip_serializing_if = "Option::is_none")]
361 reason: Option<String>,
362 #[serde(default, skip_serializing_if = "Option::is_none")]
364 provider_options: Option<ProviderOptions>,
365 },
366 ErrorText {
368 value: String,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
372 provider_options: Option<ProviderOptions>,
373 },
374 ErrorJson {
376 value: JsonValue,
378 #[serde(default, skip_serializing_if = "Option::is_none")]
380 provider_options: Option<ProviderOptions>,
381 },
382 Content {
384 value: Vec<ToolResultContentPart>,
386 },
387}
388
389impl ToolResultOutput {
390 #[must_use]
392 pub fn text(value: impl Into<String>) -> Self {
393 Self::Text {
394 value: value.into(),
395 provider_options: None,
396 }
397 }
398
399 #[must_use]
401 pub fn json(value: JsonValue) -> Self {
402 Self::Json {
403 value,
404 provider_options: None,
405 }
406 }
407
408 #[must_use]
410 pub fn error_text(value: impl Into<String>) -> Self {
411 Self::ErrorText {
412 value: value.into(),
413 provider_options: None,
414 }
415 }
416
417 #[must_use]
419 pub fn error_json(value: JsonValue) -> Self {
420 Self::ErrorJson {
421 value,
422 provider_options: None,
423 }
424 }
425
426 #[must_use]
428 pub fn execution_denied(reason: Option<String>) -> Self {
429 Self::ExecutionDenied {
430 reason,
431 provider_options: None,
432 }
433 }
434
435 #[must_use]
437 pub fn is_error(&self) -> bool {
438 matches!(self, Self::ErrorText { .. } | Self::ErrorJson { .. })
439 }
440}
441
442#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
444#[serde(tag = "type", rename_all = "kebab-case")]
445#[non_exhaustive]
446pub enum ToolResultContentPart {
447 Text {
449 text: String,
451 #[serde(default, skip_serializing_if = "Option::is_none")]
453 provider_options: Option<ProviderOptions>,
454 },
455 File {
457 data: FileData,
459 media_type: MediaType,
461 #[serde(default, skip_serializing_if = "Option::is_none")]
463 filename: Option<String>,
464 #[serde(default, skip_serializing_if = "Option::is_none")]
466 provider_options: Option<ProviderOptions>,
467 },
468 Custom {
470 #[serde(default, skip_serializing_if = "Option::is_none")]
472 provider_options: Option<ProviderOptions>,
473 },
474}