gitdataai/libs/agent/tool/call.rs
ZhenYi f7e087e066 fix(agent/service): retry jitter, tool executor ordering, curl SSRF, grep/JSON
- agent/client: full jitter backoff (random(0, base_ms)) instead of equal jitter
- agent/tool/executor: fix buffer_unordered ordering mismatch with
  HashMap-by-index approach for concurrent tool execution
- agent/chat: AiChunkType emit fixes, is_retryable_tool_error refinements,
  process_react uses request.max_tool_depth
- agent/chat/context: fix Function message sender_name field
- file_tools/curl: shared reqwest::Client via OnceLock, manual redirect
  following with per-hop SSRF validation, blocked sensitive headers
- file_tools/grep: fix case-insensitive glob matching, segment consumption
- file_tools/json: bracket notation support, remove .vscodeignore from JSONC
- git_tools: git_diff_stats resolve base/head independently,
  DiffFileOut old_file.path for Deleted, reflog offset_minutes
- git/repo: create_commit read parent tree into index, bare repo init
- project_tools/repos: branch/path validation, .git/ prefix check
- service/agent: tokent integration, billing, pr_summary, code_review fixes
2026-04-25 09:53:31 +08:00

111 lines
2.7 KiB
Rust

//! Tool call and result types.
use serde::{Deserialize, Serialize};
/// A single tool invocation requested by the AI model.
#[derive(Debug, Clone)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: String,
}
impl ToolCall {
pub fn arguments_json(&self) -> serde_json::Result<serde_json::Value> {
serde_json::from_str(&self.arguments)
}
pub fn parse_args<T: serde::de::DeserializeOwned>(&self) -> serde_json::Result<T> {
serde_json::from_str(&self.arguments)
}
}
/// The result of executing a tool call.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", content = "value")]
pub enum ToolResult {
/// Successful result with a JSON value.
#[serde(rename = "ok")]
Ok(serde_json::Value),
/// Error result with an error message.
#[serde(rename = "error")]
Error(String),
}
impl ToolResult {
pub fn ok<T: Serialize>(value: T) -> Self {
Self::Ok(serde_json::to_value(value).unwrap_or(serde_json::Value::Null))
}
pub fn error(message: impl Into<String>) -> Self {
Self::Error(message.into())
}
pub fn is_error(&self) -> bool {
matches!(self, Self::Error(_))
}
}
/// Errors that can occur during tool execution.
#[derive(Debug, thiserror::Error)]
pub enum ToolError {
#[error("tool not found: {0}")]
NotFound(String),
#[error("argument parse error: {0}")]
ParseError(String),
#[error("execution error: {0}")]
ExecutionError(String),
#[error("recursion limit exceeded (max depth: {max_depth})")]
RecursionLimitExceeded { max_depth: u32 },
#[error("max tool calls exceeded: {0}")]
MaxToolCallsExceeded(usize),
#[error("internal error: {0}")]
Internal(String),
}
impl ToolError {
pub fn into_result(self) -> ToolResult {
ToolResult::Error(self.to_string())
}
}
impl From<serde_json::Error> for ToolError {
fn from(e: serde_json::Error) -> Self {
Self::ParseError(e.to_string())
}
}
/// A completed tool call with its result, ready to be sent back to the AI.
#[derive(Debug, Clone)]
pub struct ToolCallResult {
/// The original tool call.
pub call: ToolCall,
/// The execution result.
pub result: ToolResult,
}
impl ToolCallResult {
pub fn ok(call: ToolCall, value: serde_json::Value) -> Self {
Self {
call,
result: ToolResult::Ok(value),
}
}
pub fn error(call: ToolCall, message: impl Into<String>) -> Self {
Self {
call,
result: ToolResult::Error(message.into()),
}
}
pub fn from_result(call: ToolCall, result: ToolResult) -> Self {
Self { call, result }
}
}