2026-07-12 11:28:39 +07:00
|
|
|
//! 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.
|
|
|
|
|
|
2026-07-11 13:16:10 +07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use serde_json::Value;
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// A single tool-call request emitted by the model in an assistant message.
|
2026-07-11 13:16:10 +07:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ToolCall {
|
|
|
|
|
pub id: String,
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub type_: String,
|
|
|
|
|
pub function: ToolFunction,
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// The function name and raw arguments payload for a `ToolCall`.
|
2026-07-11 13:16:10 +07:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ToolFunction {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub arguments: Value,
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// 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.
|
2026-07-11 13:16:10 +07:00
|
|
|
pub fn sanitize_tool_arguments(args: &Value) -> Value {
|
|
|
|
|
match args {
|
|
|
|
|
Value::String(s) => {
|
2026-07-12 10:23:26 +07:00
|
|
|
match serde_json::from_str::<Value>(s) {
|
|
|
|
|
Ok(v) => v,
|
|
|
|
|
Err(e) => {
|
2026-07-12 10:57:32 +07:00
|
|
|
tracing::warn!(
|
2026-07-12 10:23:26 +07:00
|
|
|
"warning: tool argument is a JSON string but failed to parse: {}. Using raw string.",
|
|
|
|
|
e
|
|
|
|
|
);
|
|
|
|
|
args.clone()
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-11 13:16:10 +07:00
|
|
|
}
|
|
|
|
|
obj @ Value::Object(_) => obj.clone(),
|
|
|
|
|
other => other.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|