spec_ai_core/agent/
function_calling.rs1use async_openai::types::{ChatCompletionTool, ChatCompletionToolType, FunctionObject};
7use serde_json::{json, Value};
8
9fn parameters_to_openai_schema(params: &Value) -> Value {
11 let properties = params
13 .get("properties")
14 .and_then(|p| p.as_object())
15 .cloned()
16 .unwrap_or_default();
17
18 let required: Vec<String> = params
19 .get("required")
20 .and_then(|r| r.as_array())
21 .map(|arr| {
22 arr.iter()
23 .filter_map(|v| v.as_str().map(String::from))
24 .collect()
25 })
26 .unwrap_or_default();
27
28 json!({
29 "type": "object",
30 "properties": properties,
31 "required": required
32 })
33}
34
35pub fn tool_to_openai_function(
37 name: &str,
38 description: &str,
39 parameters: &Value,
40) -> ChatCompletionTool {
41 let schema = parameters_to_openai_schema(parameters);
42
43 ChatCompletionTool {
44 r#type: ChatCompletionToolType::Function,
45 function: FunctionObject {
46 name: name.to_string(),
47 description: Some(description.to_string()),
48 parameters: Some(schema),
49 strict: Some(false),
50 },
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct FunctionCall {
57 pub name: String,
59 pub arguments: Value,
61}
62
63pub fn parse_tool_call_from_message(
66 _tool_call_id: &str,
67 function_name: &str,
68 arguments_str: &str,
69) -> Option<(String, Value)> {
70 let args = serde_json::from_str(arguments_str).ok()?;
72 Some((function_name.to_string(), args))
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_tool_to_openai_function() {
81 let params = json!({
82 "type": "object",
83 "properties": {
84 "message": {
85 "type": "string",
86 "description": "The message to echo"
87 }
88 },
89 "required": ["message"]
90 });
91
92 let tool = tool_to_openai_function("echo", "Echo a message", ¶ms);
93
94 assert_eq!(tool.function.name, "echo");
95 assert_eq!(
96 tool.function.description,
97 Some("Echo a message".to_string())
98 );
99 assert!(tool.function.parameters.is_some());
100 }
101
102 #[test]
103 fn test_parse_tool_call_from_message() {
104 let result = parse_tool_call_from_message("call_123", "echo", r#"{"message": "hello"}"#);
105
106 assert!(result.is_some());
107 let (name, args) = result.unwrap();
108 assert_eq!(name, "echo");
109 assert_eq!(args["message"], "hello");
110 }
111
112 #[test]
113 fn test_parse_tool_call_invalid_json() {
114 let result = parse_tool_call_from_message("call_123", "echo", "invalid json");
115 assert!(result.is_none());
116 }
117}