use crate::{
output::{
ActivityId, ActivityMetadata, pending_tool_display_summary, tool_display_label,
tool_display_summary,
},
providers::ToolCall,
tools::ToolResult,
};
use serde_json::json;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub(super) struct ToolCallFingerprint {
id: String,
name: String,
arguments: String,
}
impl From<&ToolCall> for ToolCallFingerprint {
fn from(call: &ToolCall) -> Self {
Self {
id: call.id.clone(),
name: call.name.clone(),
arguments: call.arguments.to_string(),
}
}
}
pub(super) fn function_call_item(call: &ToolCall) -> serde_json::Value {
json!({
"type": "function_call",
"call_id": call.id.clone(),
"name": call.name.clone(),
"arguments": arguments_json_text(&call.arguments),
"status": "completed",
})
}
pub(super) fn arguments_json_text(arguments: &serde_json::Value) -> String {
match arguments {
serde_json::Value::String(text) => text.clone(),
value => value.to_string(),
}
}
pub(super) fn activity_id_for_call(
iteration: usize,
tool_index: usize,
call: &ToolCall,
) -> ActivityId {
if call.id.trim().is_empty() {
ActivityId::new(format!("turn-{iteration}/tool-{tool_index}-{}", call.name))
} else {
ActivityId::new(call.id.clone())
}
}
pub(super) fn tool_activity_metadata(
call: &ToolCall,
result: Option<&ToolResult>,
) -> ActivityMetadata {
let summary = result
.map(|result| tool_display_summary(call, result))
.or_else(|| pending_tool_display_summary(call));
let mut metadata = ActivityMetadata::new(
summary
.as_ref()
.map(|summary| summary.label.clone())
.unwrap_or_else(|| tool_display_label(call)),
);
if let Some(summary) = summary {
metadata.fields = summary.metadata;
}
metadata
}