1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use ts_rs::TS;
8
9#[derive(Debug, Clone, Serialize, Deserialize, TS)]
11#[ts(export)]
12pub struct FunctionTool {
13 pub name: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub description: Option<String>,
18 pub input_schema: Value,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub strict: Option<bool>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub provider_options: Option<HashMap<String, Value>>,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub input_examples: Option<Vec<Value>>,
32}
33
34impl FunctionTool {
35 pub fn new(name: impl Into<String>, input_schema: Value) -> Self {
39 FunctionTool {
40 name: name.into(),
41 description: None,
42 input_schema,
43 strict: None,
44 provider_options: None,
45 input_examples: None,
46 }
47 }
48
49 pub fn with_description(mut self, description: impl Into<String>) -> Self {
51 self.description = Some(description.into());
52 self
53 }
54
55 pub fn with_strict(mut self, strict: bool) -> Self {
57 self.strict = Some(strict);
58 self
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, TS)]
69#[ts(export)]
70pub struct ProviderTool {
71 pub id: String,
73 pub name: String,
75 pub args: Value,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, TS)]
84#[serde(tag = "type", rename_all = "snake_case")]
85#[ts(export)]
86pub enum Tool {
87 Function(FunctionTool),
89 Provider(ProviderTool),
91}
92
93impl From<FunctionTool> for Tool {
94 fn from(ft: FunctionTool) -> Self {
95 Tool::Function(ft)
96 }
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, TS)]
101#[ts(export)]
102pub struct ToolCall {
103 pub tool_call_id: String,
105 pub tool_name: String,
107 pub input: Value,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub provider_executed: Option<bool>,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub dynamic: Option<bool>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, TS)]
119#[ts(export)]
120pub struct ToolResult {
121 pub tool_call_id: String,
123 pub result: Value,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub is_error: Option<bool>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub preliminary: Option<bool>,
131}
132
133#[derive(Debug, Clone, Default, PartialEq, TS)]
140#[ts(
141 export,
142 type = "\"auto\" | \"none\" | \"required\" | { type: \"tool\"; toolName: string }"
143)]
144pub enum ToolChoice {
145 #[default]
147 Auto,
148 None,
150 Required,
152 Tool { tool_name: String },
154}
155
156impl Serialize for ToolChoice {
157 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
158 where
159 S: serde::Serializer,
160 {
161 use serde::ser::SerializeStruct;
162
163 match self {
164 ToolChoice::Auto => serializer.serialize_str("auto"),
165 ToolChoice::None => serializer.serialize_str("none"),
166 ToolChoice::Required => serializer.serialize_str("required"),
167 ToolChoice::Tool { tool_name } => {
168 let mut s = serializer.serialize_struct("ToolChoice", 2)?;
169 s.serialize_field("type", "tool")?;
170 s.serialize_field("toolName", tool_name)?;
171 s.end()
172 }
173 }
174 }
175}
176
177impl<'de> Deserialize<'de> for ToolChoice {
178 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179 where
180 D: serde::Deserializer<'de>,
181 {
182 use serde::de::Error;
183
184 #[derive(Deserialize)]
185 #[serde(untagged)]
186 enum Repr {
187 Str(String),
188 Tool {
189 #[serde(rename = "type")]
190 typ: String,
191 #[serde(rename = "toolName")]
192 tool_name: Option<String>,
193 },
194 }
195
196 match Repr::deserialize(deserializer)? {
197 Repr::Str(s) => match s.as_str() {
198 "auto" => Ok(ToolChoice::Auto),
199 "none" => Ok(ToolChoice::None),
200 "required" => Ok(ToolChoice::Required),
201 other => Err(D::Error::custom(format!(
202 "unknown toolChoice '{other}' (expected auto/none/required or {{type:\"tool\",toolName}})"
203 ))),
204 },
205 Repr::Tool { typ, tool_name } => {
206 if typ != "tool" {
207 return Err(D::Error::custom(format!(
208 "unknown toolChoice type '{typ}' (expected \"tool\")"
209 )));
210 }
211 let tool_name = tool_name.ok_or_else(|| {
212 D::Error::custom("toolChoice {type:\"tool\"} requires a toolName field")
213 })?;
214 Ok(ToolChoice::Tool { tool_name })
215 }
216 }
217 }
218}