1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ChatMessage {
13 pub role: String, #[serde(default)]
15 pub content: String,
16 #[serde(skip_serializing_if = "Option::is_none", default)]
17 pub tool_calls: Option<Vec<ToolCall>>,
18 #[serde(skip_serializing_if = "Option::is_none", default)]
19 pub tool_call_id: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none", default)]
21 pub reasoning_content: Option<String>,
22}
23
24impl ChatMessage {
25 pub fn system(content: impl Into<String>) -> Self {
26 Self::simple("system", content)
27 }
28 pub fn user(content: impl Into<String>) -> Self {
29 Self::simple("user", content)
30 }
31 pub fn assistant(content: impl Into<String>) -> Self {
32 Self::simple("assistant", content)
33 }
34 pub fn simple(role: &str, content: impl Into<String>) -> Self {
35 ChatMessage {
36 role: role.to_string(),
37 content: content.into(),
38 tool_calls: None,
39 tool_call_id: None,
40 reasoning_content: None,
41 }
42 }
43 pub fn tool_result(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
45 ChatMessage {
46 role: "tool".to_string(),
47 content: content.into(),
48 tool_calls: None,
49 tool_call_id: Some(tool_call_id.into()),
50 reasoning_content: None,
51 }
52 }
53 pub fn assistant_with_tools(content: impl Into<String>, tool_calls: Vec<ToolCall>) -> Self {
55 ChatMessage {
56 role: "assistant".to_string(),
57 content: content.into(),
58 tool_calls: Some(tool_calls),
59 tool_call_id: None,
60 reasoning_content: None,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
67pub struct ToolCall {
68 pub id: String,
69 pub name: String,
70 pub arguments: String,
72}
73
74impl ToolCall {
75 pub fn parsed_args(&self) -> Value {
78 serde_json::from_str(&self.arguments).unwrap_or_else(|_| serde_json::json!({}))
79 }
80}
81
82#[derive(Debug, Clone, Default)]
84pub struct LlmResponse {
85 pub text: String,
86 pub tool_calls: Vec<ToolCall>,
87 pub reasoning_content: Option<String>,
88 pub finish_reason: Option<String>,
90}
91
92impl LlmResponse {
93 pub fn is_truncated(&self) -> bool {
94 matches!(
95 self.finish_reason.as_deref(),
96 Some("length") | Some("max_tokens") | Some("connection_closed")
97 )
98 }
99}