use serde::{Deserialize, Serialize};
use crate::adapters::ToolAdapter;
use crate::ToolMeta;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnthropicTool {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AnthropicToolUse {
#[serde(rename = "type")]
pub use_type: String,
pub id: String,
pub name: String,
pub input: serde_json::Value,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AnthropicCallRequest {
pub tool_use: AnthropicToolUse,
}
#[derive(Debug, Clone, Serialize)]
pub struct AnthropicToolResult {
#[serde(rename = "type")]
pub result_type: String,
pub tool_use_id: String,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_error: Option<bool>,
}
pub struct AnthropicAdapter;
impl ToolAdapter for AnthropicAdapter {
type ToolSpec = AnthropicTool;
type CallRequest = AnthropicCallRequest;
type CallResponse = AnthropicToolResult;
fn to_spec(meta: &ToolMeta) -> AnthropicTool {
AnthropicTool {
name: meta.name.to_string(),
description: meta.description.to_string(),
input_schema: (meta.schema)().clone(),
}
}
fn from_request(req: AnthropicCallRequest) -> Result<(String, serde_json::Value), crate::ToolError> {
Ok((req.tool_use.name, req.tool_use.input))
}
fn to_response(output: serde_json::Value) -> AnthropicToolResult {
AnthropicToolResult {
result_type: "tool_result".to_string(),
tool_use_id: String::new(), content: serde_json::to_string(&output).unwrap_or_else(|_| output.to_string()),
is_error: None,
}
}
}
impl AnthropicAdapter {
pub fn to_response_with_id(output: serde_json::Value, tool_use_id: String) -> AnthropicToolResult {
AnthropicToolResult {
result_type: "tool_result".to_string(),
tool_use_id,
content: serde_json::to_string(&output).unwrap_or_else(|_| output.to_string()),
is_error: None,
}
}
pub fn error_response(error_message: String, tool_use_id: String) -> AnthropicToolResult {
AnthropicToolResult {
result_type: "tool_result".to_string(),
tool_use_id,
content: error_message,
is_error: Some(true),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Tool, ToolContext, ToolError};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Deserialize, JsonSchema)]
struct TestParams {
value: i32,
}
#[derive(Serialize, JsonSchema)]
struct TestOutput {
result: i32,
}
struct TestTool;
impl Tool for TestTool {
type Params = TestParams;
type Output = TestOutput;
const NAME: &'static str = "test_anthropic";
const DESCRIPTION: &'static str = "Test tool for Anthropic";
async fn call(
_ctx: Arc<ToolContext>,
params: Self::Params,
) -> Result<Self::Output, ToolError> {
Ok(TestOutput {
result: params.value * 3,
})
}
}
inventory::submit! {
crate::ToolMeta {
name: TestTool::NAME,
description: TestTool::DESCRIPTION,
call: |ctx, params| {
Box::pin(async move {
let params: TestParams = serde_json::from_value(params)?;
let result = <TestTool as Tool>::call(ctx, params).await?;
Ok(serde_json::to_value(result)?)
})
},
schema: || <TestTool as Tool>::schema(),
examples: || <TestTool as Tool>::EXAMPLES,
}
}
#[test]
fn test_to_spec() {
let meta = crate::find_tool("test_anthropic").unwrap();
let spec = AnthropicAdapter::to_spec(meta);
assert_eq!(spec.name, "test_anthropic");
assert_eq!(spec.description, "Test tool for Anthropic");
assert!(spec.input_schema.is_object());
}
#[test]
fn test_from_request() {
let req = AnthropicCallRequest {
tool_use: AnthropicToolUse {
use_type: "tool_use".to_string(),
id: "toolu_123".to_string(),
name: "test_anthropic".to_string(),
input: serde_json::json!({"value": 5}),
},
};
let (name, params) = AnthropicAdapter::from_request(req).unwrap();
assert_eq!(name, "test_anthropic");
assert_eq!(params["value"], 5);
}
#[test]
fn test_to_response() {
let output = serde_json::json!({"result": 15});
let response = AnthropicAdapter::to_response(output);
assert_eq!(response.result_type, "tool_result");
assert!(response.content.contains("result"));
assert!(response.is_error.is_none());
}
#[test]
fn test_to_response_with_id() {
let output = serde_json::json!({"result": 15});
let response = AnthropicAdapter::to_response_with_id(output, "toolu_456".to_string());
assert_eq!(response.tool_use_id, "toolu_456");
assert_eq!(response.result_type, "tool_result");
}
#[test]
fn test_error_response() {
let response = AnthropicAdapter::error_response("Test error".to_string(), "toolu_789".to_string());
assert_eq!(response.tool_use_id, "toolu_789");
assert_eq!(response.result_type, "tool_result");
assert_eq!(response.is_error, Some(true));
}
}