Skip to main content

funera_core/chat/
message.rs

1use std::{fmt, sync::Arc};
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use serde_json::{Value as JsonValue, json};
6use uuid::Uuid;
7
8#[cfg(feature = "tool")]
9use crate::re_act::tool::ToolType;
10
11pub trait Message {
12    fn to_prompt_content(&self) -> String;
13}
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub enum Role {
17    User,
18    Assistant,
19    System,
20    Tool,
21}
22impl fmt::Display for Role {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            Role::User => write!(f, "user"),
26            Role::Assistant => write!(f, "assistant"),
27            Role::System => write!(f, "system"),
28            Role::Tool => write!(f, "tool"),
29        }
30    }
31}
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub enum MsgVariant {
35    Text(TextMessage),
36    #[cfg(feature = "tool")]
37    ToolRequest(ToolRequestMessage),
38    #[cfg(feature = "tool")]
39    ToolResponse(ToolResponseMessage),
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct FuneraMessage {
44    id: Uuid,
45    role: Role,
46    timestamp: DateTime<Utc>,
47    msg_variant: MsgVariant,
48}
49
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51pub struct TextMessage {
52    pub text: Arc<str>,
53    pub reasoning_content: Option<Arc<str>>,
54}
55
56#[cfg(feature = "tool")]
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
58pub struct ToolRequestMessage {
59    pub tool_call_id: Arc<str>,
60    pub tool_type: ToolType,
61    pub function_name: Arc<str>,
62    pub function_args: JsonValue,
63    pub reasoning_content: Option<Arc<str>>,
64}
65
66#[cfg(feature = "tool")]
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub struct ToolResponseMessage {
69    pub tool_call_id: Arc<str>,
70    pub result: Arc<str>,
71}
72
73impl Message for TextMessage {
74    fn to_prompt_content(&self) -> String {
75        self.text.to_string()
76    }
77}
78
79#[cfg(feature = "tool")]
80impl Message for ToolRequestMessage {
81    fn to_prompt_content(&self) -> String {
82        format!(
83            "Tool Call -> id: {}, tool_type: {}, function_name: {}, function_args: {}",
84            self.tool_call_id, self.tool_type, self.function_name, self.function_args
85        )
86    }
87}
88
89#[cfg(feature = "tool")]
90impl Message for ToolResponseMessage {
91    fn to_prompt_content(&self) -> String {
92        format!(
93            "Tool Response -> tool_call_id: {}, result: {}",
94            self.tool_call_id, self.result
95        )
96    }
97}
98
99impl Message for FuneraMessage {
100    fn to_prompt_content(&self) -> String {
101        match &self.msg_variant {
102            MsgVariant::Text(text_msg) => text_msg.to_prompt_content(),
103            #[cfg(feature = "tool")]
104            MsgVariant::ToolRequest(tool_requet_msg) => tool_requet_msg.to_prompt_content(),
105            #[cfg(feature = "tool")]
106            MsgVariant::ToolResponse(tool_response_msg) => tool_response_msg.to_prompt_content(),
107        }
108    }
109}
110
111impl FuneraMessage {
112    pub fn new(role: Role, msg_variant: MsgVariant) -> Self {
113        Self {
114            id: Uuid::new_v4(),
115            role,
116            timestamp: Utc::now(),
117            msg_variant,
118        }
119    }
120
121    pub fn id(&self) -> Uuid {
122        self.id
123    }
124
125    pub fn role(&self) -> &Role {
126        &self.role
127    }
128
129    pub fn timestamp(&self) -> &DateTime<Utc> {
130        &self.timestamp
131    }
132
133    pub fn msg_variant(&self) -> &MsgVariant {
134        &self.msg_variant
135    }
136
137    pub fn format_json(&self) -> JsonValue {
138        match &self.msg_variant {
139            MsgVariant::Text(text_msg) => {
140                let mut obj = json!({
141                    "role": self.role.to_string(),
142                    "content": text_msg.to_prompt_content(),
143                });
144                if let Some(ref rc) = text_msg.reasoning_content {
145                    obj.as_object_mut()
146                        .unwrap()
147                        .insert("reasoning_content".into(), json!(rc.as_ref()));
148                }
149                obj
150            }
151            #[cfg(feature = "tool")]
152            MsgVariant::ToolRequest(tool_request_msg) => {
153                let mut obj = json!({
154                    "role": self.role.to_string(),
155                    "tool_calls": [
156                        {
157                            "id": tool_request_msg.tool_call_id.as_ref(),
158                            "type": tool_request_msg.tool_type.to_string(),
159                            "function": {
160                                "name": tool_request_msg.function_name.as_ref(),
161                                "arguments": serde_json::to_string(&tool_request_msg.function_args).unwrap_or_default(),
162                            }
163                        }
164                    ]
165                });
166                if let Some(ref rc) = tool_request_msg.reasoning_content {
167                    obj.as_object_mut()
168                        .unwrap()
169                        .insert("reasoning_content".into(), json!(rc.as_ref()));
170                }
171                obj
172            }
173            #[cfg(feature = "tool")]
174            MsgVariant::ToolResponse(tool_response_msg) => {
175                json!({
176                    "role": self.role.to_string(),
177                    "tool_call_id": tool_response_msg.tool_call_id.as_ref(),
178                    "content": tool_response_msg.to_prompt_content(),
179                })
180            }
181        }
182    }
183}