use std::sync::Arc;
use serde_json::Value;
use tea_protocol::{ProtocolMetadata, ToolCallId};
use thiserror::Error;
use crate::{SchedulerClass, ToolName, ToolResource, ToolSource, ToolSpec};
#[derive(Debug, Clone, PartialEq)]
pub struct ToolInvocation {
tool_call_id: ToolCallId,
name: ToolName,
arguments: Value,
metadata: ProtocolMetadata,
}
impl ToolInvocation {
pub fn new(
tool_call_id: ToolCallId,
name: ToolName,
arguments: Value,
metadata: ProtocolMetadata,
) -> Result<Self, ToolInvocationError> {
if !arguments.is_object() {
return Err(ToolInvocationError::ArgumentsMustBeObject);
}
if serde_json::to_vec(&arguments)
.map_err(|_| ToolInvocationError::ArgumentsOutOfBounds)?
.len()
> 256 * 1024
|| json_depth(&arguments) > 32
{
return Err(ToolInvocationError::ArgumentsOutOfBounds);
}
Ok(Self {
tool_call_id,
name,
arguments,
metadata,
})
}
#[must_use]
pub const fn tool_call_id(&self) -> &ToolCallId {
&self.tool_call_id
}
#[must_use]
pub const fn name(&self) -> &ToolName {
&self.name
}
#[must_use]
pub const fn arguments(&self) -> &Value {
&self.arguments
}
#[must_use]
pub const fn metadata(&self) -> &ProtocolMetadata {
&self.metadata
}
}
#[derive(Debug, Clone)]
pub struct ValidatedToolInvocation {
invocation: ToolInvocation,
spec: Arc<ToolSpec>,
source: ToolSource,
resources: Vec<ToolResource>,
}
impl ValidatedToolInvocation {
#[cfg(feature = "execution")]
pub(crate) fn new(
invocation: ToolInvocation,
spec: Arc<ToolSpec>,
resources: Vec<ToolResource>,
) -> Self {
let source = spec.source().clone();
Self {
invocation,
spec,
source,
resources,
}
}
#[must_use]
pub const fn tool_call_id(&self) -> &ToolCallId {
self.invocation.tool_call_id()
}
#[must_use]
pub const fn name(&self) -> &ToolName {
self.invocation.name()
}
#[must_use]
pub const fn arguments(&self) -> &Value {
self.invocation.arguments()
}
#[must_use]
pub const fn metadata(&self) -> &ProtocolMetadata {
self.invocation.metadata()
}
#[must_use]
pub fn spec(&self) -> &ToolSpec {
&self.spec
}
#[must_use]
pub const fn source(&self) -> &ToolSource {
&self.source
}
#[must_use]
pub fn resources(&self) -> &[ToolResource] {
&self.resources
}
#[must_use]
pub fn scheduler_class(&self) -> SchedulerClass {
self.spec.scheduler_class()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum ToolInvocationError {
#[error("tool arguments must be a JSON object")]
ArgumentsMustBeObject,
#[error("tool arguments exceed supported bounds")]
ArgumentsOutOfBounds,
}
fn json_depth(value: &Value) -> usize {
match value {
Value::Array(values) => 1 + values.iter().map(json_depth).max().unwrap_or(0),
Value::Object(values) => 1 + values.values().map(json_depth).max().unwrap_or(0),
_ => 1,
}
}