openai_tools/responses/request.rs
1use crate::{
2 common::{
3 errors::{OpenAIToolError, Result},
4 message::Message,
5 structured_output::Schema,
6 tool::Tool,
7 },
8 responses::response::Response,
9};
10use derive_new::new;
11use dotenvy::dotenv;
12use request;
13use serde::{ser::SerializeStruct, Serialize};
14use std::collections::HashMap;
15use std::env;
16use strum::{Display, EnumString};
17
18/// Specifies additional data to include in the response output
19///
20/// This enum defines various types of additional information that can be
21/// included in the API response output, such as web search results, code
22/// interpreter outputs, image URLs, and other metadata.
23///
24/// # API Reference
25///
26/// Corresponds to the `include` parameter in the OpenAI Responses API:
27/// <https://platform.openai.com/docs/api-reference/responses/create>
28#[derive(Debug, Clone, EnumString, Display, Serialize, PartialEq)]
29#[serde(rename_all = "snake_case")]
30pub enum Include {
31 /// Include web search call results in the output
32 ///
33 /// When included, the response will contain information about web search
34 /// results that were used during the response generation process.
35 #[strum(serialize = "web_search_call.results")]
36 #[serde(rename = "web_search_call.results")]
37 WebSearchCall,
38
39 /// Include code interpreter call outputs in the output
40 ///
41 /// When included, the response will contain outputs from any code
42 /// that was executed during the response generation process.
43 #[strum(serialize = "code_interpreter_call.outputs")]
44 #[serde(rename = "code_interpreter_call.outputs")]
45 CodeInterpreterCall,
46
47 /// Include computer call output image URLs in the output
48 ///
49 /// When included, the response will contain image URLs from any
50 /// computer interaction calls that were made.
51 #[strum(serialize = "computer_call_output.output.image_url")]
52 #[serde(rename = "computer_call_output.output.image_url")]
53 ImageUrlInComputerCallOutput,
54
55 /// Include file search call results in the output
56 ///
57 /// When included, the response will contain results from any
58 /// file search operations that were performed.
59 #[strum(serialize = "file_search_call.results")]
60 #[serde(rename = "file_search_call.results")]
61 FileSearchCall,
62
63 /// Include image URLs from input messages in the output
64 ///
65 /// When included, the response will contain image URLs that were
66 /// present in the input messages.
67 #[strum(serialize = "message.input_image.image_url")]
68 #[serde(rename = "message.input_image.image_url")]
69 ImageUrlInInputMessages,
70
71 /// Include log probabilities in the output
72 ///
73 /// When included, the response will contain log probability information
74 /// for the generated text tokens.
75 #[strum(serialize = "message.output_text.logprobs")]
76 #[serde(rename = "message.output_text.logprobs")]
77 LogprobsInOutput,
78
79 /// Include reasoning encrypted content in the output
80 ///
81 /// When included, the response will contain encrypted reasoning
82 /// content that shows the model's internal reasoning process.
83 #[strum(serialize = "reasoning.encrypted_content")]
84 #[serde(rename = "reasoning.encrypted_content")]
85 ReasoningEncryptedContent,
86}
87
88/// Defines the level of reasoning effort the model should apply
89///
90/// This enum controls how much computational effort the model invests
91/// in reasoning through complex problems before generating a response.
92///
93/// # API Reference
94///
95/// Corresponds to the `reasoning.effort` parameter in the OpenAI Responses API.
96#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
97#[serde(rename_all = "snake_case")]
98pub enum ReasoningEffort {
99 /// Minimal reasoning effort - fastest response time
100 ///
101 /// Use this for simple queries that don't require deep analysis.
102 #[strum(serialize = "minimal")]
103 #[serde(rename = "minimal")]
104 Minimal,
105
106 /// Low reasoning effort - balanced performance
107 ///
108 /// Use this for moderately complex queries that benefit from some reasoning.
109 #[strum(serialize = "low")]
110 #[serde(rename = "low")]
111 Low,
112
113 /// Medium reasoning effort - comprehensive analysis
114 ///
115 /// Use this for complex queries that require thorough consideration.
116 #[strum(serialize = "medium")]
117 #[serde(rename = "medium")]
118 Medium,
119
120 /// High reasoning effort - maximum thoughtfulness
121 ///
122 /// Use this for very complex queries requiring deep, careful analysis.
123 #[strum(serialize = "high")]
124 #[serde(rename = "high")]
125 High,
126}
127
128/// Defines the format of reasoning summary to include in the response
129///
130/// This enum controls how the model's reasoning process is summarized
131/// and presented in the response output.
132///
133/// # API Reference
134///
135/// Corresponds to the `reasoning.summary` parameter in the OpenAI Responses API.
136#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
137#[serde(rename_all = "snake_case")]
138pub enum ReasoningSummary {
139 /// Automatically determine the appropriate summary format
140 ///
141 /// The model will choose the most suitable summary format based on the query.
142 #[strum(serialize = "auto")]
143 #[serde(rename = "auto")]
144 Auto,
145
146 /// Provide a concise summary of the reasoning process
147 ///
148 /// Use this for shorter, more focused reasoning explanations.
149 #[strum(serialize = "concise")]
150 #[serde(rename = "concise")]
151 Concise,
152
153 /// Provide a detailed summary of the reasoning process
154 ///
155 /// Use this for comprehensive reasoning explanations with full detail.
156 #[strum(serialize = "detailed")]
157 #[serde(rename = "detailed")]
158 Detailed,
159}
160
161/// Configuration for reasoning behavior in responses
162///
163/// This struct allows you to control how the model approaches reasoning
164/// for complex queries, including the effort level and summary format.
165///
166/// # API Reference
167///
168/// Corresponds to the `reasoning` parameter in the OpenAI Responses API.
169#[derive(Debug, Clone, Serialize)]
170pub struct Reasoning {
171 /// The level of reasoning effort to apply
172 pub effort: Option<ReasoningEffort>,
173 /// The format for the reasoning summary
174 pub summary: Option<ReasoningSummary>,
175}
176
177/// Defines how the model should choose and use tools
178///
179/// This enum controls the model's behavior regarding tool usage during
180/// response generation.
181///
182/// # API Reference
183///
184/// Corresponds to the `tool_choice` parameter in the OpenAI Responses API.
185#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
186#[serde(rename_all = "snake_case")]
187pub enum ToolChoiceMode {
188 /// Disable tool usage completely
189 ///
190 /// The model will not use any tools and will generate responses
191 /// based solely on its training data.
192 #[strum(serialize = "none")]
193 #[serde(rename = "none")]
194 None,
195
196 /// Automatically decide when to use tools
197 ///
198 /// The model will automatically determine when tools are needed
199 /// and which tools to use based on the query context.
200 #[strum(serialize = "auto")]
201 #[serde(rename = "auto")]
202 Auto,
203
204 /// Require the use of tools
205 ///
206 /// The model must use at least one of the provided tools in its response.
207 #[strum(serialize = "required")]
208 #[serde(rename = "required")]
209 Required,
210}
211
212/// Controls truncation behavior for long inputs
213///
214/// This enum defines how the system should handle inputs that exceed
215/// the maximum context length.
216///
217/// # API Reference
218///
219/// Corresponds to the `truncation` parameter in the OpenAI Responses API.
220#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
221#[serde(rename_all = "snake_case")]
222pub enum Truncation {
223 /// Automatically truncate inputs to fit context length
224 ///
225 /// The system will automatically trim inputs to ensure they fit
226 /// within the model's context window.
227 #[strum(serialize = "auto")]
228 #[serde(rename = "auto")]
229 Auto,
230
231 /// Disable truncation - return error if input is too long
232 ///
233 /// The system will return an error rather than truncating
234 /// inputs that exceed the context length.
235 #[strum(serialize = "disabled")]
236 #[serde(rename = "disabled")]
237 Disabled,
238}
239
240/// Options for streaming responses
241///
242/// This struct configures how streaming responses should behave,
243/// including whether to include obfuscated content.
244///
245/// # API Reference
246///
247/// Corresponds to the `stream_options` parameter in the OpenAI Responses API.
248#[derive(Debug, Clone, Serialize)]
249pub struct StreamOptions {
250 /// Whether to include obfuscated content in streaming responses
251 ///
252 /// When enabled, streaming responses may include placeholder or
253 /// obfuscated content that gets replaced as the real content is generated.
254 pub include_obfuscation: bool,
255}
256/// Represents the format configuration for structured output in responses
257///
258/// This struct is used to specify the schema format for structured text output
259/// when making requests to the OpenAI Responses API.
260#[derive(Debug, Clone, Default, Serialize, new)]
261pub struct Format {
262 /// The schema definition that specifies the structure of the expected output
263 pub format: Schema,
264}
265
266/// Represents the body of a request to the OpenAI Responses API
267///
268/// This struct contains all the parameters for making requests to the OpenAI Responses API.
269/// It supports both plain text and structured message input, along with extensive configuration
270/// options for tools, reasoning, output formatting, and response behavior.
271///
272/// # Required Parameters
273///
274/// - `model`: The ID of the model to use
275/// - Either `plain_text_input` OR `messages_input` (mutually exclusive)
276///
277/// # API Reference
278///
279/// Based on the OpenAI Responses API specification:
280/// <https://platform.openai.com/docs/api-reference/responses/create>
281///
282/// # Examples
283///
284/// ## Simple Text Input
285///
286/// ```rust
287/// use openai_tools::responses::request::Body;
288///
289/// let body = Body {
290/// model: "gpt-4".to_string(),
291/// plain_text_input: Some("What is the weather like?".to_string()),
292/// ..Default::default()
293/// };
294/// ```
295///
296/// ## With Messages and Tools
297///
298/// ```rust
299/// use openai_tools::responses::request::Body;
300/// use openai_tools::common::message::Message;
301/// use openai_tools::common::role::Role;
302///
303/// let messages = vec![
304/// Message::from_string(Role::User, "Help me with coding")
305/// ];
306///
307/// let body = Body {
308/// model: "gpt-4".to_string(),
309/// messages_input: Some(messages),
310/// instructions: Some("You are a helpful coding assistant".to_string()),
311/// max_output_tokens: Some(1000),
312/// ..Default::default()
313/// };
314/// ```
315#[derive(Debug, Clone, Default, new)]
316pub struct Body {
317 /// The ID of the model to use for generating responses
318 ///
319 /// Specifies which OpenAI model to use for response generation.
320 /// Common values include "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo".
321 ///
322 /// # Required
323 ///
324 /// This field is required for all requests.
325 ///
326 /// # Examples
327 ///
328 /// - `"gpt-4"` - Latest GPT-4 model
329 /// - `"gpt-4-turbo"` - GPT-4 Turbo for faster responses
330 /// - `"gpt-3.5-turbo"` - More cost-effective option
331 pub model: String,
332
333 /// Optional instructions to guide the model's behavior and response style
334 ///
335 /// Provides system-level instructions that define how the model should
336 /// behave, its personality, response format, or any other behavioral guidance.
337 ///
338 /// # Examples
339 ///
340 /// - `"You are a helpful assistant that provides concise answers"`
341 /// - `"Respond only with JSON formatted data"`
342 /// - `"Act as a professional code reviewer"`
343 pub instructions: Option<String>,
344
345 /// Plain text input for simple text-based requests
346 ///
347 /// Use this for straightforward text input when you don't need the structure
348 /// of messages with roles. This is mutually exclusive with `messages_input`.
349 ///
350 /// # Mutually Exclusive
351 ///
352 /// Cannot be used together with `messages_input`. Choose one based on your needs:
353 /// - Use `plain_text_input` for simple, single-turn interactions
354 /// - Use `messages_input` for conversation history or role-based interactions
355 ///
356 /// # Examples
357 ///
358 /// - `"What is the capital of France?"`
359 /// - `"Summarize this article: [article content]"`
360 /// - `"Write a haiku about programming"`
361 pub plain_text_input: Option<String>,
362
363 /// Structured message input for conversation-style interactions
364 ///
365 /// Use this when you need conversation history, different message roles
366 /// (user, assistant, system), or structured dialogue. This is mutually
367 /// exclusive with `plain_text_input`.
368 ///
369 /// # Mutually Exclusive
370 ///
371 /// Cannot be used together with `plain_text_input`.
372 ///
373 /// # Message Roles
374 ///
375 /// - `System`: Instructions for the model's behavior
376 /// - `User`: User input or questions
377 /// - `Assistant`: Previous model responses (for conversation history)
378 ///
379 /// # Examples
380 ///
381 /// ```rust
382 /// use openai_tools::common::message::Message;
383 /// use openai_tools::common::role::Role;
384 ///
385 /// let messages = vec![
386 /// Message::from_string(Role::System, "You are a helpful assistant"),
387 /// Message::from_string(Role::User, "Hello!"),
388 /// Message::from_string(Role::Assistant, "Hi there! How can I help you?"),
389 /// Message::from_string(Role::User, "What's 2+2?"),
390 /// ];
391 /// ```
392 pub messages_input: Option<Vec<Message>>,
393
394 /// Optional tools that the model can use during response generation
395 ///
396 /// Provides the model with access to external tools like web search,
397 /// code execution, file access, or custom functions. The model will
398 /// automatically decide when and how to use these tools based on the query.
399 ///
400 /// # Tool Types
401 ///
402 /// - Web search tools for finding current information
403 /// - Code interpreter for running and analyzing code
404 /// - File search tools for accessing document collections
405 /// - Custom function tools for specific business logic
406 ///
407 /// # Examples
408 ///
409 /// ```rust
410 /// use openai_tools::common::tool::Tool;
411 /// use openai_tools::common::parameters::ParameterProperty;
412 ///
413 /// let tools = vec![
414 /// Tool::function("search", "Search the web", Vec::<(&str, ParameterProperty)>::new(), false),
415 /// Tool::function("calculate", "Perform calculations", Vec::<(&str, ParameterProperty)>::new(), false),
416 /// ];
417 /// ```
418 pub tools: Option<Vec<Tool>>,
419 /// Optional tool choice configuration
420 // TODO: Implement ToolChoice
421 // pub tool_choice: Option<ToolChoice>,
422
423 /// Optional structured output format specification
424 ///
425 /// Defines the structure and format for the model's response output.
426 /// Use this when you need the response in a specific JSON schema format
427 /// or other structured format for programmatic processing.
428 ///
429 /// # Examples
430 ///
431 /// ```rust
432 /// use openai_tools::common::structured_output::Schema;
433 /// use openai_tools::responses::request::Format;
434 ///
435 /// let format = Format::new(Schema::responses_json_schema("response_schema"));
436 /// ```
437 pub structured_output: Option<Format>,
438
439 /// Optional sampling temperature for controlling response randomness
440 ///
441 /// Controls the randomness and creativity of the model's responses.
442 /// Higher values make the output more random and creative, while lower
443 /// values make it more focused, deterministic, and consistent.
444 ///
445 /// # Range
446 ///
447 /// - **Range**: 0.0 to 2.0
448 /// - **Default**: 1.0 (if not specified)
449 /// - **Minimum**: 0.0 (most deterministic, least creative)
450 /// - **Maximum**: 2.0 (most random, most creative)
451 ///
452 /// # Recommended Values
453 ///
454 /// - **0.0 - 0.3**: Highly focused and deterministic
455 /// - Best for: Factual questions, code generation, translations
456 /// - Behavior: Very consistent, predictable responses
457 ///
458 /// - **0.3 - 0.7**: Balanced creativity and consistency
459 /// - Best for: General conversation, explanations, analysis
460 /// - Behavior: Good balance between creativity and reliability
461 ///
462 /// - **0.7 - 1.2**: More creative and varied responses
463 /// - Best for: Creative writing, brainstorming, ideation
464 /// - Behavior: More diverse and interesting outputs
465 ///
466 /// - **1.2 - 2.0**: Highly creative and unpredictable
467 /// - Best for: Experimental creative tasks, humor, unconventional ideas
468 /// - Behavior: Very diverse but potentially less coherent
469 ///
470 /// # Usage Guidelines
471 ///
472 /// - **Start with 0.7** for most applications as a good default
473 /// - **Use 0.0-0.3** when you need consistent, reliable responses
474 /// - **Use 0.8-1.2** for creative tasks that still need coherence
475 /// - **Avoid values above 1.5** unless you specifically want very random outputs
476 ///
477 /// # API Reference
478 ///
479 /// Corresponds to the `temperature` parameter in the OpenAI Responses API:
480 /// <https://platform.openai.com/docs/api-reference/responses/create>
481 ///
482 /// # Examples
483 ///
484 /// ```rust
485 /// use openai_tools::responses::request::Responses;
486 ///
487 /// // Deterministic, factual responses
488 /// let mut client_factual = Responses::new();
489 /// client_factual.temperature(0.2);
490 ///
491 /// // Balanced creativity and consistency
492 /// let mut client_balanced = Responses::new();
493 /// client_balanced.temperature(0.7);
494 ///
495 /// // High creativity for brainstorming
496 /// let mut client_creative = Responses::new();
497 /// client_creative.temperature(1.1);
498 /// ```
499 pub temperature: Option<f64>,
500
501 /// Optional maximum number of tokens to generate in the response
502 ///
503 /// Controls the maximum length of the generated response. The actual response
504 /// may be shorter if the model naturally concludes or hits other stopping conditions.
505 ///
506 /// # Range
507 ///
508 /// - Minimum: 1
509 /// - Maximum: Depends on the model (typically 4096-8192 for most models)
510 ///
511 /// # Default Behavior
512 ///
513 /// If not specified, the model will use its default maximum output length.
514 ///
515 /// # Examples
516 ///
517 /// - `Some(100)` - Short responses, good for summaries or brief answers
518 /// - `Some(1000)` - Medium responses, suitable for detailed explanations
519 /// - `Some(4000)` - Long responses, for comprehensive analysis or long-form content
520 pub max_output_tokens: Option<usize>,
521
522 /// Optional maximum number of tool calls to make
523 ///
524 /// Limits how many tools the model can invoke during response generation.
525 /// This helps control cost and response time when using multiple tools.
526 ///
527 /// # Range
528 ///
529 /// - Minimum: 0 (no tool calls allowed)
530 /// - Maximum: Implementation-dependent
531 ///
532 /// # Use Cases
533 ///
534 /// - Set to `Some(1)` for single tool usage
535 /// - Set to `Some(0)` to disable tool usage entirely
536 /// - Leave as `None` for unlimited tool usage (subject to other constraints)
537 pub max_tool_calls: Option<usize>,
538
539 /// Optional metadata to include with the request
540 ///
541 /// Arbitrary key-value pairs that can be attached to the request for
542 /// tracking, logging, or passing additional context that doesn't affect
543 /// the model's behavior.
544 ///
545 /// # Common Use Cases
546 ///
547 /// - Request tracking: `{"request_id": "req_123", "user_id": "user_456"}`
548 /// - A/B testing: `{"experiment": "variant_a", "test_group": "control"}`
549 /// - Analytics: `{"session_id": "sess_789", "feature": "chat"}`
550 ///
551 /// # Examples
552 ///
553 /// ```rust
554 /// use std::collections::HashMap;
555 /// use serde_json::Value;
556 ///
557 /// let mut metadata = HashMap::new();
558 /// metadata.insert("user_id".to_string(), Value::String("user123".to_string()));
559 /// metadata.insert("session_id".to_string(), Value::String("sess456".to_string()));
560 /// metadata.insert("priority".to_string(), Value::Number(serde_json::Number::from(1)));
561 /// ```
562 pub metadata: Option<HashMap<String, serde_json::Value>>,
563
564 /// Optional flag to enable parallel tool calls
565 ///
566 /// When enabled, the model can make multiple tool calls simultaneously
567 /// rather than sequentially. This can significantly improve response time
568 /// when multiple independent tools need to be used.
569 ///
570 /// # Default
571 ///
572 /// If not specified, defaults to the model's default behavior (usually `true`).
573 ///
574 /// # When to Use
575 ///
576 /// - `Some(true)`: Enable when tools are independent and can run in parallel
577 /// - `Some(false)`: Disable when tools have dependencies or order matters
578 ///
579 /// # Examples
580 ///
581 /// - Weather + Stock prices: Can run in parallel (`true`)
582 /// - File read + File analysis: Should run sequentially (`false`)
583 pub parallel_tool_calls: Option<bool>,
584
585 /// Optional fields to include in the output
586 ///
587 /// Specifies additional metadata and information to include in the response
588 /// beyond the main generated content. This can include tool call details,
589 /// reasoning traces, log probabilities, and more.
590 ///
591 /// # Available Inclusions
592 ///
593 /// - Web search call sources and results
594 /// - Code interpreter execution outputs
595 /// - Image URLs from various sources
596 /// - Log probabilities for generated tokens
597 /// - Reasoning traces and encrypted content
598 ///
599 /// # Examples
600 ///
601 /// ```rust
602 /// use openai_tools::responses::request::Include;
603 ///
604 /// let includes = vec![
605 /// Include::WebSearchCall,
606 /// Include::LogprobsInOutput,
607 /// Include::ReasoningEncryptedContent,
608 /// ];
609 /// ```
610 pub include: Option<Vec<Include>>,
611
612 /// Optional flag to enable background processing
613 ///
614 /// When enabled, allows the request to be processed in the background,
615 /// potentially improving throughput for non-urgent requests.
616 ///
617 /// # Use Cases
618 ///
619 /// - `Some(true)`: Batch processing, non-interactive requests
620 /// - `Some(false)` or `None`: Real-time, interactive requests
621 ///
622 /// # Trade-offs
623 ///
624 /// - Background processing may have lower latency guarantees
625 /// - May be more cost-effective for bulk operations
626 /// - May have different rate limiting behavior
627 pub background: Option<bool>,
628
629 /// Optional conversation ID for tracking
630 ///
631 /// Identifier for grouping related requests as part of the same conversation
632 /// or session. This helps with context management and analytics.
633 ///
634 /// # Format
635 ///
636 /// Typically a UUID or other unique identifier string.
637 ///
638 /// # Examples
639 ///
640 /// - `Some("conv_123e4567-e89b-12d3-a456-426614174000".to_string())`
641 /// - `Some("user123_session456".to_string())`
642 pub conversation: Option<String>,
643
644 /// Optional ID of the previous response for context
645 ///
646 /// References a previous response in the same conversation to maintain
647 /// context and enable features like response chaining or follow-up handling.
648 ///
649 /// # Use Cases
650 ///
651 /// - Multi-turn conversations with context preservation
652 /// - Follow-up questions or clarifications
653 /// - Response refinement or iteration
654 ///
655 /// # Examples
656 ///
657 /// - `Some("resp_abc123def456".to_string())`
658 pub previous_response_id: Option<String>,
659
660 /// Optional reasoning configuration
661 ///
662 /// Controls how the model approaches complex reasoning tasks, including
663 /// the effort level and format of reasoning explanations.
664 ///
665 /// # Use Cases
666 ///
667 /// - Complex problem-solving requiring deep analysis
668 /// - Mathematical or logical reasoning tasks
669 /// - When you need insight into the model's reasoning process
670 ///
671 /// # Examples
672 ///
673 /// ```rust
674 /// use openai_tools::responses::request::{Reasoning, ReasoningEffort, ReasoningSummary};
675 ///
676 /// let reasoning = Reasoning {
677 /// effort: Some(ReasoningEffort::High),
678 /// summary: Some(ReasoningSummary::Detailed),
679 /// };
680 /// ```
681 pub reasoning: Option<Reasoning>,
682
683 /// Optional safety identifier
684 ///
685 /// Identifier for safety and content filtering configurations.
686 /// Used to specify which safety policies should be applied to the request.
687 ///
688 /// # Examples
689 ///
690 /// - `Some("strict".to_string())` - Apply strict content filtering
691 /// - `Some("moderate".to_string())` - Apply moderate content filtering
692 /// - `Some("permissive".to_string())` - Apply permissive content filtering
693 pub safety_identifier: Option<String>,
694
695 /// Optional service tier specification
696 ///
697 /// Specifies the service tier for the request, which may affect
698 /// processing priority, rate limits, and pricing.
699 ///
700 /// # Common Values
701 ///
702 /// - `Some("default".to_string())` - Standard service tier
703 /// - `Some("scale".to_string())` - High-throughput tier
704 /// - `Some("premium".to_string())` - Premium service tier with enhanced features
705 pub service_tier: Option<String>,
706
707 /// Optional flag to store the conversation
708 ///
709 /// When enabled, the conversation may be stored for future reference,
710 /// training, or analytics purposes (subject to privacy policies).
711 ///
712 /// # Privacy Considerations
713 ///
714 /// - `Some(true)`: Allow storage (check privacy policies)
715 /// - `Some(false)`: Explicitly opt-out of storage
716 /// - `None`: Use default storage policy
717 pub store: Option<bool>,
718
719 /// Optional flag to enable streaming responses
720 ///
721 /// When enabled, the response will be streamed back in chunks as it's
722 /// generated, allowing for real-time display of partial results.
723 ///
724 /// # Use Cases
725 ///
726 /// - `Some(true)`: Real-time chat interfaces, live text generation
727 /// - `Some(false)`: Batch processing, when you need the complete response
728 ///
729 /// # Considerations
730 ///
731 /// - Streaming responses require different handling in client code
732 /// - May affect some response features or formatting options
733 pub stream: Option<bool>,
734
735 /// Optional streaming configuration options
736 ///
737 /// Additional options for controlling streaming response behavior,
738 /// such as whether to include obfuscated placeholder content.
739 ///
740 /// # Only Relevant When Streaming
741 ///
742 /// This field is only meaningful when `stream` is `Some(true)`.
743 pub stream_options: Option<StreamOptions>,
744
745 /// Optional number of top log probabilities to include
746 ///
747 /// Specifies how many of the most likely alternative tokens to include
748 /// with their log probabilities for each generated token.
749 ///
750 /// # Range
751 ///
752 /// - Minimum: 0 (no log probabilities)
753 /// - Maximum: Model-dependent (typically 5-20)
754 ///
755 /// # Use Cases
756 ///
757 /// - Model analysis and debugging
758 /// - Confidence estimation
759 /// - Alternative response exploration
760 ///
761 /// # Examples
762 ///
763 /// - `Some(1)` - Include the top alternative for each token
764 /// - `Some(5)` - Include top 5 alternatives for detailed analysis
765 pub top_logprobs: Option<usize>,
766
767 /// Optional nucleus sampling parameter
768 ///
769 /// Controls the randomness of the model's responses by limiting the
770 /// cumulative probability of considered tokens.
771 ///
772 /// # Range
773 ///
774 /// - 0.0 to 1.0
775 /// - Lower values (e.g., 0.1) make responses more focused and deterministic
776 /// - Higher values (e.g., 0.9) make responses more diverse and creative
777 ///
778 /// # Default
779 ///
780 /// If not specified, uses the model's default value (typically around 1.0).
781 ///
782 /// # Examples
783 ///
784 /// - `Some(0.1)` - Very focused, deterministic responses
785 /// - `Some(0.7)` - Balanced creativity and focus
786 /// - `Some(0.95)` - High creativity and diversity
787 pub top_p: Option<f64>,
788
789 /// Optional truncation behavior configuration
790 ///
791 /// Controls how the system handles inputs that exceed the maximum
792 /// context length supported by the model.
793 ///
794 /// # Options
795 ///
796 /// - `Some(Truncation::Auto)` - Automatically truncate long inputs
797 /// - `Some(Truncation::Disabled)` - Return error for long inputs
798 /// - `None` - Use system default behavior
799 ///
800 /// # Use Cases
801 ///
802 /// - `Auto`: When you want to handle long documents gracefully
803 /// - `Disabled`: When you need to ensure complete input processing
804 pub truncation: Option<Truncation>,
805}
806
807impl Serialize for Body {
808 /// Custom serialization implementation for the request body
809 ///
810 /// This implementation handles the conversion of either plain text input
811 /// or messages input into the appropriate "input" field format required
812 /// by the OpenAI API. It also conditionally includes optional fields
813 /// like tools and text formatting.
814 ///
815 /// # Errors
816 ///
817 /// Returns a serialization error if neither plain_text_input nor
818 /// messages_input is set, as one of them is required.
819 fn serialize<S>(&self, serializer: S) -> anyhow::Result<S::Ok, S::Error>
820 where
821 S: serde::Serializer,
822 {
823 let mut state = serializer.serialize_struct("ResponsesBody", 4)?;
824 state.serialize_field("model", &self.model)?;
825
826 // Set input
827 if self.plain_text_input.is_some() {
828 state.serialize_field("input", &self.plain_text_input.clone().unwrap())?;
829 } else if self.messages_input.is_some() {
830 state.serialize_field("input", &self.messages_input.clone().unwrap())?;
831 } else {
832 return Err(serde::ser::Error::custom("Either plain_text_input or messages_input must be set."));
833 };
834
835 // Optional fields
836 if self.temperature.is_some() {
837 state.serialize_field("temperature", &self.temperature)?;
838 }
839 if self.instructions.is_some() {
840 state.serialize_field("instructions", &self.instructions)?;
841 }
842 if self.tools.is_some() {
843 state.serialize_field("tools", &self.tools)?;
844 }
845 if self.structured_output.is_some() {
846 state.serialize_field("text", &self.structured_output)?;
847 }
848 if self.max_output_tokens.is_some() {
849 state.serialize_field("max_output_tokens", &self.max_output_tokens)?;
850 }
851 if self.max_tool_calls.is_some() {
852 state.serialize_field("max_tool_calls", &self.max_tool_calls)?;
853 }
854 if self.metadata.is_some() {
855 state.serialize_field("metadata", &self.metadata)?;
856 }
857 if self.parallel_tool_calls.is_some() {
858 state.serialize_field("parallel_tool_calls", &self.parallel_tool_calls)?;
859 }
860 if self.include.is_some() {
861 state.serialize_field("include", &self.include)?;
862 }
863 if self.background.is_some() {
864 state.serialize_field("background", &self.background)?;
865 }
866 if self.conversation.is_some() {
867 state.serialize_field("conversation", &self.conversation)?;
868 }
869 if self.previous_response_id.is_some() {
870 state.serialize_field("previous_response_id", &self.previous_response_id)?;
871 }
872 if self.reasoning.is_some() {
873 state.serialize_field("reasoning", &self.reasoning)?;
874 }
875 if self.safety_identifier.is_some() {
876 state.serialize_field("safety_identifier", &self.safety_identifier)?;
877 }
878 if self.service_tier.is_some() {
879 state.serialize_field("service_tier", &self.service_tier)?;
880 }
881 if self.store.is_some() {
882 state.serialize_field("store", &self.store)?;
883 }
884 if self.stream.is_some() {
885 state.serialize_field("stream", &self.stream)?;
886 }
887 if self.stream_options.is_some() {
888 state.serialize_field("stream_options", &self.stream_options)?;
889 }
890 if self.top_logprobs.is_some() {
891 state.serialize_field("top_logprobs", &self.top_logprobs)?;
892 }
893 if self.top_p.is_some() {
894 state.serialize_field("top_p", &self.top_p)?;
895 }
896 if self.truncation.is_some() {
897 state.serialize_field("truncation", &self.truncation)?;
898 }
899 state.end()
900 }
901}
902
903/// Client for making requests to the OpenAI Responses API
904///
905/// This struct provides a convenient interface for building and executing requests
906/// to the OpenAI Responses API. It handles authentication, request formatting,
907/// and response parsing automatically.
908///
909/// # Examples
910///
911/// ```rust,no_run
912/// use openai_tools::responses::request::Responses;
913///
914/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
915/// let mut client = Responses::new();
916/// let response = client
917/// .model_id("gpt-4")
918/// .instructions("You are a helpful assistant.")
919/// .str_message("Hello, how are you?")
920/// .complete()
921/// .await?;
922/// # Ok(())
923/// # }
924/// ```
925#[derive(Debug, Clone, Default, Serialize)]
926pub struct Responses {
927 /// The API endpoint for the OpenAI Responses service
928 endpoint: String,
929 /// The OpenAI API key used for authentication
930 api_key: String,
931 /// The User-Agent string to include in requests
932 user_agent: String,
933 /// The request body containing all parameters for the API call
934 pub request_body: Body,
935}
936
937impl Responses {
938 /// Creates a new instance of the Responses client
939 ///
940 /// This method initializes a new client by loading the OpenAI API key from
941 /// the `OPENAI_API_KEY` environment variable. Make sure to set this environment
942 /// variable before calling this method.
943 ///
944 /// # Panics
945 ///
946 /// Panics if the `OPENAI_API_KEY` environment variable is not set.
947 pub fn new() -> Self {
948 dotenv().ok();
949 let api_key = env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY is not set.");
950 Self { endpoint: "https://api.openai.com/v1/responses".into(), api_key, user_agent: "".into(), request_body: Body::default() }
951 }
952
953 /// Creates a new instance of the Responses client with a custom endpoint
954 pub fn from_endpoint<T: AsRef<str>>(endpoint: T) -> Self {
955 dotenv().ok();
956 let api_key = env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY is not set.");
957 Self { endpoint: endpoint.as_ref().to_string(), api_key, user_agent: "".into(), request_body: Body::default() }
958 }
959
960 /// Sets the model ID for the request
961 ///
962 /// # Arguments
963 ///
964 /// * `model_id` - The ID of the model to use (e.g., "gpt-4", "gpt-3.5-turbo")
965 ///
966 /// # Returns
967 ///
968 /// A mutable reference to self for method chaining
969 pub fn model_id<T: AsRef<str>>(&mut self, model_id: T) -> &mut Self {
970 self.request_body.model = model_id.as_ref().to_string();
971 self
972 }
973
974 /// Sets the User-Agent string for the request
975 ///
976 /// # Arguments
977 ///
978 /// * `user_agent` - The User-Agent string to include in the request headers
979 ///
980 /// # Returns
981 ///
982 /// A mutable reference to self for method chaining
983 pub fn user_agent<T: AsRef<str>>(&mut self, user_agent: T) -> &mut Self {
984 self.user_agent = user_agent.as_ref().to_string();
985 self
986 }
987
988 /// Sets instructions to guide the model's behavior
989 ///
990 /// # Arguments
991 ///
992 /// * `instructions` - Instructions that define how the model should behave
993 ///
994 /// # Returns
995 ///
996 /// A mutable reference to self for method chaining
997 pub fn instructions<T: AsRef<str>>(&mut self, instructions: T) -> &mut Self {
998 self.request_body.instructions = Some(instructions.as_ref().to_string());
999 self
1000 }
1001
1002 /// Sets plain text input for simple text-based requests
1003 ///
1004 /// This method is mutually exclusive with `messages()`. Use this for simple
1005 /// text-based interactions where you don't need conversation history.
1006 ///
1007 /// # Arguments
1008 ///
1009 /// * `input` - The plain text input to send to the model
1010 ///
1011 /// # Returns
1012 ///
1013 /// A mutable reference to self for method chaining
1014 pub fn str_message<T: AsRef<str>>(&mut self, input: T) -> &mut Self {
1015 self.request_body.plain_text_input = Some(input.as_ref().to_string());
1016 self
1017 }
1018
1019 /// Sets structured message input for conversation-style interactions
1020 ///
1021 /// This method is mutually exclusive with `plain_text_input()`. Use this
1022 /// for complex conversations with message history and different roles.
1023 ///
1024 /// # Arguments
1025 ///
1026 /// * `messages` - A vector of messages representing the conversation history
1027 ///
1028 /// # Returns
1029 ///
1030 /// A mutable reference to self for method chaining
1031 pub fn messages(&mut self, messages: Vec<Message>) -> &mut Self {
1032 self.request_body.messages_input = Some(messages);
1033 self
1034 }
1035
1036 /// Sets tools that the model can use during response generation
1037 ///
1038 /// # Arguments
1039 ///
1040 /// * `tools` - A vector of tools available to the model
1041 ///
1042 /// # Returns
1043 ///
1044 /// A mutable reference to self for method chaining
1045 pub fn tools(&mut self, tools: Vec<Tool>) -> &mut Self {
1046 self.request_body.tools = Some(tools);
1047 self
1048 }
1049
1050 /// Sets structured output format specification
1051 ///
1052 /// This allows you to specify the exact format and structure of the
1053 /// model's response output.
1054 ///
1055 /// # Arguments
1056 ///
1057 /// * `text_format` - The schema defining the expected output structure
1058 ///
1059 /// # Returns
1060 ///
1061 /// A mutable reference to self for method chaining
1062 pub fn structured_output(&mut self, text_format: Schema) -> &mut Self {
1063 self.request_body.structured_output = Option::from(Format::new(text_format));
1064 self
1065 }
1066
1067 /// Sets the sampling temperature for controlling response randomness
1068 ///
1069 /// Controls the randomness and creativity of the model's responses.
1070 /// Higher values make the output more random and creative, while lower
1071 /// values make it more focused and deterministic.
1072 ///
1073 /// # Arguments
1074 ///
1075 /// * `temperature` - The temperature value (0.0 to 2.0)
1076 /// - 0.0: Most deterministic and focused responses
1077 /// - 1.0: Default balanced behavior
1078 /// - 2.0: Most random and creative responses
1079 ///
1080 /// # Panics
1081 ///
1082 /// This method will panic if the temperature value is outside the valid
1083 /// range of 0.0 to 2.0, as this would result in an API error.
1084 ///
1085 /// # Returns
1086 ///
1087 /// A mutable reference to self for method chaining
1088 ///
1089 /// # Examples
1090 ///
1091 /// ```rust
1092 /// use openai_tools::responses::request::Responses;
1093 ///
1094 /// // Deterministic responses for factual queries
1095 /// let mut client = Responses::new();
1096 /// client.temperature(0.2);
1097 ///
1098 /// // Creative responses for brainstorming
1099 /// let mut client = Responses::new();
1100 /// client.temperature(1.1);
1101 /// ```
1102 pub fn temperature(&mut self, temperature: f64) -> &mut Self {
1103 assert!((0.0..=2.0).contains(&temperature), "Temperature must be between 0.0 and 2.0, got {}", temperature);
1104 self.request_body.temperature = Some(temperature);
1105 self
1106 }
1107
1108 /// Sets the maximum number of tokens to generate in the response
1109 ///
1110 /// Controls the maximum length of the generated response. The actual response
1111 /// may be shorter if the model naturally concludes or hits other stopping conditions.
1112 ///
1113 /// # Arguments
1114 ///
1115 /// * `max_tokens` - Maximum number of tokens to generate (minimum: 1)
1116 ///
1117 /// # Returns
1118 ///
1119 /// A mutable reference to self for method chaining
1120 ///
1121 /// # Examples
1122 ///
1123 /// ```rust
1124 /// use openai_tools::responses::request::Responses;
1125 ///
1126 /// let mut client = Responses::new();
1127 /// client.max_output_tokens(100); // Limit response to 100 tokens
1128 /// ```
1129 pub fn max_output_tokens(&mut self, max_tokens: usize) -> &mut Self {
1130 self.request_body.max_output_tokens = Some(max_tokens);
1131 self
1132 }
1133
1134 /// Sets the maximum number of tool calls allowed during response generation
1135 ///
1136 /// Limits how many tools the model can invoke during response generation.
1137 /// This helps control cost and response time when using multiple tools.
1138 ///
1139 /// # Arguments
1140 ///
1141 /// * `max_tokens` - Maximum number of tool calls allowed (0 = no tool calls)
1142 ///
1143 /// # Returns
1144 ///
1145 /// A mutable reference to self for method chaining
1146 ///
1147 /// # Examples
1148 ///
1149 /// ```rust
1150 /// use openai_tools::responses::request::Responses;
1151 ///
1152 /// let mut client = Responses::new();
1153 /// client.max_tool_calls(3); // Allow up to 3 tool calls
1154 /// client.max_tool_calls(0); // Disable tool usage
1155 /// ```
1156 pub fn max_tool_calls(&mut self, max_tokens: usize) -> &mut Self {
1157 self.request_body.max_tool_calls = Some(max_tokens);
1158 self
1159 }
1160
1161 /// Adds or updates a metadata key-value pair for the request
1162 ///
1163 /// Metadata provides arbitrary key-value pairs that can be attached to the request
1164 /// for tracking, logging, or passing additional context that doesn't affect
1165 /// the model's behavior.
1166 ///
1167 /// # Arguments
1168 ///
1169 /// * `key` - The metadata key (string identifier)
1170 /// * `value` - The metadata value (can be string, number, boolean, etc.)
1171 ///
1172 /// # Behavior
1173 ///
1174 /// - If the key already exists, the old value is replaced with the new one
1175 /// - If metadata doesn't exist yet, a new metadata map is created
1176 /// - Values are stored as `serde_json::Value` for flexibility
1177 ///
1178 /// # Returns
1179 ///
1180 /// A mutable reference to self for method chaining
1181 ///
1182 /// # Examples
1183 ///
1184 /// ```rust
1185 /// use openai_tools::responses::request::Responses;
1186 /// use serde_json::Value;
1187 ///
1188 /// let mut client = Responses::new();
1189 /// client.metadata("user_id".to_string(), Value::String("user123".to_string()));
1190 /// client.metadata("priority".to_string(), Value::Number(serde_json::Number::from(1)));
1191 /// client.metadata("debug".to_string(), Value::Bool(true));
1192 /// ```
1193 pub fn metadata(&mut self, key: String, value: serde_json::Value) -> &mut Self {
1194 if self.request_body.metadata.is_none() {
1195 self.request_body.metadata = Some(HashMap::new());
1196 }
1197 if self.request_body.metadata.as_ref().unwrap().keys().any(|k| k == &key) {
1198 self.request_body.metadata.as_mut().unwrap().remove(&key);
1199 }
1200 self.request_body.metadata.as_mut().unwrap().insert(key, value);
1201 self
1202 }
1203
1204 /// Enables or disables parallel tool calls
1205 ///
1206 /// When enabled, the model can make multiple tool calls simultaneously
1207 /// rather than sequentially. This can significantly improve response time
1208 /// when multiple independent tools need to be used.
1209 ///
1210 /// # Arguments
1211 ///
1212 /// * `enable` - Whether to enable parallel tool calls
1213 /// - `true`: Tools can be called in parallel (faster for independent tools)
1214 /// - `false`: Tools are called sequentially (better for dependent operations)
1215 ///
1216 /// # Returns
1217 ///
1218 /// A mutable reference to self for method chaining
1219 ///
1220 /// # When to Use
1221 ///
1222 /// - **Enable (true)**: When tools are independent (e.g., weather + stock prices)
1223 /// - **Disable (false)**: When tools have dependencies (e.g., read file → analyze content)
1224 ///
1225 /// # Examples
1226 ///
1227 /// ```rust
1228 /// use openai_tools::responses::request::Responses;
1229 ///
1230 /// let mut client = Responses::new();
1231 /// client.parallel_tool_calls(true); // Enable parallel execution
1232 /// client.parallel_tool_calls(false); // Force sequential execution
1233 /// ```
1234 pub fn parallel_tool_calls(&mut self, enable: bool) -> &mut Self {
1235 self.request_body.parallel_tool_calls = Some(enable);
1236 self
1237 }
1238
1239 /// Specifies additional data to include in the response output
1240 ///
1241 /// Defines various types of additional information that can be included
1242 /// in the API response output, such as web search results, code interpreter
1243 /// outputs, image URLs, log probabilities, and reasoning traces.
1244 ///
1245 /// # Arguments
1246 ///
1247 /// * `includes` - A vector of `Include` enum values specifying what to include
1248 ///
1249 /// # Available Inclusions
1250 ///
1251 /// - `Include::WebSearchCall` - Web search results and sources
1252 /// - `Include::CodeInterpreterCall` - Code execution outputs
1253 /// - `Include::FileSearchCall` - File search operation results
1254 /// - `Include::LogprobsInOutput` - Token log probabilities
1255 /// - `Include::ReasoningEncryptedContent` - Reasoning process traces
1256 /// - `Include::ImageUrlInInputMessages` - Image URLs from input
1257 /// - `Include::ImageUrlInComputerCallOutput` - Computer interaction screenshots
1258 ///
1259 /// # Returns
1260 ///
1261 /// A mutable reference to self for method chaining
1262 ///
1263 /// # Examples
1264 ///
1265 /// ```rust
1266 /// use openai_tools::responses::request::{Responses, Include};
1267 ///
1268 /// let mut client = Responses::new();
1269 /// client.include(vec![
1270 /// Include::WebSearchCall,
1271 /// Include::LogprobsInOutput,
1272 /// Include::ReasoningEncryptedContent,
1273 /// ]);
1274 /// ```
1275 pub fn include(&mut self, includes: Vec<Include>) -> &mut Self {
1276 self.request_body.include = Some(includes);
1277 self
1278 }
1279
1280 /// Enables or disables background processing for the request
1281 ///
1282 /// When enabled, allows the request to be processed in the background,
1283 /// potentially improving throughput for non-urgent requests at the cost
1284 /// of potentially higher latency.
1285 ///
1286 /// # Arguments
1287 ///
1288 /// * `enable` - Whether to enable background processing
1289 /// - `true`: Process in background (lower priority, potentially longer latency)
1290 /// - `false`: Process with standard priority (default behavior)
1291 ///
1292 /// # Trade-offs
1293 ///
1294 /// - **Background processing**: Better for batch operations, non-interactive requests
1295 /// - **Standard processing**: Better for real-time, interactive applications
1296 ///
1297 /// # Returns
1298 ///
1299 /// A mutable reference to self for method chaining
1300 ///
1301 /// # Examples
1302 ///
1303 /// ```rust
1304 /// use openai_tools::responses::request::Responses;
1305 ///
1306 /// let mut client = Responses::new();
1307 /// client.background(true); // Enable background processing
1308 /// client.background(false); // Use standard processing
1309 /// ```
1310 pub fn background(&mut self, enable: bool) -> &mut Self {
1311 self.request_body.background = Some(enable);
1312 self
1313 }
1314
1315 /// Sets the conversation ID for grouping related requests
1316 ///
1317 /// Identifier for grouping related requests as part of the same conversation
1318 /// or session. This helps with context management, analytics, and conversation
1319 /// tracking across multiple API calls.
1320 ///
1321 /// # Arguments
1322 ///
1323 /// * `conversation_id` - The conversation identifier
1324 /// - Must start with "conv-" prefix according to API requirements
1325 /// - Should be a unique identifier (UUID recommended)
1326 ///
1327 /// # Returns
1328 ///
1329 /// A mutable reference to self for method chaining
1330 ///
1331 /// # Format Requirements
1332 ///
1333 /// The conversation ID must follow the format: `conv-{identifier}`
1334 ///
1335 /// # Examples
1336 ///
1337 /// ```rust
1338 /// use openai_tools::responses::request::Responses;
1339 ///
1340 /// let mut client = Responses::new();
1341 /// client.conversation("conv-123e4567-e89b-12d3-a456-426614174000");
1342 /// client.conversation("conv-user123-session456");
1343 /// ```
1344 pub fn conversation<T: AsRef<str>>(&mut self, conversation_id: T) -> &mut Self {
1345 self.request_body.conversation = Some(conversation_id.as_ref().to_string());
1346 self
1347 }
1348
1349 /// Sets the ID of the previous response for context continuation
1350 ///
1351 /// References a previous response in the same conversation to maintain
1352 /// context and enable features like response chaining, follow-up handling,
1353 /// or response refinement.
1354 ///
1355 /// # Arguments
1356 ///
1357 /// * `response_id` - The ID of the previous response to reference
1358 ///
1359 /// # Use Cases
1360 ///
1361 /// - **Multi-turn conversations**: Maintaining context across multiple exchanges
1362 /// - **Follow-up questions**: Building on previous responses
1363 /// - **Response refinement**: Iterating on or clarifying previous answers
1364 /// - **Context chaining**: Creating connected sequences of responses
1365 ///
1366 /// # Returns
1367 ///
1368 /// A mutable reference to self for method chaining
1369 ///
1370 /// # Examples
1371 ///
1372 /// ```rust
1373 /// use openai_tools::responses::request::Responses;
1374 ///
1375 /// let mut client = Responses::new();
1376 /// client.previous_response_id("resp_abc123def456");
1377 /// client.previous_response_id("response-uuid-here");
1378 /// ```
1379 pub fn previous_response_id<T: AsRef<str>>(&mut self, response_id: T) -> &mut Self {
1380 self.request_body.previous_response_id = Some(response_id.as_ref().to_string());
1381 self
1382 }
1383
1384 /// Configures reasoning behavior for complex problem-solving
1385 ///
1386 /// Controls how the model approaches complex reasoning tasks, including
1387 /// the computational effort level and format of reasoning explanations.
1388 /// This is particularly useful for mathematical, logical, or analytical tasks.
1389 ///
1390 /// # Arguments
1391 ///
1392 /// * `effort` - The level of reasoning effort to apply:
1393 /// - `ReasoningEffort::Minimal` - Fastest, for simple queries
1394 /// - `ReasoningEffort::Low` - Balanced, for moderate complexity
1395 /// - `ReasoningEffort::Medium` - Thorough, for complex queries
1396 /// - `ReasoningEffort::High` - Maximum analysis, for very complex problems
1397 ///
1398 /// * `summary` - The format for reasoning explanations:
1399 /// - `ReasoningSummary::Auto` - Let the model choose the format
1400 /// - `ReasoningSummary::Concise` - Brief, focused explanations
1401 /// - `ReasoningSummary::Detailed` - Comprehensive, step-by-step explanations
1402 ///
1403 /// # Returns
1404 ///
1405 /// A mutable reference to self for method chaining
1406 ///
1407 /// # Use Cases
1408 ///
1409 /// - Mathematical problem-solving with step-by-step explanations
1410 /// - Complex logical reasoning tasks
1411 /// - Analysis requiring deep consideration
1412 /// - Tasks where understanding the reasoning process is important
1413 ///
1414 /// # Examples
1415 ///
1416 /// ```rust
1417 /// use openai_tools::responses::request::{Responses, ReasoningEffort, ReasoningSummary};
1418 ///
1419 /// let mut client = Responses::new();
1420 ///
1421 /// // High effort with detailed explanations for complex problems
1422 /// client.reasoning(ReasoningEffort::High, ReasoningSummary::Detailed);
1423 ///
1424 /// // Medium effort with concise explanations for balanced approach
1425 /// client.reasoning(ReasoningEffort::Medium, ReasoningSummary::Concise);
1426 /// ```
1427 pub fn reasoning(&mut self, effort: ReasoningEffort, summary: ReasoningSummary) -> &mut Self {
1428 self.request_body.reasoning = Some(Reasoning { effort: Some(effort), summary: Some(summary) });
1429 self
1430 }
1431
1432 /// Sets the safety identifier for content filtering configuration
1433 ///
1434 /// Specifies which safety and content filtering policies should be applied
1435 /// to the request. Different safety levels provide varying degrees of content
1436 /// restriction and filtering.
1437 ///
1438 /// # Arguments
1439 ///
1440 /// * `safety_id` - The safety configuration identifier
1441 ///
1442 /// # Common Safety Levels
1443 ///
1444 /// - `"strict"` - Apply strict content filtering (highest safety)
1445 /// - `"moderate"` - Apply moderate content filtering (balanced approach)
1446 /// - `"permissive"` - Apply permissive content filtering (minimal restrictions)
1447 /// - `"default"` - Use system default safety settings
1448 ///
1449 /// # Returns
1450 ///
1451 /// A mutable reference to self for method chaining
1452 ///
1453 /// # Use Cases
1454 ///
1455 /// - Educational content requiring strict filtering
1456 /// - Business applications with moderate restrictions
1457 /// - Research applications needing broader content access
1458 ///
1459 /// # Examples
1460 ///
1461 /// ```rust
1462 /// use openai_tools::responses::request::Responses;
1463 ///
1464 /// let mut client = Responses::new();
1465 /// client.safety_identifier("strict"); // High safety for education
1466 /// client.safety_identifier("moderate"); // Balanced for general use
1467 /// client.safety_identifier("permissive"); // Minimal restrictions
1468 /// ```
1469 pub fn safety_identifier<T: AsRef<str>>(&mut self, safety_id: T) -> &mut Self {
1470 self.request_body.safety_identifier = Some(safety_id.as_ref().to_string());
1471 self
1472 }
1473
1474 /// Sets the service tier for request processing priority and features
1475 ///
1476 /// Specifies the service tier for the request, which affects processing
1477 /// priority, rate limits, pricing, and available features. Different tiers
1478 /// provide different levels of service quality and capabilities.
1479 ///
1480 /// # Arguments
1481 ///
1482 /// * `tier` - The service tier identifier
1483 ///
1484 /// # Common Service Tiers
1485 ///
1486 /// - `"default"` - Standard service tier with regular priority
1487 /// - `"scale"` - High-throughput tier optimized for bulk processing
1488 /// - `"premium"` - Premium service tier with enhanced features and priority
1489 /// - `"enterprise"` - Enterprise tier with dedicated resources
1490 ///
1491 /// # Returns
1492 ///
1493 /// A mutable reference to self for method chaining
1494 ///
1495 /// # Considerations
1496 ///
1497 /// - Higher tiers may have different pricing structures
1498 /// - Some features may only be available in certain tiers
1499 /// - Rate limits and quotas may vary by tier
1500 ///
1501 /// # Examples
1502 ///
1503 /// ```rust
1504 /// use openai_tools::responses::request::Responses;
1505 ///
1506 /// let mut client = Responses::new();
1507 /// client.service_tier("default"); // Standard service
1508 /// client.service_tier("scale"); // High-throughput processing
1509 /// client.service_tier("premium"); // Premium features and priority
1510 /// ```
1511 pub fn service_tier<T: AsRef<str>>(&mut self, tier: T) -> &mut Self {
1512 self.request_body.service_tier = Some(tier.as_ref().to_string());
1513 self
1514 }
1515
1516 /// Enables or disables conversation storage
1517 ///
1518 /// Controls whether the conversation may be stored for future reference,
1519 /// training, or analytics purposes. This setting affects data retention
1520 /// and privacy policies.
1521 ///
1522 /// # Arguments
1523 ///
1524 /// * `enable` - Whether to allow conversation storage
1525 /// - `true`: Allow storage for training, analytics, etc.
1526 /// - `false`: Explicitly opt-out of storage
1527 ///
1528 /// # Privacy Considerations
1529 ///
1530 /// - **Enabled storage**: Conversation may be retained according to service policies
1531 /// - **Disabled storage**: Request explicit deletion after processing
1532 /// - **Default behavior**: Varies by service configuration
1533 ///
1534 /// # Returns
1535 ///
1536 /// A mutable reference to self for method chaining
1537 ///
1538 /// # Use Cases
1539 ///
1540 /// - **Enable**: Contributing to model improvement, analytics
1541 /// - **Disable**: Sensitive data, privacy-critical applications
1542 ///
1543 /// # Examples
1544 ///
1545 /// ```rust
1546 /// use openai_tools::responses::request::Responses;
1547 ///
1548 /// let mut client = Responses::new();
1549 /// client.store(false); // Opt-out of storage for privacy
1550 /// client.store(true); // Allow storage for improvement
1551 /// ```
1552 pub fn store(&mut self, enable: bool) -> &mut Self {
1553 self.request_body.store = Some(enable);
1554 self
1555 }
1556
1557 /// Enables or disables streaming responses
1558 ///
1559 /// When enabled, the response will be streamed back in chunks as it's
1560 /// generated, allowing for real-time display of partial results instead
1561 /// of waiting for the complete response.
1562 ///
1563 /// # Arguments
1564 ///
1565 /// * `enable` - Whether to enable streaming
1566 /// - `true`: Stream response in real-time chunks
1567 /// - `false`: Wait for complete response before returning
1568 ///
1569 /// # Returns
1570 ///
1571 /// A mutable reference to self for method chaining
1572 ///
1573 /// # Use Cases
1574 ///
1575 /// - **Enable streaming**: Real-time chat interfaces, live text generation
1576 /// - **Disable streaming**: Batch processing, when complete response is needed
1577 ///
1578 /// # Implementation Notes
1579 ///
1580 /// - Streaming responses require different handling in client code
1581 /// - May affect some response features or formatting options
1582 /// - Typically used with `stream_options()` for additional configuration
1583 ///
1584 /// # Examples
1585 ///
1586 /// ```rust
1587 /// use openai_tools::responses::request::Responses;
1588 ///
1589 /// let mut client = Responses::new();
1590 /// client.stream(true); // Enable real-time streaming
1591 /// client.stream(false); // Wait for complete response
1592 /// ```
1593 pub fn stream(&mut self, enable: bool) -> &mut Self {
1594 self.request_body.stream = Some(enable);
1595 self
1596 }
1597
1598 /// Configures streaming response options
1599 ///
1600 /// Additional options for controlling streaming response behavior,
1601 /// such as whether to include obfuscated placeholder content during
1602 /// the streaming process.
1603 ///
1604 /// # Arguments
1605 ///
1606 /// * `include_obfuscation` - Whether to include obfuscated content
1607 /// - `true`: Include placeholder/obfuscated content in streams
1608 /// - `false`: Only include final, non-obfuscated content
1609 ///
1610 /// # Returns
1611 ///
1612 /// A mutable reference to self for method chaining
1613 ///
1614 /// # Relevance
1615 ///
1616 /// This setting is only meaningful when `stream(true)` is also set.
1617 /// It has no effect on non-streaming responses.
1618 ///
1619 /// # Use Cases
1620 ///
1621 /// - **Include obfuscation**: Better user experience with placeholder content
1622 /// - **Exclude obfuscation**: Cleaner streams with only final content
1623 ///
1624 /// # Examples
1625 ///
1626 /// ```rust
1627 /// use openai_tools::responses::request::Responses;
1628 ///
1629 /// let mut client = Responses::new();
1630 /// client.stream(true); // Enable streaming
1631 /// client.stream_options(true); // Include placeholder content
1632 /// client.stream_options(false); // Only final content
1633 /// ```
1634 pub fn stream_options(&mut self, include_obfuscation: bool) -> &mut Self {
1635 self.request_body.stream_options = Some(StreamOptions { include_obfuscation });
1636 self
1637 }
1638
1639 /// Sets the number of top log probabilities to include in the response
1640 ///
1641 /// Specifies how many of the most likely alternative tokens to include
1642 /// with their log probabilities for each generated token. This provides
1643 /// insight into the model's confidence and alternative choices.
1644 ///
1645 /// # Arguments
1646 ///
1647 /// * `n` - Number of top alternatives to include (typically 1-20)
1648 /// - `0`: No log probabilities included
1649 /// - `1-5`: Common range for most use cases
1650 /// - `>5`: Detailed analysis scenarios
1651 ///
1652 /// # Returns
1653 ///
1654 /// A mutable reference to self for method chaining
1655 ///
1656 /// # Use Cases
1657 ///
1658 /// - **Model analysis**: Understanding model decision-making
1659 /// - **Confidence estimation**: Measuring response certainty
1660 /// - **Alternative exploration**: Seeing what else the model considered
1661 /// - **Debugging**: Analyzing unexpected model behavior
1662 ///
1663 /// # Performance Note
1664 ///
1665 /// Higher values increase response size and may affect latency.
1666 ///
1667 /// # Examples
1668 ///
1669 /// ```rust
1670 /// use openai_tools::responses::request::Responses;
1671 ///
1672 /// let mut client = Responses::new();
1673 /// client.top_logprobs(1); // Include top alternative for each token
1674 /// client.top_logprobs(5); // Include top 5 alternatives (detailed analysis)
1675 /// client.top_logprobs(0); // No log probabilities
1676 /// ```
1677 pub fn top_logprobs(&mut self, n: usize) -> &mut Self {
1678 self.request_body.top_logprobs = Some(n);
1679 self
1680 }
1681
1682 /// Sets the nucleus sampling parameter for controlling response diversity
1683 ///
1684 /// Controls the randomness of the model's responses by limiting the
1685 /// cumulative probability of considered tokens. This is an alternative
1686 /// to temperature-based sampling that can provide more stable results.
1687 ///
1688 /// # Arguments
1689 ///
1690 /// * `p` - The nucleus sampling parameter (0.0 to 1.0)
1691 /// - `0.1`: Very focused, deterministic responses
1692 /// - `0.7`: Balanced creativity and focus (good default)
1693 /// - `0.9`: More diverse and creative responses
1694 /// - `1.0`: Consider all possible tokens (no truncation)
1695 ///
1696 /// # Returns
1697 ///
1698 /// A mutable reference to self for method chaining
1699 ///
1700 /// # How It Works
1701 ///
1702 /// The model considers only the tokens whose cumulative probability
1703 /// reaches the specified threshold, filtering out unlikely options.
1704 ///
1705 /// # Interaction with Temperature
1706 ///
1707 /// Can be used together with `temperature()` for fine-tuned control:
1708 /// - Low top_p + Low temperature = Very focused responses
1709 /// - High top_p + High temperature = Very creative responses
1710 ///
1711 /// # Examples
1712 ///
1713 /// ```rust
1714 /// use openai_tools::responses::request::Responses;
1715 ///
1716 /// let mut client = Responses::new();
1717 /// client.top_p(0.1); // Very focused responses
1718 /// client.top_p(0.7); // Balanced (recommended default)
1719 /// client.top_p(0.95); // High diversity
1720 /// ```
1721 pub fn top_p(&mut self, p: f64) -> &mut Self {
1722 self.request_body.top_p = Some(p);
1723 self
1724 }
1725
1726 /// Sets the truncation behavior for handling long inputs
1727 ///
1728 /// Controls how the system handles inputs that exceed the maximum
1729 /// context length supported by the model. This helps manage cases
1730 /// where input content is too large to process entirely.
1731 ///
1732 /// # Arguments
1733 ///
1734 /// * `truncation` - The truncation mode to use:
1735 /// - `Truncation::Auto`: Automatically truncate long inputs to fit
1736 /// - `Truncation::Disabled`: Return error if input exceeds context length
1737 ///
1738 /// # Returns
1739 ///
1740 /// A mutable reference to self for method chaining
1741 ///
1742 /// # Use Cases
1743 ///
1744 /// - **Auto truncation**: When you want to handle long documents gracefully
1745 /// - **Disabled truncation**: When you need to ensure complete input processing
1746 ///
1747 /// # Considerations
1748 ///
1749 /// - Auto truncation may remove important context from long inputs
1750 /// - Disabled truncation ensures complete processing but may cause errors
1751 /// - Consider breaking long inputs into smaller chunks when possible
1752 ///
1753 /// # Examples
1754 ///
1755 /// ```rust
1756 /// use openai_tools::responses::request::{Responses, Truncation};
1757 ///
1758 /// let mut client = Responses::new();
1759 /// client.truncation(Truncation::Auto); // Handle long inputs gracefully
1760 /// client.truncation(Truncation::Disabled); // Ensure complete processing
1761 /// ```
1762 pub fn truncation(&mut self, truncation: Truncation) -> &mut Self {
1763 self.request_body.truncation = Some(truncation);
1764 self
1765 }
1766
1767 /// Executes the request and returns the response
1768 ///
1769 /// This method sends the configured request to the OpenAI Responses API
1770 /// and returns the parsed response. It performs validation of required
1771 /// fields before sending the request.
1772 ///
1773 /// # Returns
1774 ///
1775 /// A `Result` containing the `Response` on success, or an `OpenAIToolError` on failure
1776 ///
1777 /// # Errors
1778 ///
1779 /// Returns an error if:
1780 /// - The API key is not set or is empty
1781 /// - The model ID is not set or is empty
1782 /// - Neither messages nor plain text input is provided
1783 /// - Both messages and plain text input are provided (mutually exclusive)
1784 /// - The HTTP request fails
1785 /// - The response cannot be parsed
1786 ///
1787 /// # Examples
1788 ///
1789 /// ```rust,no_run
1790 /// use openai_tools::responses::request::Responses;
1791 ///
1792 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1793 /// let mut client = Responses::new();
1794 /// let response = client
1795 /// .model_id("gpt-4")
1796 /// .str_message("Hello!")
1797 /// .complete()
1798 /// .await?;
1799 /// # Ok(())
1800 /// # }
1801 /// ```
1802 pub async fn complete(&self) -> Result<Response> {
1803 if self.api_key.is_empty() {
1804 return Err(OpenAIToolError::Error("API key is not set.".into()));
1805 }
1806 if self.request_body.model.is_empty() {
1807 return Err(OpenAIToolError::Error("Model ID is not set.".into()));
1808 }
1809 if self.request_body.messages_input.is_none() && self.request_body.plain_text_input.is_none() {
1810 return Err(OpenAIToolError::Error("Messages are not set.".into()));
1811 } else if self.request_body.plain_text_input.is_none() && self.request_body.messages_input.is_none() {
1812 return Err(OpenAIToolError::Error("Both plain text input and messages are set. Please use one of them.".into()));
1813 }
1814
1815 let body = serde_json::to_string(&self.request_body)?;
1816 let url = self.endpoint.clone();
1817
1818 let client = request::Client::new();
1819
1820 // Set up headers
1821 let mut header = request::header::HeaderMap::new();
1822 header.insert("Content-Type", request::header::HeaderValue::from_static("application/json"));
1823 header.insert("Authorization", request::header::HeaderValue::from_str(&format!("Bearer {}", self.api_key)).unwrap());
1824 if !self.user_agent.is_empty() {
1825 header.insert("User-Agent", request::header::HeaderValue::from_str(&self.user_agent).unwrap());
1826 }
1827
1828 if cfg!(test) {
1829 tracing::info!("Endpoint: {}", self.endpoint);
1830 // Replace API key with a placeholder for security
1831 let body_for_debug = serde_json::to_string_pretty(&self.request_body).unwrap().replace(&self.api_key, "*************");
1832 // Log the request body for debugging purposes
1833 tracing::info!("Request body: {}", body_for_debug);
1834 }
1835
1836 // Send the request and handle the response
1837 match client.post(url).headers(header).body(body).send().await.map_err(OpenAIToolError::RequestError) {
1838 Err(e) => {
1839 tracing::error!("Request error: {}", e);
1840 return Err(e);
1841 }
1842 Ok(response) if !response.status().is_success() => {
1843 let status = response.status();
1844 let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
1845 tracing::error!("API error (status: {}): {}", status, error_text);
1846 return Err(OpenAIToolError::Error(format!("API request failed with status {}: {}", status, error_text)));
1847 }
1848 Ok(response) => {
1849 let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
1850
1851 if cfg!(test) {
1852 tracing::info!("Response content: {}", content);
1853 }
1854
1855 let data = serde_json::from_str::<Response>(&content).map_err(OpenAIToolError::SerdeJsonError);
1856 return data;
1857 }
1858 }
1859 }
1860}