1use crate::tool::ToolResult;
4
5pub type SessionId = String;
7
8pub fn new_session_id() -> SessionId {
10 uuid::Uuid::new_v4().to_string()
11}
12
13#[derive(Debug, Clone)]
15pub struct MediaAttachment {
16 pub content_type: String,
18 pub url: String,
20 pub filename: Option<String>,
22 pub size: Option<u64>,
24 pub width: Option<u32>,
26 pub height: Option<u32>,
28}
29
30impl MediaAttachment {
31 pub fn is_image(&self) -> bool {
33 self.content_type.starts_with("image/")
34 }
35
36 pub fn describe(&self) -> String {
38 let filename = self.filename.as_deref().unwrap_or("unknown");
39 let type_desc = if self.is_image() { "图片" } else { "文件" };
40 let size_str = self
41 .size
42 .map(|s| format!(" ({:.1}KB)", s as f64 / 1024.0))
43 .unwrap_or_default();
44 format!("[用户发送了{}: {}{}]", type_desc, filename, size_str)
45 }
46}
47
48#[derive(Debug)]
50pub enum AgentEvent {
51 TextDelta(String),
53
54 ToolCallRequested {
56 tool_call_id: String,
57 name: String,
58 arguments: String,
59 },
60
61 ToolCallResult {
63 tool_call_id: String,
64 result: ToolResult,
65 },
66
67 TurnComplete,
69
70 Error(crate::error::AgentError),
72
73 SkillTriggered { name: String, description: String },
75}
76
77#[derive(Debug)]
79pub enum FrontendMessage {
80 UserInput {
82 text: String,
83 attachments: Vec<MediaAttachment>,
84 },
85
86 Cancel,
88
89 ConfirmationResponse {
91 tool_call_id: String,
92 approved: bool,
93 },
94}
95
96impl From<String> for FrontendMessage {
98 fn from(text: String) -> Self {
99 Self::UserInput {
100 text,
101 attachments: vec![],
102 }
103 }
104}
105
106impl From<&str> for FrontendMessage {
108 fn from(text: &str) -> Self {
109 Self::UserInput {
110 text: text.to_string(),
111 attachments: vec![],
112 }
113 }
114}