Skip to main content

conversation_api/
content.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5#[serde(tag = "kind", rename_all = "snake_case")]
6pub enum MediaSource {
7    Artifact { uri: String },
8}
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11#[serde(tag = "type", rename_all = "snake_case")]
12pub enum MessagePart {
13    Text {
14        text: String,
15    },
16    Image {
17        source: MediaSource,
18        #[serde(default, skip_serializing_if = "Option::is_none")]
19        mime_type: Option<String>,
20        #[serde(default, skip_serializing_if = "Option::is_none")]
21        alt_text: Option<String>,
22        #[serde(default, skip_serializing_if = "Option::is_none")]
23        detail: Option<String>,
24    },
25    Audio {
26        source: MediaSource,
27        #[serde(default, skip_serializing_if = "Option::is_none")]
28        mime_type: Option<String>,
29        #[serde(default, skip_serializing_if = "Option::is_none")]
30        transcript: Option<String>,
31    },
32    File {
33        source: MediaSource,
34        #[serde(default, skip_serializing_if = "Option::is_none")]
35        mime_type: Option<String>,
36        #[serde(default, skip_serializing_if = "Option::is_none")]
37        name: Option<String>,
38    },
39    Json {
40        value: Value,
41    },
42}
43
44#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
45#[serde(rename_all = "camelCase")]
46pub struct MessageContent {
47    #[serde(default)]
48    pub parts: Vec<MessagePart>,
49}
50
51impl MessageContent {
52    pub fn text(text: impl Into<String>) -> Self {
53        Self {
54            parts: vec![MessagePart::Text { text: text.into() }],
55        }
56    }
57
58    pub fn plain_text(&self) -> String {
59        self.parts
60            .iter()
61            .filter_map(|part| match part {
62                MessagePart::Text { text } => Some(text.as_str()),
63                _ => None,
64            })
65            .collect::<Vec<_>>()
66            .join("\n")
67    }
68
69    pub fn has_non_text_parts(&self) -> bool {
70        self.parts
71            .iter()
72            .any(|part| !matches!(part, MessagePart::Text { .. }))
73    }
74}
75
76#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(rename_all = "snake_case")]
78pub enum ConversationMessageRole {
79    User,
80    Assistant,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
84#[serde(rename_all = "camelCase")]
85pub struct ConversationMessage {
86    pub message_id: String,
87    pub request_id: String,
88    pub role: ConversationMessageRole,
89    pub content: MessageContent,
90    pub created_at: String,
91}