Skip to main content

openagent/api/response/
type.rs

1//! OpenAI response API common types.
2
3#![allow(missing_docs)]
4
5// self
6use crate::_prelude::*;
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct ResponseMessage<T> {
10	pub content: T,
11	pub role: Role,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
15pub struct ResponseOutputMessage {
16	#[serde(flatten)]
17	pub message: ResponseMessage<Vec<ResponseMessageOutputContent>>,
18	pub id: String,
19	pub status: Status3,
20}
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
23#[serde(tag = "type", rename_all = "snake_case")]
24pub enum ResponseMessageOutputContent {
25	OutputText {
26		annotations: Vec<Annotation>,
27		text: String,
28		#[serde(skip_serializing_if = "Option::is_none")]
29		logprobs: Option<Vec<Logprobs>>,
30	},
31	Refusal(Refusal),
32}
33
34#[derive(Clone, Debug, Serialize, Deserialize)]
35#[serde(tag = "type", rename_all = "snake_case")]
36pub enum Annotation {
37	FileCitation {
38		file_id: String,
39		index: u32,
40	},
41	UrlCitation {
42		end_index: u32,
43		start_index: u32,
44		title: String,
45		url: String,
46	},
47	ContainerFileCitation {
48		container_id: String,
49		end_index: u32,
50		file_id: String,
51		start_index: u32,
52	},
53	FilePath {
54		file_id: String,
55		index: u32,
56	},
57}
58
59#[derive(Clone, Debug, Serialize, Deserialize)]
60pub struct Refusal {
61	pub refusal: String,
62}
63
64impl_serializable_deserializable_enum! {
65	Status3 {
66		InProgress => "in_progress",
67		Completed => "completed",
68		Incomplete => "incomplete",
69	}
70}
71impl Status3 {
72	pub fn in_progress(&self) -> bool {
73		matches!(self, Self::InProgress)
74	}
75
76	pub fn completed(&self) -> bool {
77		matches!(self, Self::Completed)
78	}
79
80	pub fn incomplete(&self) -> bool {
81		matches!(self, Self::Incomplete)
82	}
83}
84
85#[derive(Clone, Debug, Serialize, Deserialize)]
86pub struct FileSearchCall {
87	pub id: String,
88	pub queries: Vec<String>,
89	pub status: FileSearchToolCallStatus,
90	#[serde(skip_serializing_if = "Option::is_none")]
91	pub results: Option<Vec<FileSearchToolCallResult>>,
92}
93
94impl_serializable_deserializable_enum! {
95	FileSearchToolCallStatus {
96		InProgress => "in_progress",
97		Searching => "searching",
98		Incomplete => "incomplete",
99		Failed => "failed",
100	}
101}
102
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct FileSearchToolCallResult {
105	#[serde(skip_serializing_if = "Option::is_none")]
106	pub attributes: Option<Map>,
107	#[serde(skip_serializing_if = "Option::is_none")]
108	pub file_id: Option<String>,
109	#[serde(skip_serializing_if = "Option::is_none")]
110	pub filename: Option<String>,
111	#[serde(skip_serializing_if = "Option::is_none")]
112	pub score: Option<f32>,
113	#[serde(skip_serializing_if = "Option::is_none")]
114	pub text: Option<String>,
115}
116
117#[derive(Clone, Debug, Serialize, Deserialize)]
118pub struct ComputerCall {
119	pub action: ComputerToolCallAction,
120	pub call_id: String,
121	pub id: String,
122	pub pending_safety_checks: Vec<PendingSafetyCheck>,
123	pub status: Status3,
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
127#[serde(tag = "type", rename_all = "snake_case")]
128pub enum ComputerToolCallAction {
129	Click {
130		button: Button,
131		#[serde(flatten)]
132		coordinate: Coordinate,
133	},
134	DoubleClick {
135		#[serde(flatten)]
136		coordinate: Coordinate,
137	},
138	Drag {
139		path: Vec<Coordinate>,
140	},
141	Keypress {
142		// TODO: Keycode.
143		keys: Vec<String>,
144	},
145	Move {
146		#[serde(flatten)]
147		coordinate: Coordinate,
148	},
149	Screenshot,
150	Scroll {
151		scroll_x: i32,
152		scroll_y: i32,
153		#[serde(flatten)]
154		coordinate: Coordinate,
155	},
156	Type {
157		text: String,
158	},
159	Wait,
160}
161
162impl_serializable_deserializable_enum! {
163	Button {
164		Left => "left",
165		Right => "right",
166		Wheel => "wheel",
167		Back => "back",
168		Forward => "forward",
169	}
170}
171
172#[derive(Clone, Debug, Serialize, Deserialize)]
173pub struct Coordinate {
174	pub x: u32,
175	pub y: u32,
176}
177
178#[derive(Clone, Debug, Serialize, Deserialize)]
179pub struct PendingSafetyCheck {
180	pub code: String,
181	pub id: String,
182	pub message: String,
183}
184
185#[derive(Clone, Debug, Serialize, Deserialize)]
186pub struct WebSearchCall {
187	pub id: String,
188	pub status: String,
189}
190
191#[derive(Clone, Debug, Serialize, Deserialize)]
192pub struct FunctionCall {
193	pub arguments: Value,
194	pub call_id: String,
195	pub name: String,
196	#[serde(skip_serializing_if = "Option::is_none")]
197	pub id: Option<String>,
198	#[serde(skip_serializing_if = "Option::is_none")]
199	pub status: Option<Status3>,
200}
201
202#[derive(Clone, Debug, Serialize, Deserialize)]
203pub struct ReasoningItem {
204	pub id: String,
205	pub summary: Vec<SummaryText>,
206	#[serde(skip_serializing_if = "Option::is_none")]
207	pub encrypted_content: Option<String>,
208	#[serde(skip_serializing_if = "Option::is_none")]
209	pub status: Option<Status3>,
210}
211
212#[derive(Clone, Debug, Serialize, Deserialize)]
213pub struct SummaryText {
214	pub text: String,
215	pub r#type: ConstSummaryText,
216}
217
218impl_const_str! {
219	SummaryText  => "summary_text",
220}
221
222#[derive(Clone, Debug, Serialize, Deserialize)]
223pub struct ImageGenerationCall {
224	pub id: String,
225	// This field requires explicit null serialization.
226	pub result: Option<String>,
227	pub status: String,
228}
229
230#[derive(Clone, Debug, Serialize, Deserialize)]
231pub struct CodeInterpreterCall {
232	pub code: String,
233	pub id: String,
234	pub results: Vec<CodeInterpreterCallOutput>,
235	pub status: String,
236	#[serde(skip_serializing_if = "Option::is_none")]
237	pub container_id: Option<String>,
238}
239
240#[derive(Clone, Debug, Serialize, Deserialize)]
241#[serde(tag = "type", rename_all = "snake_case")]
242pub enum CodeInterpreterCallOutput {
243	Logs { logs: String },
244	Files { files: Vec<FileOutput> },
245}
246
247#[derive(Clone, Debug, Serialize, Deserialize)]
248pub struct FileOutput {
249	pub file_id: String,
250	pub mime_type: String,
251}
252
253#[derive(Clone, Debug, Serialize, Deserialize)]
254pub struct LocalShellCall {
255	pub action: ShellAction,
256	pub call_id: String,
257	pub id: String,
258	pub status: String,
259}
260
261#[derive(Clone, Debug, Serialize, Deserialize)]
262pub struct ShellAction {
263	pub command: Vec<String>,
264	pub env: Value,
265	pub r#type: ConstExec,
266	#[serde(skip_serializing_if = "Option::is_none")]
267	pub timeout_ms: Option<u64>,
268	#[serde(skip_serializing_if = "Option::is_none")]
269	pub user: Option<String>,
270	#[serde(skip_serializing_if = "Option::is_none")]
271	pub working_directory: Option<String>,
272}
273
274impl_const_str! {
275	Exec  => "exec",
276}
277
278#[derive(Clone, Debug, Serialize, Deserialize)]
279pub struct McpListTools {
280	pub id: String,
281	pub server_label: String,
282	pub tools: Vec<ToolInfo>,
283	#[serde(skip_serializing_if = "Option::is_none")]
284	pub error: Option<String>,
285}
286
287#[derive(Clone, Debug, Serialize, Deserialize)]
288pub struct ToolInfo {
289	pub input_schema: Value,
290	pub name: String,
291	#[serde(skip_serializing_if = "Option::is_none")]
292	pub annotations: Option<Value>,
293	#[serde(skip_serializing_if = "Option::is_none")]
294	pub description: Option<String>,
295}
296
297#[derive(Clone, Debug, Serialize, Deserialize)]
298pub struct McpApprovalRequest {
299	pub arguments: Value,
300	pub id: String,
301	pub name: String,
302	pub server_label: String,
303}
304
305#[derive(Clone, Debug, Serialize, Deserialize)]
306pub struct McpCall {
307	pub arguments: Value,
308	pub id: String,
309	pub name: String,
310	pub server_label: String,
311	#[serde(skip_serializing_if = "Option::is_none")]
312	pub error: Option<String>,
313	#[serde(skip_serializing_if = "Option::is_none")]
314	pub output: Option<String>,
315}
316
317#[derive(Clone, Debug, Serialize, Deserialize)]
318pub struct Reasoning {
319	#[serde(skip_serializing_if = "Option::is_none")]
320	pub effort: Option<ReasoningEffort>,
321	#[serde(skip_serializing_if = "Option::is_none")]
322	pub summary: Option<Summary>,
323}
324
325impl_serializable_deserializable_enum! {
326	Summary {
327		Auto => "auto",
328		Concise => "concise",
329		Detailed => "detailed"
330	}
331}
332
333#[derive(Clone, Debug, Serialize, Deserialize)]
334pub struct Text {
335	#[serde(skip_serializing_if = "Option::is_none")]
336	pub format: Option<ResponseTextFormat>,
337}
338
339#[derive(Clone, Debug, Serialize, Deserialize)]
340#[serde(tag = "type", rename_all = "snake_case")]
341pub enum ResponseTextFormat {
342	Text,
343	JsonSchema {
344		name: String,
345		schema: Value,
346		#[serde(skip_serializing_if = "Option::is_none")]
347		description: Option<String>,
348		#[serde(skip_serializing_if = "Option::is_none")]
349		strict: Option<bool>,
350	},
351	JsonObject,
352}
353
354#[derive(Clone, Debug, Serialize, Deserialize)]
355#[serde(untagged)]
356pub enum ToolChoice {
357	Mode(ToolChoiceMode),
358	HostedTool { r#type: HostedTool },
359	FunctionTool { name: String, r#type: ConstFunction },
360}
361
362impl_serializable_deserializable_enum! {
363	ToolChoiceMode {
364		None => "none",
365		Auto => "auto",
366		Required => "required",
367	}
368}
369
370impl_serializable_deserializable_enum! {
371	HostedTool {
372		FileSearch => "file_search",
373		WebSearchPreview => "web_search_preview",
374		WebSearchPreview20250311 => "web_search_preview_2025_03_11",
375		ComputerUsePreview => "computer_use_preview",
376		CodeInterpreter => "code_interpreter",
377		Mcp => "mcp",
378		ImageGeneration => "image_generation",
379	}
380}
381
382#[derive(Clone, Debug, Serialize, Deserialize)]
383#[serde(tag = "type", rename_all = "snake_case")]
384pub enum Tool {
385	Function {
386		name: String,
387		parameters: Value,
388		strict: bool,
389		#[serde(skip_serializing_if = "Option::is_none")]
390		description: Option<String>,
391	},
392	FileSearch {
393		vector_store_ids: Vec<String>,
394		#[serde(skip_serializing_if = "Option::is_none")]
395		filters: Option<FileSearchFilters>,
396		#[serde(skip_serializing_if = "Option::is_none")]
397		max_num_results: Option<u32>,
398		#[serde(skip_serializing_if = "Option::is_none")]
399		ranking_options: Option<RankingOptions>,
400	},
401	WebSearchPreview {
402		#[serde(skip_serializing_if = "Option::is_none")]
403		search_context_size: Option<SearchContextSize>,
404		#[serde(skip_serializing_if = "Option::is_none")]
405		user_location: Option<Location>,
406	},
407	ComputerUsePreview {
408		display_height: u32,
409		display_width: u32,
410		environment: String,
411	},
412	Mcp {
413		server_label: String,
414		server_url: String,
415		#[serde(skip_serializing_if = "Option::is_none")]
416		allowed_tools: Option<Either<Vec<String>, McpFilter>>,
417		#[serde(skip_serializing_if = "Option::is_none")]
418		headers: Option<Value>,
419		#[serde(skip_serializing_if = "Option::is_none")]
420		require_approval: Option<Either<McpApprovalFilter, McpApprovalSetting>>,
421	},
422	CodeInterpreter {
423		container: Either<String, CodeInterpreterContainer>,
424	},
425	ImageGeneration {
426		#[serde(skip_serializing_if = "Option::is_none")]
427		background: Option<ImageBackground>,
428		#[serde(skip_serializing_if = "Option::is_none")]
429		input_image_mask: Option<InputImageMask>,
430		#[serde(skip_serializing_if = "Option::is_none")]
431		model: Option<Model>,
432		#[serde(skip_serializing_if = "Option::is_none")]
433		moderation: Option<String>,
434		#[serde(skip_serializing_if = "Option::is_none")]
435		output_compression: Option<u8>,
436		#[serde(skip_serializing_if = "Option::is_none")]
437		output_format: Option<ImageFormat>,
438		#[serde(skip_serializing_if = "Option::is_none")]
439		partial_images: Option<u8>,
440		#[serde(skip_serializing_if = "Option::is_none")]
441		quality: Option<ImageQuality>,
442		#[serde(skip_serializing_if = "Option::is_none")]
443		size: Option<ImageSize>,
444	},
445	LocalShell,
446}
447
448#[derive(Clone, Debug, Serialize, Deserialize)]
449#[serde(tag = "type", rename_all = "snake_case")]
450pub enum FileSearchFilters {
451	Eq(ComparisonFilter),
452	Ne(ComparisonFilter),
453	Gt(ComparisonFilter),
454	Gte(ComparisonFilter),
455	Lt(ComparisonFilter),
456	Lte(ComparisonFilter),
457	And { filters: Vec<FileSearchFilters> },
458	Or { filters: Vec<FileSearchFilters> },
459}
460
461#[derive(Clone, Debug, Serialize, Deserialize)]
462pub struct ComparisonFilter {
463	pub key: String,
464	pub value: ComparisonValue,
465}
466
467#[derive(Clone, Debug, Serialize, Deserialize)]
468#[serde(untagged)]
469pub enum ComparisonValue {
470	String(String),
471	Number(f64),
472	Boolean(bool),
473}
474
475#[derive(Clone, Debug, Serialize, Deserialize)]
476pub struct RankingOptions {
477	#[serde(skip_serializing_if = "Option::is_none")]
478	pub ranker: Option<String>,
479	#[serde(skip_serializing_if = "Option::is_none")]
480	pub score_threshold: Option<f32>,
481}
482
483impl_serializable_deserializable_enum! {
484	SearchContextSize {
485		Low => "low",
486		Medium => "medium",
487		High => "high",
488	}
489}
490
491#[derive(Clone, Debug, Serialize, Deserialize)]
492pub struct Location {
493	pub r#type: ConstApproximate,
494	#[serde(skip_serializing_if = "Option::is_none")]
495	pub city: Option<String>,
496	// TODO: Country code.
497	#[serde(skip_serializing_if = "Option::is_none")]
498	pub country: Option<String>,
499	#[serde(skip_serializing_if = "Option::is_none")]
500	pub region: Option<String>,
501	#[serde(skip_serializing_if = "Option::is_none")]
502	pub timezone: Option<String>,
503}
504
505impl_const_str! {
506	Approximate  => "approximate",
507}
508
509#[derive(Clone, Debug, Serialize, Deserialize)]
510pub struct McpFilter {
511	#[serde(skip_serializing_if = "Option::is_none")]
512	pub tool_names: Option<Vec<String>>,
513}
514
515#[derive(Clone, Debug, Serialize, Deserialize)]
516pub struct McpApprovalFilter {
517	#[serde(skip_serializing_if = "Option::is_none")]
518	pub always: Option<McpFilter>,
519	#[serde(skip_serializing_if = "Option::is_none")]
520	pub never: Option<McpFilter>,
521}
522
523impl_serializable_deserializable_enum! {
524	McpApprovalSetting {
525		Always => "always",
526		Never => "never",
527	}
528}
529
530#[derive(Clone, Debug, Serialize, Deserialize)]
531pub struct CodeInterpreterContainer {
532	pub r#type: ConstAuto,
533	#[serde(skip_serializing_if = "Option::is_none")]
534	pub file_ids: Option<Vec<String>>,
535}
536
537impl_const_str! {
538	Auto  => "auto",
539}
540
541impl_serializable_deserializable_enum! {
542	ImageBackground {
543		Transparent => "transparent",
544		Opaque => "opaque",
545		Auto => "auto",
546	}
547}
548
549#[derive(Clone, Debug, Serialize, Deserialize)]
550pub struct InputImageMask {
551	#[serde(skip_serializing_if = "Option::is_none")]
552	pub file_id: Option<String>,
553	#[serde(skip_serializing_if = "Option::is_none")]
554	pub image_url: Option<String>,
555}
556
557impl_serializable_deserializable_enum! {
558	ImageFormat {
559		Png => "png",
560		Webp => "webp",
561		Jpeg => "jpeg",
562	}
563}
564
565impl_serializable_deserializable_enum! {
566	ImageQuality {
567		Low => "low",
568		Medium => "medium",
569		High => "high",
570		Auto => "auto",
571	}
572}
573
574impl_serializable_deserializable_enum! {
575	ImageSize {
576		W1024H1024 => "1024x1024",
577		W1024H1536 => "1024x1536",
578		W1536H1024 => "1536x1024",
579		Auto => "auto",
580	}
581}
582
583impl_serializable_deserializable_enum! {
584	Truncation {
585		Auto => "auto",
586		Disabled => "disabled",
587	}
588}