sayr_engine/
message.rs

1use serde::{Deserialize, Serialize};
2
3/// A non-textual payload that can accompany a message.
4#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
5pub struct Attachment {
6    pub kind: AttachmentKind,
7    pub uri: String,
8    #[serde(skip_serializing_if = "Option::is_none")]
9    pub description: Option<String>,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub media_type: Option<String>,
12}
13
14/// Types of attachments supported by the runtime.
15#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
16pub enum AttachmentKind {
17    File,
18    Image,
19    Audio,
20    Video,
21    Other,
22}
23
24/// Chat roles supported by the runtime.
25#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
26pub enum Role {
27    System,
28    User,
29    Assistant,
30    Tool,
31}
32
33/// A tool call generated by the language model.
34#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
35pub struct ToolCall {
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub id: Option<String>,
38    pub name: String,
39    pub arguments: serde_json::Value,
40}
41
42/// A tool result message captured in the transcript.
43#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
44pub struct ToolResult {
45    pub name: String,
46    pub output: serde_json::Value,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub tool_call_id: Option<String>,
49}
50
51/// A single message in the conversation transcript.
52#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
53pub struct Message {
54    pub role: Role,
55    pub content: String,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub tool_call: Option<ToolCall>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub tool_result: Option<ToolResult>,
60    #[serde(skip_serializing_if = "Vec::is_empty", default)]
61    pub attachments: Vec<Attachment>,
62}
63
64impl Message {
65    pub fn system(content: impl Into<String>) -> Self {
66        Self {
67            role: Role::System,
68            content: content.into(),
69            tool_call: None,
70            tool_result: None,
71            attachments: Vec::new(),
72        }
73    }
74
75    pub fn user(content: impl Into<String>) -> Self {
76        Self {
77            role: Role::User,
78            content: content.into(),
79            tool_call: None,
80            tool_result: None,
81            attachments: Vec::new(),
82        }
83    }
84
85    pub fn assistant(content: impl Into<String>) -> Self {
86        Self {
87            role: Role::Assistant,
88            content: content.into(),
89            tool_call: None,
90            tool_result: None,
91            attachments: Vec::new(),
92        }
93    }
94
95    pub fn tool(name: impl Into<String>, output: serde_json::Value) -> Self {
96        let name = name.into();
97
98        Self {
99            role: Role::Tool,
100            content: format!("Result from `{}`", name),
101            tool_call: None,
102            tool_result: Some(ToolResult {
103                name: name.clone(),
104                output,
105                tool_call_id: None,
106            }),
107            attachments: Vec::new(),
108        }
109    }
110
111    pub fn tool_with_call(
112        name: impl Into<String>,
113        output: serde_json::Value,
114        tool_call_id: Option<String>,
115    ) -> Self {
116        let name = name.into();
117
118        Self {
119            role: Role::Tool,
120            content: format!("Result from `{}`", name),
121            tool_call: None,
122            tool_result: Some(ToolResult {
123                name: name.clone(),
124                output,
125                tool_call_id,
126            }),
127            attachments: Vec::new(),
128        }
129    }
130}