Files
zesdex/src/dto/chat/tool.rs
T

58 lines
2.0 KiB
Rust
Raw Normal View History

//! Tool-call DTOs embedded in assistant chat messages.
//!
//! Flow: provider response/stream carries `tool_calls` on an assistant
//! message → deserialized into `ToolCall`/`ToolFunction` → harness resolves
//! `function.name` against `all_tools()` and runs it with
//! `sanitize_tool_arguments(function.arguments)`.
//!
//! Why: kept separate from `dto::provider` because tool calls are a property
//! of a chat *message*, not of the request/response envelope.
use serde::{Deserialize, Serialize};
use serde_json::Value;
/// A single tool-call request emitted by the model in an assistant message.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
pub function: ToolFunction,
}
/// The function name and raw arguments payload for a `ToolCall`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolFunction {
pub name: String,
pub arguments: Value,
}
/// Normalize tool-call arguments into a JSON object/value.
///
/// Flow: some providers send `arguments` as a JSON-encoded string rather
/// than a nested object; if `args` is a string, attempt to parse it as
/// JSON. Objects and other value types pass through unchanged.
///
/// Why: falling back to the raw string on parse failure (rather than
/// erroring) keeps the harness resilient to malformed provider output.
///
/// Return: the parsed `Value`, or the original `args` clone if parsing fails.
pub fn sanitize_tool_arguments(args: &Value) -> Value {
match args {
Value::String(s) => {
match serde_json::from_str::<Value>(s) {
Ok(v) => v,
Err(e) => {
tracing::warn!(
"warning: tool argument is a JSON string but failed to parse: {}. Using raw string.",
e
);
args.clone()
}
}
}
obj @ Value::Object(_) => obj.clone(),
other => other.clone(),
}
}