1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use ts_rs::TS;
8
9use crate::error::AiMuxError;
10use crate::types::ProviderMetadata;
11
12#[derive(Debug, Clone, Serialize, Deserialize, TS)]
14#[ts(export)]
15pub struct FunctionTool {
16 pub name: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub description: Option<String>,
21 pub input_schema: Value,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub strict: Option<bool>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub provider_options: Option<HashMap<String, Value>>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub input_examples: Option<Vec<Value>>,
35}
36
37impl FunctionTool {
38 pub fn new(name: impl Into<String>, input_schema: Value) -> Self {
42 FunctionTool {
43 name: name.into(),
44 description: None,
45 input_schema,
46 strict: None,
47 provider_options: None,
48 input_examples: None,
49 }
50 }
51
52 #[must_use]
54 pub fn with_description(mut self, description: impl Into<String>) -> Self {
55 self.description = Some(description.into());
56 self
57 }
58
59 #[must_use]
61 pub fn with_strict(mut self, strict: bool) -> Self {
62 self.strict = Some(strict);
63 self
64 }
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, TS)]
74#[ts(export)]
75pub struct ProviderTool {
76 pub id: String,
78 pub name: String,
80 pub args: Value,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, TS)]
89#[serde(tag = "type", rename_all = "snake_case")]
90#[ts(export)]
91pub enum Tool {
92 Function(FunctionTool),
94 Provider(ProviderTool),
96}
97
98impl From<FunctionTool> for Tool {
99 fn from(ft: FunctionTool) -> Self {
100 Tool::Function(ft)
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, TS)]
106#[ts(export)]
107pub struct ToolCall {
108 pub tool_call_id: String,
110 pub tool_name: String,
112 pub input: Value,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub provider_executed: Option<bool>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub dynamic: Option<bool>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub thought_signature: Option<String>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub provider_metadata: Option<ProviderMetadata>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub invalid: Option<bool>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub error: Option<AiMuxError>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize, TS)]
141#[ts(export)]
142pub struct ToolResult {
143 pub tool_call_id: String,
145 pub result: Value,
147 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub is_error: Option<bool>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub preliminary: Option<bool>,
153}
154
155#[derive(Debug, Clone, Default, PartialEq, TS)]
162#[ts(
163 export,
164 type = "\"auto\" | \"none\" | \"required\" | { type: \"tool\"; toolName: string }"
165)]
166pub enum ToolChoice {
167 #[default]
169 Auto,
170 None,
172 Required,
174 Tool { tool_name: String },
176}
177
178impl Serialize for ToolChoice {
179 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
180 where
181 S: serde::Serializer,
182 {
183 use serde::ser::SerializeStruct;
184
185 match self {
186 ToolChoice::Auto => serializer.serialize_str("auto"),
187 ToolChoice::None => serializer.serialize_str("none"),
188 ToolChoice::Required => serializer.serialize_str("required"),
189 ToolChoice::Tool { tool_name } => {
190 let mut s = serializer.serialize_struct("ToolChoice", 2)?;
191 s.serialize_field("type", "tool")?;
192 s.serialize_field("toolName", tool_name)?;
193 s.end()
194 }
195 }
196 }
197}
198
199impl<'de> Deserialize<'de> for ToolChoice {
200 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
201 where
202 D: serde::Deserializer<'de>,
203 {
204 use serde::de::Error;
205
206 #[derive(Deserialize)]
207 #[serde(untagged)]
208 enum Repr {
209 Str(String),
210 Tool {
211 #[serde(rename = "type")]
212 typ: String,
213 #[serde(rename = "toolName")]
214 tool_name: Option<String>,
215 },
216 }
217
218 match Repr::deserialize(deserializer)? {
219 Repr::Str(s) => match s.as_str() {
220 "auto" => Ok(ToolChoice::Auto),
221 "none" => Ok(ToolChoice::None),
222 "required" => Ok(ToolChoice::Required),
223 other => Err(D::Error::custom(format!(
224 "unknown toolChoice '{other}' (expected auto/none/required or {{type:\"tool\",toolName}})"
225 ))),
226 },
227 Repr::Tool { typ, tool_name } => {
228 if typ != "tool" {
229 return Err(D::Error::custom(format!(
230 "unknown toolChoice type '{typ}' (expected \"tool\")"
231 )));
232 }
233 let tool_name = tool_name.ok_or_else(|| {
234 D::Error::custom("toolChoice {type:\"tool\"} requires a toolName field")
235 })?;
236 Ok(ToolChoice::Tool { tool_name })
237 }
238 }
239 }
240}