gglib_core/domain/
chat.rs1use serde::{Deserialize, Serialize};
10
11use super::agent::messages::AgentMessage;
12use super::agent::messages::AssistantContent;
13use super::agent::tool_types::ToolCall;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Conversation {
18 pub id: i64,
19 pub title: String,
20 pub model_id: Option<i64>,
21 pub system_prompt: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub settings: Option<ConversationSettings>,
25 pub created_at: String,
26 pub updated_at: String,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Message {
32 pub id: i64,
33 pub conversation_id: i64,
34 pub role: MessageRole,
35 pub content: String,
36 pub created_at: String,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub metadata: Option<serde_json::Value>,
40}
41
42impl Message {
43 #[must_use]
48 pub fn to_agent_message(&self) -> AgentMessage {
49 match self.role {
50 MessageRole::System => AgentMessage::System {
51 content: self.content.clone(),
52 },
53 MessageRole::User => AgentMessage::User {
54 content: self.content.clone(),
55 },
56 MessageRole::Assistant => {
57 let tool_calls: Vec<ToolCall> = self
58 .metadata
59 .as_ref()
60 .and_then(|m| m.get("tool_calls"))
61 .and_then(|v| serde_json::from_value(v.clone()).ok())
62 .unwrap_or_default();
63 AgentMessage::Assistant {
64 content: AssistantContent {
65 text: if self.content.is_empty() {
66 None
67 } else {
68 Some(self.content.clone())
69 },
70 tool_calls,
71 },
72 }
73 }
74 MessageRole::Tool => {
75 let tool_call_id = self
76 .metadata
77 .as_ref()
78 .and_then(|m| m.get("tool_call_id"))
79 .and_then(|v| v.as_str())
80 .unwrap_or("")
81 .to_string();
82 AgentMessage::Tool {
83 tool_call_id,
84 content: self.content.clone(),
85 }
86 }
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "lowercase")]
94pub enum MessageRole {
95 System,
96 User,
97 Assistant,
98 Tool,
99}
100
101impl MessageRole {
102 #[must_use]
104 pub fn parse(s: &str) -> Option<Self> {
105 match s {
106 "system" => Some(Self::System),
107 "user" => Some(Self::User),
108 "assistant" => Some(Self::Assistant),
109 "tool" => Some(Self::Tool),
110 _ => None,
111 }
112 }
113
114 #[must_use]
116 pub const fn as_str(&self) -> &'static str {
117 match self {
118 Self::System => "system",
119 Self::User => "user",
120 Self::Assistant => "assistant",
121 Self::Tool => "tool",
122 }
123 }
124}
125
126impl std::fmt::Display for MessageRole {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 write!(f, "{}", self.as_str())
129 }
130}
131
132#[derive(Debug, Clone)]
134pub struct NewConversation {
135 pub title: String,
136 pub model_id: Option<i64>,
137 pub system_prompt: Option<String>,
138 pub settings: Option<ConversationSettings>,
140}
141
142#[derive(Debug, Clone)]
144pub struct NewMessage {
145 pub conversation_id: i64,
146 pub role: MessageRole,
147 pub content: String,
148 pub metadata: Option<serde_json::Value>,
150}
151
152#[derive(Debug, Clone, Default)]
154pub struct ConversationUpdate {
155 pub title: Option<String>,
156 pub system_prompt: Option<Option<String>>,
158 pub settings: Option<Option<ConversationSettings>>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
167pub struct ConversationSettings {
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub model_name: Option<String>,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub temperature: Option<f32>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub top_p: Option<f32>,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub top_k: Option<i32>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub max_tokens: Option<u32>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub repeat_penalty: Option<f32>,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub ctx_size: Option<String>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub mlock: Option<bool>,
192 #[serde(default, skip_serializing_if = "Vec::is_empty")]
194 pub tools: Vec<String>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub tool_timeout_ms: Option<u64>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub max_parallel: Option<usize>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub max_iterations: Option<usize>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub no_tools: Option<bool>,
207}