systemprompt_models/ai/tools/
tool_call.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value as JsonValue;
4use std::collections::HashMap;
5use systemprompt_identifiers::{AiToolCallId, McpServerId};
6use systemprompt_traits::parse_database_datetime;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ToolCall {
10 pub ai_tool_call_id: AiToolCallId,
11 pub name: String,
12 pub arguments: JsonValue,
13}
14
15pub use rmcp::model::CallToolResult;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ToolExecution {
19 pub id: String,
20 pub request_id: String,
21 pub sequence: i32,
22 pub tool_name: String,
23 pub service_id: McpServerId,
24 pub input: JsonValue,
25 pub output: Option<JsonValue>,
26 pub status: String,
27 pub execution_time_ms: Option<i32>,
28 pub error_message: Option<String>,
29 pub created_at: DateTime<Utc>,
30}
31
32impl ToolExecution {
33 pub fn from_json_row(row: &HashMap<String, JsonValue>) -> anyhow::Result<Self> {
34 let id = row
35 .get("id")
36 .and_then(|v| v.as_str())
37 .ok_or_else(|| anyhow::anyhow!("Missing id"))?
38 .to_string();
39
40 let request_id = row
41 .get("request_id")
42 .and_then(|v| v.as_str())
43 .ok_or_else(|| anyhow::anyhow!("Missing request_id"))?
44 .to_string();
45
46 let sequence = row
47 .get("sequence")
48 .and_then(serde_json::Value::as_i64)
49 .ok_or_else(|| anyhow::anyhow!("Missing sequence"))
50 .and_then(|i| i32::try_from(i).map_err(|_| anyhow::anyhow!("Sequence out of range")))?;
51
52 let tool_name = row
53 .get("tool_name")
54 .and_then(|v| v.as_str())
55 .ok_or_else(|| anyhow::anyhow!("Missing tool_name"))?
56 .to_string();
57
58 let service_id = row
59 .get("service_id")
60 .and_then(|v| v.as_str())
61 .ok_or_else(|| anyhow::anyhow!("Missing service_id"))
62 .map(McpServerId::new)?;
63
64 let input = row
65 .get("input")
66 .and_then(|v| v.as_str())
67 .and_then(|s| {
68 serde_json::from_str(s)
69 .map_err(|e| {
70 tracing::warn!(error = %e, raw = %s, "Failed to parse tool input JSON");
71 e
72 })
73 .ok()
74 })
75 .unwrap_or(JsonValue::Null);
76
77 let output = row.get("output").and_then(|v| v.as_str()).and_then(|s| {
78 serde_json::from_str(s)
79 .map_err(|e| {
80 tracing::warn!(error = %e, raw = %s, "Failed to parse tool output JSON");
81 e
82 })
83 .ok()
84 });
85
86 let status = row
87 .get("status")
88 .and_then(|v| v.as_str())
89 .ok_or_else(|| anyhow::anyhow!("Missing status"))?
90 .to_string();
91
92 let execution_time_ms = row
93 .get("execution_time_ms")
94 .and_then(serde_json::Value::as_i64)
95 .and_then(|i| i32::try_from(i).ok());
96
97 let error_message = row
98 .get("error_message")
99 .and_then(|v| v.as_str())
100 .map(String::from);
101
102 let created_at = row
103 .get("created_at")
104 .and_then(parse_database_datetime)
105 .ok_or_else(|| anyhow::anyhow!("Missing or invalid created_at"))?;
106
107 Ok(Self {
108 id,
109 request_id,
110 sequence,
111 tool_name,
112 service_id,
113 input,
114 output,
115 status,
116 execution_time_ms,
117 error_message,
118 created_at,
119 })
120 }
121}