Skip to main content

alchemy_llm/types/
message.rs

1use serde::{Deserialize, Deserializer, Serialize};
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use super::content::{Content, ImageContent, TextContent};
5use super::tool_call_id::ToolCallId;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(tag = "role")]
9pub enum Message {
10    User(UserMessage),
11    Assistant(AssistantMessage),
12    ToolResult(ToolResultMessage),
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct UserMessage {
17    #[serde(deserialize_with = "deserialize_user_content")]
18    pub content: UserContent,
19    #[serde(default = "current_timestamp")]
20    pub timestamp: i64,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(untagged)]
25pub enum UserContent {
26    Text(String),
27    Multi(Vec<UserContentBlock>),
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(untagged)]
32pub enum UserContentBlock {
33    Text(TextContent),
34    Image(ImageContent),
35}
36
37fn deserialize_user_content<'de, D>(deserializer: D) -> Result<UserContent, D::Error>
38where
39    D: Deserializer<'de>,
40{
41    #[derive(Deserialize)]
42    #[serde(untagged)]
43    enum ContentHelper {
44        String(String),
45        Array(Vec<UserContentBlock>),
46    }
47
48    match ContentHelper::deserialize(deserializer)? {
49        ContentHelper::String(s) => Ok(UserContent::Text(s)),
50        ContentHelper::Array(arr) => Ok(UserContent::Multi(arr)),
51    }
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct AssistantMessage {
56    pub content: Vec<Content>,
57    pub api: super::api::Api,
58    pub provider: super::api::Provider,
59    pub model: String,
60    pub usage: Usage,
61    pub stop_reason: StopReason,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub error_message: Option<String>,
64    #[serde(default = "current_timestamp")]
65    pub timestamp: i64,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct ToolResultMessage {
70    pub tool_call_id: ToolCallId,
71    pub tool_name: String,
72    pub content: Vec<ToolResultContent>,
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub details: Option<serde_json::Value>,
75    pub is_error: bool,
76    #[serde(default = "current_timestamp")]
77    pub timestamp: i64,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81#[serde(untagged)]
82pub enum ToolResultContent {
83    Text(TextContent),
84    Image(ImageContent),
85}
86
87#[derive(Debug, Clone, Default, Serialize, Deserialize)]
88pub struct Context {
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub system_prompt: Option<String>,
91    pub messages: Vec<Message>,
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub tools: Option<Vec<Tool>>,
94}
95
96use super::tool::Tool;
97use super::usage::{StopReason, Usage};
98
99fn current_timestamp() -> i64 {
100    match SystemTime::now().duration_since(UNIX_EPOCH) {
101        Ok(duration) => duration.as_millis() as i64,
102        Err(error) => -(error.duration().as_millis() as i64),
103    }
104}