1use compact_str::CompactString;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct SendResponse {
9 pub agent: CompactString,
11 pub content: String,
13 pub session: u64,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ToolCallInfo {
20 pub name: CompactString,
22 pub arguments: String,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum StreamEvent {
29 Start {
31 agent: CompactString,
33 session: u64,
35 },
36 Chunk {
38 content: String,
40 },
41 Thinking {
43 content: String,
45 },
46 ToolStart {
48 calls: Vec<ToolCallInfo>,
50 },
51 ToolResult {
53 call_id: CompactString,
55 output: String,
57 },
58 ToolsComplete,
60 End {
62 agent: CompactString,
64 },
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(rename_all = "snake_case")]
70pub enum DownloadKind {
71 Model,
73 Hub,
75 Embeddings,
77 Skill,
79}
80
81impl std::fmt::Display for DownloadKind {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 Self::Model => write!(f, "model"),
85 Self::Hub => write!(f, "hub"),
86 Self::Embeddings => write!(f, "embeddings"),
87 Self::Skill => write!(f, "skill"),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum DownloadEvent {
95 Created {
97 id: u64,
99 kind: DownloadKind,
101 label: String,
103 },
104 Progress {
106 id: u64,
108 bytes: u64,
110 total_bytes: u64,
112 },
113 Step {
115 id: u64,
117 message: String,
119 },
120 Completed {
122 id: u64,
124 },
125 Failed {
127 id: u64,
129 error: String,
131 },
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct DownloadInfo {
137 pub id: u64,
139 pub kind: DownloadKind,
141 pub label: String,
143 pub status: String,
145 pub bytes_downloaded: u64,
147 pub total_bytes: u64,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub error: Option<String>,
152 pub alive_secs: u64,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct SessionInfo {
159 pub id: u64,
161 pub agent: CompactString,
163 pub created_by: CompactString,
165 pub message_count: usize,
167 pub alive_secs: u64,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct TaskInfo {
174 pub id: u64,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub parent_id: Option<u64>,
179 pub agent: CompactString,
181 pub status: String,
183 pub description: String,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub result: Option<String>,
188 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub error: Option<String>,
191 pub created_by: CompactString,
193 pub prompt_tokens: u64,
195 pub completion_tokens: u64,
197 pub alive_secs: u64,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub blocked_on: Option<String>,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206pub enum TaskEvent {
207 Created {
209 task: TaskInfo,
211 },
212 StatusChanged {
214 task_id: u64,
216 status: String,
218 #[serde(default, skip_serializing_if = "Option::is_none")]
220 blocked_on: Option<String>,
221 },
222 Completed {
224 task_id: u64,
226 status: String,
228 #[serde(default, skip_serializing_if = "Option::is_none")]
230 result: Option<String>,
231 #[serde(default, skip_serializing_if = "Option::is_none")]
233 error: Option<String>,
234 },
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct EntityInfo {
240 pub entity_type: CompactString,
242 pub key: CompactString,
244 pub value: String,
246 pub created_at: u64,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct RelationInfo {
253 pub source_id: CompactString,
255 pub relation: CompactString,
257 pub target_id: CompactString,
259 pub created_at: u64,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct JournalInfo {
266 pub summary: String,
268 pub agent: CompactString,
270 pub created_at: u64,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276#[serde(rename_all = "snake_case")]
277pub enum MemoryResult {
278 Entities(Vec<EntityInfo>),
280 Relations(Vec<RelationInfo>),
282 Journals(Vec<JournalInfo>),
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288#[serde(tag = "type", rename_all = "snake_case")]
289pub enum ServerMessage {
290 Response(SendResponse),
292 Stream(StreamEvent),
294 Download(DownloadEvent),
296 Error {
298 code: u16,
300 message: String,
302 },
303 Pong,
305 Sessions(Vec<SessionInfo>),
307 Downloads(Vec<DownloadInfo>),
309 Tasks(Vec<TaskInfo>),
311 Task(TaskEvent),
313 Evaluation {
315 respond: bool,
317 },
318 Config {
320 config: String,
322 },
323 Memory(MemoryResult),
325}
326
327impl From<SendResponse> for ServerMessage {
328 fn from(r: SendResponse) -> Self {
329 Self::Response(r)
330 }
331}
332
333impl From<StreamEvent> for ServerMessage {
334 fn from(e: StreamEvent) -> Self {
335 Self::Stream(e)
336 }
337}
338
339impl From<DownloadEvent> for ServerMessage {
340 fn from(e: DownloadEvent) -> Self {
341 Self::Download(e)
342 }
343}
344
345impl From<TaskEvent> for ServerMessage {
346 fn from(e: TaskEvent) -> Self {
347 Self::Task(e)
348 }
349}
350
351fn error_or_unexpected(msg: ServerMessage) -> anyhow::Error {
352 match msg {
353 ServerMessage::Error { code, message } => {
354 anyhow::anyhow!("server error ({code}): {message}")
355 }
356 other => anyhow::anyhow!("unexpected response: {other:?}"),
357 }
358}
359
360impl TryFrom<ServerMessage> for SendResponse {
361 type Error = anyhow::Error;
362 fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
363 match msg {
364 ServerMessage::Response(r) => Ok(r),
365 other => Err(error_or_unexpected(other)),
366 }
367 }
368}
369
370impl TryFrom<ServerMessage> for StreamEvent {
371 type Error = anyhow::Error;
372 fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
373 match msg {
374 ServerMessage::Stream(e) => Ok(e),
375 other => Err(error_or_unexpected(other)),
376 }
377 }
378}
379
380impl TryFrom<ServerMessage> for DownloadEvent {
381 type Error = anyhow::Error;
382 fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
383 match msg {
384 ServerMessage::Download(e) => Ok(e),
385 other => Err(error_or_unexpected(other)),
386 }
387 }
388}
389
390impl TryFrom<ServerMessage> for TaskEvent {
391 type Error = anyhow::Error;
392 fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
393 match msg {
394 ServerMessage::Task(e) => Ok(e),
395 other => Err(error_or_unexpected(other)),
396 }
397 }
398}
399
400impl TryFrom<ServerMessage> for MemoryResult {
401 type Error = anyhow::Error;
402 fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
403 match msg {
404 ServerMessage::Memory(r) => Ok(r),
405 other => Err(error_or_unexpected(other)),
406 }
407 }
408}