llm_tool_runtime/
provider.rs1use crate::{ToolBackendKind, ToolDescriptor, ToolError, ToolErrorClass};
2use serde_json::{json, Value};
3
4pub fn render_openai_tool(descriptor: &ToolDescriptor, strict: bool) -> Result<Value, ToolError> {
6 match descriptor.backend_kind {
7 ToolBackendKind::LocalFunction | ToolBackendKind::OpenAiFunction => {
8 let mut schema = descriptor.input_schema.clone();
9
10 if strict {
13 if let Some(obj) = schema.as_object_mut() {
14 obj.insert("additionalProperties".into(), Value::Bool(false));
15
16 if let Some(props) = obj.get("properties").and_then(|p| p.as_object()) {
17 let all_keys: Vec<Value> =
18 props.keys().map(|k| Value::String(k.clone())).collect();
19 obj.insert("required".into(), Value::Array(all_keys));
20 }
21 }
22 }
23
24 Ok(json!({
25 "type": "function",
26 "name": descriptor.name,
27 "description": descriptor.description.clone().unwrap_or_default(),
28 "parameters": schema,
29 "strict": strict,
30 }))
31 }
32 ToolBackendKind::OpenAiBuiltIn | ToolBackendKind::RemoteMcp => {
33 descriptor.provider_payload.clone().ok_or_else(|| {
34 ToolError::new(
35 ToolErrorClass::ProviderContract,
36 format!(
37 "tool {} requires provider_payload for OpenAI rendering",
38 descriptor.name
39 ),
40 )
41 })
42 }
43 ToolBackendKind::OllamaFunction => Err(ToolError::new(
44 ToolErrorClass::ProviderContract,
45 format!("tool {} is Ollama-only", descriptor.name),
46 )),
47 }
48}
49
50pub fn render_ollama_tool(descriptor: &ToolDescriptor) -> Result<Value, ToolError> {
52 match descriptor.backend_kind {
53 ToolBackendKind::LocalFunction | ToolBackendKind::OllamaFunction => Ok(json!({
54 "type": "function",
55 "function": {
56 "name": descriptor.name,
57 "description": descriptor.description.clone().unwrap_or_default(),
58 "parameters": descriptor.input_schema,
59 }
60 })),
61 _ => Err(ToolError::new(
62 ToolErrorClass::ProviderContract,
63 format!("tool {} cannot be rendered for Ollama", descriptor.name),
64 )),
65 }
66}