Skip to main content

gproxy_protocol/openai/generate_content/responses/tools/
definitions.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::openai::common::*;
7
8use super::*;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11#[serde(tag = "type")]
12#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
13pub enum ResponseTool {
14    #[serde(rename = "function")]
15    Function {
16        name: String,
17        parameters: ResponseFunctionParameters,
18        // Some OpenAI-compatible Responses providers echo `strict: null`.
19        #[serde(default, skip_serializing_if = "ResponseFunctionStrict::is_absent")]
20        strict: ResponseFunctionStrict,
21        #[serde(skip_serializing_if = "Option::is_none")]
22        defer_loading: Option<bool>,
23        #[serde(skip_serializing_if = "Option::is_none")]
24        description: Option<String>,
25        #[serde(skip_serializing_if = "Option::is_none")]
26        output_schema: Option<JsonSchema>,
27        #[serde(skip_serializing_if = "Option::is_none")]
28        allowed_callers: Option<Vec<ToolCaller>>,
29        #[serde(rename = "async", skip_serializing_if = "Option::is_none")]
30        async_: Option<bool>,
31        #[serde(default, flatten)]
32        rest: Rest,
33    },
34    #[serde(rename = "file_search")]
35    FileSearch {
36        vector_store_ids: Vec<String>,
37        #[serde(skip_serializing_if = "Option::is_none")]
38        filters: Option<FileSearchFilter>,
39        #[serde(skip_serializing_if = "Option::is_none")]
40        max_num_results: Option<u32>,
41        #[serde(skip_serializing_if = "Option::is_none")]
42        ranking_options: Option<FileSearchRankingOptions>,
43        #[serde(default, flatten)]
44        rest: Rest,
45    },
46    #[serde(rename = "computer")]
47    Computer {
48        #[serde(default, flatten)]
49        rest: Rest,
50    },
51    #[serde(rename = "computer_use_preview")]
52    ComputerUsePreview {
53        display_height: u32,
54        display_width: u32,
55        environment: ComputerUseEnvironment,
56        #[serde(default, flatten)]
57        rest: Rest,
58    },
59    #[serde(rename = "web_search")]
60    WebSearch {
61        #[serde(skip_serializing_if = "Option::is_none")]
62        filters: Option<WebSearchFilters>,
63        // gproxy extension: forwarded to Claude web_search `max_uses`; real OpenAI
64        // upstreams do not accept it, so OpenAI-bound transforms never set it.
65        #[serde(skip_serializing_if = "Option::is_none")]
66        max_uses: Option<u64>,
67        #[serde(skip_serializing_if = "Option::is_none")]
68        search_context_size: Option<SearchContextSize>,
69        #[serde(skip_serializing_if = "Option::is_none")]
70        user_location: Option<WebSearchUserLocation>,
71        #[serde(default, flatten)]
72        rest: Rest,
73    },
74    #[serde(rename = "web_search_2025_08_26")]
75    WebSearch20250826 {
76        #[serde(skip_serializing_if = "Option::is_none")]
77        filters: Option<WebSearchFilters>,
78        // gproxy extension: see `WebSearch::max_uses`.
79        #[serde(skip_serializing_if = "Option::is_none")]
80        max_uses: Option<u64>,
81        #[serde(skip_serializing_if = "Option::is_none")]
82        search_context_size: Option<SearchContextSize>,
83        #[serde(skip_serializing_if = "Option::is_none")]
84        user_location: Option<WebSearchUserLocation>,
85        #[serde(default, flatten)]
86        rest: Rest,
87    },
88    // gproxy extension: mirrors Claude web_fetch so Responses clients can
89    // configure fetch behaviour on Claude routes.
90    #[serde(rename = "web_fetch")]
91    WebFetch {
92        #[serde(skip_serializing_if = "Option::is_none")]
93        allowed_domains: Option<Vec<String>>,
94        #[serde(skip_serializing_if = "Option::is_none")]
95        blocked_domains: Option<Vec<String>>,
96        #[serde(skip_serializing_if = "Option::is_none")]
97        max_content_tokens: Option<u64>,
98        #[serde(skip_serializing_if = "Option::is_none")]
99        max_uses: Option<u64>,
100        #[serde(default, flatten)]
101        rest: Rest,
102    },
103    // gproxy extension: mirrors Claude memory_20250818.
104    #[serde(rename = "memory")]
105    Memory {
106        #[serde(default, flatten)]
107        rest: Rest,
108    },
109    #[serde(rename = "x_search")]
110    XSearch {
111        #[serde(default, flatten)]
112        rest: Rest,
113    },
114    #[serde(rename = "collections_search")]
115    CollectionsSearch {
116        vector_store_ids: Vec<String>,
117        #[serde(default, flatten)]
118        rest: Rest,
119    },
120    #[serde(rename = "mcp")]
121    Mcp {
122        server_label: String,
123        #[serde(skip_serializing_if = "Option::is_none")]
124        allowed_tools: Option<McpAllowedTools>,
125        #[serde(skip_serializing_if = "Option::is_none")]
126        authorization: Option<String>,
127        #[serde(skip_serializing_if = "Option::is_none")]
128        connector_id: Option<McpConnectorId>,
129        #[serde(skip_serializing_if = "Option::is_none")]
130        defer_loading: Option<bool>,
131        #[serde(skip_serializing_if = "Option::is_none")]
132        headers: Option<BTreeMap<String, String>>,
133        #[serde(skip_serializing_if = "Option::is_none")]
134        require_approval: Option<McpRequireApproval>,
135        #[serde(skip_serializing_if = "Option::is_none")]
136        server_description: Option<String>,
137        #[serde(skip_serializing_if = "Option::is_none")]
138        server_url: Option<String>,
139        #[serde(skip_serializing_if = "Option::is_none")]
140        tunnel_id: Option<String>,
141        #[serde(skip_serializing_if = "Option::is_none")]
142        allowed_callers: Option<Vec<ToolCaller>>,
143        #[serde(default, flatten)]
144        rest: Rest,
145    },
146    #[serde(rename = "code_execution")]
147    CodeExecution {
148        #[serde(default, flatten)]
149        rest: Rest,
150    },
151    #[serde(rename = "code_interpreter")]
152    CodeInterpreter {
153        container: CodeInterpreterContainer,
154        #[serde(skip_serializing_if = "Option::is_none")]
155        allowed_callers: Option<Vec<ToolCaller>>,
156        #[serde(default, flatten)]
157        rest: Rest,
158    },
159    #[serde(rename = "image_generation")]
160    ImageGeneration {
161        #[serde(skip_serializing_if = "Option::is_none")]
162        action: Option<ImageGenerationAction>,
163        #[serde(skip_serializing_if = "Option::is_none")]
164        background: Option<ImageBackground>,
165        #[serde(skip_serializing_if = "Option::is_none")]
166        input_fidelity: Option<ImageInputFidelity>,
167        #[serde(skip_serializing_if = "Option::is_none")]
168        input_image_mask: Option<ImageMask>,
169        #[serde(skip_serializing_if = "Option::is_none")]
170        model: Option<OpenAiModelId>,
171        #[serde(skip_serializing_if = "Option::is_none")]
172        moderation: Option<ImageModeration>,
173        #[serde(skip_serializing_if = "Option::is_none")]
174        output_compression: Option<u32>,
175        #[serde(skip_serializing_if = "Option::is_none")]
176        output_format: Option<ImageOutputFormat>,
177        #[serde(skip_serializing_if = "Option::is_none")]
178        partial_images: Option<u32>,
179        #[serde(skip_serializing_if = "Option::is_none")]
180        quality: Option<ImageEditQuality>,
181        #[serde(skip_serializing_if = "Option::is_none")]
182        size: Option<ResponseImageGenerationSize>,
183        #[serde(default, flatten)]
184        rest: Rest,
185    },
186    #[serde(rename = "local_shell")]
187    LocalShell {
188        #[serde(default, flatten)]
189        rest: Rest,
190    },
191    #[serde(rename = "shell")]
192    Shell {
193        #[serde(skip_serializing_if = "Option::is_none")]
194        environment: Option<ResponseShellEnvironment>,
195        #[serde(skip_serializing_if = "Option::is_none")]
196        allowed_callers: Option<Vec<ToolCaller>>,
197        #[serde(default, flatten)]
198        rest: Rest,
199    },
200    #[serde(rename = "custom")]
201    Custom {
202        name: String,
203        #[serde(skip_serializing_if = "Option::is_none")]
204        defer_loading: Option<bool>,
205        #[serde(skip_serializing_if = "Option::is_none")]
206        description: Option<String>,
207        #[serde(skip_serializing_if = "Option::is_none")]
208        format: Option<CustomToolInputFormat>,
209        #[serde(skip_serializing_if = "Option::is_none")]
210        allowed_callers: Option<Vec<ToolCaller>>,
211        #[serde(rename = "async", skip_serializing_if = "Option::is_none")]
212        async_: Option<bool>,
213        #[serde(default, flatten)]
214        rest: Rest,
215    },
216    #[serde(rename = "namespace")]
217    Namespace {
218        description: String,
219        name: String,
220        tools: Vec<ResponseNamespaceTool>,
221        #[serde(default, flatten)]
222        rest: Rest,
223    },
224    #[serde(rename = "tool_search")]
225    ToolSearch {
226        #[serde(skip_serializing_if = "Option::is_none")]
227        description: Option<String>,
228        #[serde(skip_serializing_if = "Option::is_none")]
229        execution: Option<ToolSearchExecution>,
230        /// `upstream_docs/openai/docs/Responses.md`, `ToolSearch.parameters`:
231        /// optional unknown or null.
232        #[serde(skip_serializing_if = "Option::is_none")]
233        parameters: Option<Value>,
234        #[serde(default, flatten)]
235        rest: Rest,
236    },
237    #[serde(rename = "programmatic_tool_calling")]
238    ProgrammaticToolCalling {
239        #[serde(default, flatten)]
240        rest: Rest,
241    },
242    #[serde(rename = "web_search_preview")]
243    WebSearchPreview {
244        #[serde(skip_serializing_if = "Option::is_none")]
245        search_content_types: Option<Vec<SearchContentType>>,
246        #[serde(skip_serializing_if = "Option::is_none")]
247        search_context_size: Option<SearchContextSize>,
248        #[serde(skip_serializing_if = "Option::is_none")]
249        user_location: Option<WebSearchPreviewUserLocation>,
250        #[serde(default, flatten)]
251        rest: Rest,
252    },
253    #[serde(rename = "web_search_preview_2025_03_11")]
254    WebSearchPreview20250311 {
255        #[serde(skip_serializing_if = "Option::is_none")]
256        search_content_types: Option<Vec<SearchContentType>>,
257        #[serde(skip_serializing_if = "Option::is_none")]
258        search_context_size: Option<SearchContextSize>,
259        #[serde(skip_serializing_if = "Option::is_none")]
260        user_location: Option<WebSearchPreviewUserLocation>,
261        #[serde(default, flatten)]
262        rest: Rest,
263    },
264    #[serde(rename = "apply_patch")]
265    ApplyPatch {
266        #[serde(skip_serializing_if = "Option::is_none")]
267        allowed_callers: Option<Vec<ToolCaller>>,
268        // gproxy extension: forwarded to Claude text_editor `max_characters`.
269        #[serde(skip_serializing_if = "Option::is_none")]
270        max_characters: Option<u64>,
271        #[serde(default, flatten)]
272        rest: Rest,
273    },
274}
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
277#[serde(tag = "type")]
278#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
279pub enum ResponseNamespaceTool {
280    #[serde(rename = "function")]
281    Function {
282        name: String,
283        #[serde(skip_serializing_if = "Option::is_none")]
284        defer_loading: Option<bool>,
285        #[serde(skip_serializing_if = "Option::is_none")]
286        description: Option<String>,
287        #[serde(skip_serializing_if = "Option::is_none")]
288        output_schema: Option<JsonSchema>,
289        /// `upstream_docs/openai/docs/Responses.md`,
290        /// `Namespace.tools[].Function.parameters`: optional unknown or null.
291        #[serde(skip_serializing_if = "Option::is_none")]
292        parameters: Option<Value>,
293        #[serde(skip_serializing_if = "Option::is_none")]
294        strict: Option<bool>,
295        #[serde(skip_serializing_if = "Option::is_none")]
296        allowed_callers: Option<Vec<ToolCaller>>,
297        #[serde(rename = "async", skip_serializing_if = "Option::is_none")]
298        async_: Option<bool>,
299        #[serde(default, flatten)]
300        rest: Rest,
301    },
302    #[serde(rename = "custom")]
303    Custom {
304        name: String,
305        #[serde(skip_serializing_if = "Option::is_none")]
306        defer_loading: Option<bool>,
307        #[serde(skip_serializing_if = "Option::is_none")]
308        description: Option<String>,
309        #[serde(skip_serializing_if = "Option::is_none")]
310        format: Option<CustomToolInputFormat>,
311        #[serde(skip_serializing_if = "Option::is_none")]
312        allowed_callers: Option<Vec<ToolCaller>>,
313        #[serde(rename = "async", skip_serializing_if = "Option::is_none")]
314        async_: Option<bool>,
315        #[serde(default, flatten)]
316        rest: Rest,
317    },
318}
319
320#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
321#[serde(untagged)]
322#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
323pub enum ResponseFunctionParameters {
324    Schema(JsonSchema),
325    Null,
326}
327
328#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
329#[serde(untagged)]
330#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
331pub enum ResponseFunctionStrict {
332    Value(bool),
333    Null,
334    #[default]
335    Absent,
336}
337
338impl ResponseFunctionStrict {
339    fn is_absent(&self) -> bool {
340        matches!(self, Self::Absent)
341    }
342}