Skip to main content

openai_tools/responses/
request.rs

1use crate::{
2    common::{
3        auth::{AuthProvider, OpenAIAuth},
4        client::create_http_client,
5        errors::{OpenAIToolError, Result},
6        message::Message,
7        models::{ChatModel, ParameterRestriction},
8        structured_output::Schema,
9        tool::Tool,
10    },
11    responses::response::{CompactedResponse, DeleteResponseResult, InputItemsListResponse, InputTokensResponse, Response},
12};
13use derive_new::new;
14use request;
15use serde::{ser::SerializeStruct, Serialize};
16use std::collections::HashMap;
17use std::time::Duration;
18use strum::{Display, EnumString};
19
20/// Specifies additional data to include in the response output
21///
22/// This enum defines various types of additional information that can be
23/// included in the API response output, such as web search results, code
24/// interpreter outputs, image URLs, and other metadata.
25///
26/// # API Reference
27///
28/// Corresponds to the `include` parameter in the OpenAI Responses API:
29/// <https://platform.openai.com/docs/api-reference/responses/create>
30#[derive(Debug, Clone, EnumString, Display, Serialize, PartialEq)]
31#[serde(rename_all = "snake_case")]
32#[non_exhaustive]
33pub enum Include {
34    /// Include web search call results in the output  
35    ///
36    /// When included, the response will contain information about web search
37    /// results that were used during the response generation process.
38    #[strum(serialize = "web_search_call.results")]
39    #[serde(rename = "web_search_call.results")]
40    WebSearchCall,
41
42    /// Include code interpreter call outputs in the output
43    ///
44    /// When included, the response will contain outputs from any code
45    /// that was executed during the response generation process.
46    #[strum(serialize = "code_interpreter_call.outputs")]
47    #[serde(rename = "code_interpreter_call.outputs")]
48    CodeInterpreterCall,
49
50    /// Include computer call output image URLs in the output
51    ///
52    /// When included, the response will contain image URLs from any
53    /// computer interaction calls that were made.
54    #[strum(serialize = "computer_call_output.output.image_url")]
55    #[serde(rename = "computer_call_output.output.image_url")]
56    ImageUrlInComputerCallOutput,
57
58    /// Include file search call results in the output
59    ///
60    /// When included, the response will contain results from any
61    /// file search operations that were performed.
62    #[strum(serialize = "file_search_call.results")]
63    #[serde(rename = "file_search_call.results")]
64    FileSearchCall,
65
66    /// Include image URLs from input messages in the output
67    ///
68    /// When included, the response will contain image URLs that were
69    /// present in the input messages.
70    #[strum(serialize = "message.input_image.image_url")]
71    #[serde(rename = "message.input_image.image_url")]
72    ImageUrlInInputMessages,
73
74    /// Include log probabilities in the output
75    ///
76    /// When included, the response will contain log probability information
77    /// for the generated text tokens.
78    #[strum(serialize = "message.output_text.logprobs")]
79    #[serde(rename = "message.output_text.logprobs")]
80    LogprobsInOutput,
81
82    /// Include reasoning encrypted content in the output
83    ///
84    /// When included, the response will contain encrypted reasoning
85    /// content that shows the model's internal reasoning process.
86    #[strum(serialize = "reasoning.encrypted_content")]
87    #[serde(rename = "reasoning.encrypted_content")]
88    ReasoningEncryptedContent,
89}
90
91/// Defines the level of reasoning effort the model should apply
92///
93/// This enum controls how much computational effort the model invests
94/// in reasoning through complex problems before generating a response.
95///
96/// # Model Support
97///
98/// | Model | Supported Values |
99/// |-------|-----------------|
100/// | GPT-6 Astra | `low`, `medium`, `high`, `xhigh`, `max` |
101/// | GPT-6 Sol, GPT-6 Luna | `none`, `low`, `medium`, `high`, `xhigh`, `max` |
102/// | GPT-5.2, GPT-5.2-pro | `none`, `low`, `medium`, `high`, `xhigh` |
103/// | GPT-5.1 | `none`, `low`, `medium`, `high` |
104/// | GPT-5-mini | `minimal`, `medium`, `high` |
105/// | o1, o3, o4 series | `low`, `medium`, `high` |
106///
107/// # API Reference
108///
109/// Corresponds to the `reasoning.effort` parameter in the OpenAI Responses API.
110#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
111#[serde(rename_all = "snake_case")]
112#[non_exhaustive]
113pub enum ReasoningEffort {
114    /// No reasoning tokens - fastest response (GPT-5.1/5.2 default)
115    ///
116    /// Use this when you don't need reasoning capabilities and want
117    /// the fastest possible response time.
118    #[strum(serialize = "none")]
119    #[serde(rename = "none")]
120    None,
121
122    /// Minimal reasoning effort - fastest response time
123    ///
124    /// Use this for simple queries that don't require deep analysis.
125    #[strum(serialize = "minimal")]
126    #[serde(rename = "minimal")]
127    Minimal,
128
129    /// Low reasoning effort - balanced performance
130    ///
131    /// Use this for moderately complex queries that benefit from some reasoning.
132    #[strum(serialize = "low")]
133    #[serde(rename = "low")]
134    Low,
135
136    /// Medium reasoning effort - comprehensive analysis
137    ///
138    /// Use this for complex queries that require thorough consideration.
139    #[strum(serialize = "medium")]
140    #[serde(rename = "medium")]
141    Medium,
142
143    /// High reasoning effort - maximum thoughtfulness
144    ///
145    /// Use this for very complex queries requiring deep, careful analysis.
146    #[strum(serialize = "high")]
147    #[serde(rename = "high")]
148    High,
149
150    /// Extra-high reasoning effort - quality-critical work (GPT-5.2 only)
151    ///
152    /// Use this for the most demanding tasks requiring maximum reasoning
153    /// quality. Only available on GPT-5.2 and GPT-5.2-pro models.
154    #[strum(serialize = "xhigh")]
155    #[serde(rename = "xhigh")]
156    Xhigh,
157
158    /// Maximum reasoning effort for GPT-6 models
159    ///
160    /// Responses API only. Accepted by GPT-6 Astra, GPT-6 Sol, and GPT-6 Luna
161    /// (`gpt-6-astra`, `gpt-6-sol`, and `gpt-6-luna`). Chat Completions rejects
162    /// `max` in its `reasoning_effort` parameter, including for GPT-6 models.
163    #[strum(serialize = "max")]
164    #[serde(rename = "max")]
165    Max,
166}
167
168/// Defines the format of reasoning summary to include in the response
169///
170/// This enum controls how the model's reasoning process is summarized
171/// and presented in the response output.
172///
173/// # API Reference
174///
175/// Corresponds to the `reasoning.summary` parameter in the OpenAI Responses API.
176#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
177#[serde(rename_all = "snake_case")]
178#[non_exhaustive]
179pub enum ReasoningSummary {
180    /// Automatically determine the appropriate summary format
181    ///
182    /// The model will choose the most suitable summary format based on the query.
183    #[strum(serialize = "auto")]
184    #[serde(rename = "auto")]
185    Auto,
186
187    /// Provide a concise summary of the reasoning process
188    ///
189    /// Use this for shorter, more focused reasoning explanations.
190    #[strum(serialize = "concise")]
191    #[serde(rename = "concise")]
192    Concise,
193
194    /// Provide a detailed summary of the reasoning process
195    ///
196    /// Use this for comprehensive reasoning explanations with full detail.
197    #[strum(serialize = "detailed")]
198    #[serde(rename = "detailed")]
199    Detailed,
200}
201
202/// Configuration for reasoning behavior in responses
203///
204/// This struct allows you to control how the model approaches reasoning
205/// for complex queries, including the effort level and summary format.
206///
207/// # API Reference
208///
209/// Corresponds to the `reasoning` parameter in the OpenAI Responses API.
210#[derive(Debug, Clone, Serialize)]
211pub struct Reasoning {
212    /// The level of reasoning effort to apply
213    pub effort: Option<ReasoningEffort>,
214    /// The format for the reasoning summary
215    pub summary: Option<ReasoningSummary>,
216}
217
218/// Defines the verbosity level for text output
219///
220/// This enum controls how detailed and lengthy the model's text responses
221/// should be. Available on GPT-5.2 and newer models.
222///
223/// # API Reference
224///
225/// Corresponds to the `text.verbosity` parameter in the OpenAI Responses API.
226#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
227#[serde(rename_all = "snake_case")]
228#[non_exhaustive]
229pub enum TextVerbosity {
230    /// Low verbosity - concise responses
231    ///
232    /// Use this for brief, to-the-point answers.
233    #[strum(serialize = "low")]
234    #[serde(rename = "low")]
235    Low,
236
237    /// Medium verbosity - balanced responses (default)
238    ///
239    /// Use this for standard-length responses with appropriate detail.
240    #[strum(serialize = "medium")]
241    #[serde(rename = "medium")]
242    Medium,
243
244    /// High verbosity - comprehensive responses
245    ///
246    /// Use this for detailed explanations with thorough coverage.
247    #[strum(serialize = "high")]
248    #[serde(rename = "high")]
249    High,
250}
251
252/// Configuration for text output behavior
253///
254/// This struct allows you to control the characteristics of the generated
255/// text output, such as verbosity level.
256///
257/// # API Reference
258///
259/// Corresponds to the `text` parameter in the OpenAI Responses API.
260#[derive(Debug, Clone, Serialize)]
261pub struct TextConfig {
262    /// The verbosity level for text output
263    pub verbosity: Option<TextVerbosity>,
264}
265
266/// Defines how the model should choose and use tools
267///
268/// This enum controls the model's behavior regarding tool usage during
269/// response generation.
270///
271/// # API Reference
272///
273/// Corresponds to the `tool_choice` parameter in the OpenAI Responses API.
274#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
275#[serde(rename_all = "snake_case")]
276pub enum ToolChoiceMode {
277    /// Disable tool usage completely
278    ///
279    /// The model will not use any tools and will generate responses
280    /// based solely on its training data.
281    #[strum(serialize = "none")]
282    #[serde(rename = "none")]
283    None,
284
285    /// Automatically decide when to use tools
286    ///
287    /// The model will automatically determine when tools are needed
288    /// and which tools to use based on the query context.
289    #[strum(serialize = "auto")]
290    #[serde(rename = "auto")]
291    Auto,
292
293    /// Require the use of tools
294    ///
295    /// The model must use at least one of the provided tools in its response.
296    #[strum(serialize = "required")]
297    #[serde(rename = "required")]
298    Required,
299}
300
301/// Controls truncation behavior for long inputs
302///
303/// This enum defines how the system should handle inputs that exceed
304/// the maximum context length.
305///
306/// # API Reference
307///
308/// Corresponds to the `truncation` parameter in the OpenAI Responses API.
309#[derive(Debug, Clone, Serialize, EnumString, Display, PartialEq)]
310#[serde(rename_all = "snake_case")]
311pub enum Truncation {
312    /// Automatically truncate inputs to fit context length
313    ///
314    /// The system will automatically trim inputs to ensure they fit
315    /// within the model's context window.
316    #[strum(serialize = "auto")]
317    #[serde(rename = "auto")]
318    Auto,
319
320    /// Disable truncation - return error if input is too long
321    ///
322    /// The system will return an error rather than truncating
323    /// inputs that exceed the context length.
324    #[strum(serialize = "disabled")]
325    #[serde(rename = "disabled")]
326    Disabled,
327}
328
329/// Specifies a specific function to force the model to call
330///
331/// When you want the model to call a specific function rather than
332/// choosing which tool to use, use this structure to specify the
333/// function name.
334///
335/// # Example
336///
337/// ```rust
338/// use openai_tools::responses::request::NamedFunctionChoice;
339///
340/// let function_choice = NamedFunctionChoice::new("get_weather");
341/// ```
342#[derive(Debug, Clone, Serialize)]
343pub struct NamedFunctionChoice {
344    /// The type of tool choice, always "function" for named functions
345    #[serde(rename = "type")]
346    pub type_name: String,
347    /// The name of the function to call
348    pub name: String,
349}
350
351impl NamedFunctionChoice {
352    /// Creates a new NamedFunctionChoice for the specified function
353    ///
354    /// # Arguments
355    ///
356    /// * `name` - The name of the function to call
357    ///
358    /// # Returns
359    ///
360    /// A new NamedFunctionChoice instance
361    pub fn new<S: AsRef<str>>(name: S) -> Self {
362        Self { type_name: "function".to_string(), name: name.as_ref().to_string() }
363    }
364}
365
366/// Controls how the model selects tools
367///
368/// This enum allows you to specify whether the model should automatically
369/// choose tools, be forced to use specific tools, or be prevented from
370/// using tools altogether.
371///
372/// # API Reference
373///
374/// Corresponds to the `tool_choice` parameter in the OpenAI Responses API:
375/// <https://platform.openai.com/docs/api-reference/responses/create>
376///
377/// # Examples
378///
379/// ```rust
380/// use openai_tools::responses::request::{ToolChoice, ToolChoiceMode, NamedFunctionChoice};
381///
382/// // Let the model decide
383/// let auto_choice = ToolChoice::Simple(ToolChoiceMode::Auto);
384///
385/// // Force a specific function
386/// let function_choice = ToolChoice::Function(NamedFunctionChoice::new("get_weather"));
387/// ```
388#[derive(Debug, Clone, Serialize)]
389#[serde(untagged)]
390pub enum ToolChoice {
391    /// Simple mode selection (auto, none, required)
392    Simple(ToolChoiceMode),
393    /// Force a specific function to be called
394    Function(NamedFunctionChoice),
395}
396
397/// Reference to a prompt template with variables
398///
399/// Allows you to use pre-defined prompt templates stored in the OpenAI
400/// platform, optionally with variable substitution.
401///
402/// # API Reference
403///
404/// Corresponds to the `prompt` parameter in the OpenAI Responses API:
405/// <https://platform.openai.com/docs/api-reference/responses/create>
406///
407/// # Examples
408///
409/// ```rust
410/// use openai_tools::responses::request::Prompt;
411/// use std::collections::HashMap;
412///
413/// // Simple prompt reference
414/// let prompt = Prompt::new("prompt-abc123");
415///
416/// // Prompt with variables
417/// let mut vars = HashMap::new();
418/// vars.insert("name".to_string(), "Alice".to_string());
419/// let prompt = Prompt::with_variables("prompt-abc123", vars);
420/// ```
421#[derive(Debug, Clone, Serialize)]
422pub struct Prompt {
423    /// The ID of the prompt template
424    pub id: String,
425    /// Optional variables to substitute in the template
426    #[serde(skip_serializing_if = "Option::is_none")]
427    pub variables: Option<HashMap<String, String>>,
428}
429
430impl Prompt {
431    /// Creates a new Prompt reference without variables
432    ///
433    /// # Arguments
434    ///
435    /// * `id` - The ID of the prompt template
436    ///
437    /// # Returns
438    ///
439    /// A new Prompt instance
440    pub fn new<S: AsRef<str>>(id: S) -> Self {
441        Self { id: id.as_ref().to_string(), variables: None }
442    }
443
444    /// Creates a new Prompt reference with variables
445    ///
446    /// # Arguments
447    ///
448    /// * `id` - The ID of the prompt template
449    /// * `variables` - Variables to substitute in the template
450    ///
451    /// # Returns
452    ///
453    /// A new Prompt instance with variables
454    pub fn with_variables<S: AsRef<str>>(id: S, variables: HashMap<String, String>) -> Self {
455        Self { id: id.as_ref().to_string(), variables: Some(variables) }
456    }
457}
458
459/// Options for streaming responses
460///
461/// This struct configures how streaming responses should behave,
462/// including whether to include obfuscated content.
463///
464/// # API Reference
465///
466/// Corresponds to the `stream_options` parameter in the OpenAI Responses API.
467#[derive(Debug, Clone, Serialize)]
468pub struct StreamOptions {
469    /// Whether to include obfuscated content in streaming responses
470    ///
471    /// When enabled, streaming responses may include placeholder or
472    /// obfuscated content that gets replaced as the real content is generated.
473    pub include_obfuscation: bool,
474}
475/// Represents the format configuration for structured output in responses
476///
477/// This struct is used to specify the schema format for structured text output
478/// when making requests to the OpenAI Responses API.
479#[derive(Debug, Clone, Default, Serialize, new)]
480pub struct Format {
481    /// The schema definition that specifies the structure of the expected output
482    pub format: Schema,
483}
484
485/// Represents the body of a request to the OpenAI Responses API
486///
487/// This struct contains all the parameters for making requests to the OpenAI Responses API.
488/// It supports both plain text and structured message input, along with extensive configuration
489/// options for tools, reasoning, output formatting, and response behavior.
490///
491/// # Required Parameters
492///
493/// - `model`: The ID of the model to use
494/// - Either `plain_text_input` OR `messages_input` (mutually exclusive)
495///
496/// # API Reference
497///
498/// Based on the OpenAI Responses API specification:
499/// <https://platform.openai.com/docs/api-reference/responses/create>
500///
501/// # Examples
502///
503/// ## Simple Text Input
504///
505/// ```rust
506/// use openai_tools::responses::request::Body;
507/// use openai_tools::common::models::ChatModel;
508///
509/// let body = Body {
510///     model: ChatModel::Gpt4o,
511///     plain_text_input: Some("What is the weather like?".to_string()),
512///     ..Default::default()
513/// };
514/// ```
515///
516/// ## With Messages and Tools
517///
518/// ```rust
519/// use openai_tools::responses::request::Body;
520/// use openai_tools::common::message::Message;
521/// use openai_tools::common::role::Role;
522/// use openai_tools::common::models::ChatModel;
523///
524/// let messages = vec![
525///     Message::from_string(Role::User, "Help me with coding")
526/// ];
527///
528/// let body = Body {
529///     model: ChatModel::Gpt4o,
530///     messages_input: Some(messages),
531///     instructions: Some("You are a helpful coding assistant".to_string()),
532///     max_output_tokens: Some(1000),
533///     ..Default::default()
534/// };
535/// ```
536#[derive(Debug, Clone, Default, new)]
537#[allow(clippy::too_many_arguments)]
538pub struct Body {
539    /// The model to use for generating responses
540    ///
541    /// Specifies which OpenAI model to use for response generation.
542    ///
543    /// # Required
544    ///
545    /// This field is required for all requests.
546    ///
547    /// # Examples
548    ///
549    /// - `ChatModel::Gpt4o` - Latest GPT-4o model
550    /// - `ChatModel::Gpt4oMini` - Cost-effective option
551    /// - `ChatModel::O3Mini` - Reasoning model
552    pub model: ChatModel,
553
554    /// Optional instructions to guide the model's behavior and response style
555    ///
556    /// Provides system-level instructions that define how the model should
557    /// behave, its personality, response format, or any other behavioral guidance.
558    ///
559    /// # Examples
560    ///
561    /// - `"You are a helpful assistant that provides concise answers"`
562    /// - `"Respond only with JSON formatted data"`
563    /// - `"Act as a professional code reviewer"`
564    pub instructions: Option<String>,
565
566    /// Plain text input for simple text-based requests
567    ///
568    /// Use this for straightforward text input when you don't need the structure
569    /// of messages with roles. This is mutually exclusive with `messages_input`.
570    ///
571    /// # Mutually Exclusive
572    ///
573    /// Cannot be used together with `messages_input`. Choose one based on your needs:
574    /// - Use `plain_text_input` for simple, single-turn interactions
575    /// - Use `messages_input` for conversation history or role-based interactions
576    ///
577    /// # Examples
578    ///
579    /// - `"What is the capital of France?"`
580    /// - `"Summarize this article: [article content]"`
581    /// - `"Write a haiku about programming"`
582    pub plain_text_input: Option<String>,
583
584    /// Structured message input for conversation-style interactions
585    ///
586    /// Use this when you need conversation history, different message roles
587    /// (user, assistant, system), or structured dialogue. This is mutually
588    /// exclusive with `plain_text_input`.
589    ///
590    /// # Mutually Exclusive
591    ///
592    /// Cannot be used together with `plain_text_input`.
593    ///
594    /// # Message Roles
595    ///
596    /// - `System`: Instructions for the model's behavior
597    /// - `User`: User input or questions
598    /// - `Assistant`: Previous model responses (for conversation history)
599    ///
600    /// # Examples
601    ///
602    /// ```rust
603    /// use openai_tools::common::message::Message;
604    /// use openai_tools::common::role::Role;
605    ///
606    /// let messages = vec![
607    ///     Message::from_string(Role::System, "You are a helpful assistant"),
608    ///     Message::from_string(Role::User, "Hello!"),
609    ///     Message::from_string(Role::Assistant, "Hi there! How can I help you?"),
610    ///     Message::from_string(Role::User, "What's 2+2?"),
611    /// ];
612    /// ```
613    pub messages_input: Option<Vec<Message>>,
614
615    /// Optional tools that the model can use during response generation
616    ///
617    /// Provides the model with access to external tools like web search,
618    /// code execution, file access, or custom functions. The model will
619    /// automatically decide when and how to use these tools based on the query.
620    ///
621    /// # Tool Types
622    ///
623    /// - Web search tools for finding current information
624    /// - Code interpreter for running and analyzing code
625    /// - File search tools for accessing document collections
626    /// - Custom function tools for specific business logic
627    ///
628    /// # Examples
629    ///
630    /// ```rust
631    /// use openai_tools::common::tool::Tool;
632    /// use openai_tools::common::parameters::ParameterProperty;
633    ///
634    /// let tools = vec![
635    ///     Tool::function("search", "Search the web", Vec::<(&str, ParameterProperty)>::new(), false),
636    ///     Tool::function("calculate", "Perform calculations", Vec::<(&str, ParameterProperty)>::new(), false),
637    /// ];
638    /// ```
639    pub tools: Option<Vec<Tool>>,
640
641    /// Optional tool choice configuration
642    ///
643    /// Controls how the model selects which tool to use when tools are available.
644    /// Can be set to auto (let model decide), none (no tools), required (must use tools),
645    /// or a specific function name to force calling that function.
646    ///
647    /// # Examples
648    ///
649    /// ```rust
650    /// use openai_tools::responses::request::{ToolChoice, ToolChoiceMode, NamedFunctionChoice};
651    ///
652    /// // Let the model decide
653    /// let auto_choice = ToolChoice::Simple(ToolChoiceMode::Auto);
654    ///
655    /// // Force a specific function
656    /// let function_choice = ToolChoice::Function(NamedFunctionChoice::new("get_weather"));
657    /// ```
658    pub tool_choice: Option<ToolChoice>,
659
660    /// Optional prompt template reference
661    ///
662    /// Allows you to use pre-defined prompt templates stored in the OpenAI
663    /// platform, optionally with variable substitution.
664    ///
665    /// # Examples
666    ///
667    /// ```rust
668    /// use openai_tools::responses::request::Prompt;
669    ///
670    /// let prompt = Prompt::new("prompt-abc123");
671    /// ```
672    pub prompt: Option<Prompt>,
673
674    /// Optional prompt cache key for caching
675    ///
676    /// A unique key to use for prompt caching. When provided, the same
677    /// prompt will be cached and reused for subsequent requests with
678    /// the same cache key.
679    pub prompt_cache_key: Option<String>,
680
681    /// Optional prompt cache retention duration
682    ///
683    /// Controls how long cached prompts should be retained.
684    /// Format: duration string (e.g., "1h", "24h", "7d")
685    pub prompt_cache_retention: Option<String>,
686
687    /// Optional structured output format specification
688    ///
689    /// Defines the structure and format for the model's response output.
690    /// Use this when you need the response in a specific JSON schema format
691    /// or other structured format for programmatic processing.
692    ///
693    /// # Examples
694    ///
695    /// ```rust
696    /// use openai_tools::common::structured_output::Schema;
697    /// use openai_tools::responses::request::Format;
698    ///
699    /// let format = Format::new(Schema::responses_json_schema("response_schema"));
700    /// ```
701    pub structured_output: Option<Format>,
702
703    /// Optional sampling temperature for controlling response randomness
704    ///
705    /// Controls the randomness and creativity of the model's responses.
706    /// Higher values make the output more random and creative, while lower
707    /// values make it more focused, deterministic, and consistent.
708    ///
709    /// # Range
710    ///
711    /// - **Range**: 0.0 to 2.0
712    /// - **Default**: 1.0 (if not specified)
713    /// - **Minimum**: 0.0 (most deterministic, least creative)
714    /// - **Maximum**: 2.0 (most random, most creative)
715    ///
716    /// # Recommended Values
717    ///
718    /// - **0.0 - 0.3**: Highly focused and deterministic
719    ///   - Best for: Factual questions, code generation, translations
720    ///   - Behavior: Very consistent, predictable responses
721    ///
722    /// - **0.3 - 0.7**: Balanced creativity and consistency
723    ///   - Best for: General conversation, explanations, analysis
724    ///   - Behavior: Good balance between creativity and reliability
725    ///
726    /// - **0.7 - 1.2**: More creative and varied responses
727    ///   - Best for: Creative writing, brainstorming, ideation
728    ///   - Behavior: More diverse and interesting outputs
729    ///
730    /// - **1.2 - 2.0**: Highly creative and unpredictable
731    ///   - Best for: Experimental creative tasks, humor, unconventional ideas
732    ///   - Behavior: Very diverse but potentially less coherent
733    ///
734    /// # Usage Guidelines
735    ///
736    /// - **Start with 0.7** for most applications as a good default
737    /// - **Use 0.0-0.3** when you need consistent, reliable responses
738    /// - **Use 0.8-1.2** for creative tasks that still need coherence
739    /// - **Avoid values above 1.5** unless you specifically want very random outputs
740    ///
741    /// # API Reference
742    ///
743    /// Corresponds to the `temperature` parameter in the OpenAI Responses API:
744    /// <https://platform.openai.com/docs/api-reference/responses/create>
745    ///
746    /// # Examples
747    ///
748    /// ```rust
749    /// use openai_tools::responses::request::Responses;
750    ///
751    /// // Deterministic, factual responses
752    /// let mut client_factual = Responses::new();
753    /// client_factual.temperature(0.2);
754    ///
755    /// // Balanced creativity and consistency
756    /// let mut client_balanced = Responses::new();
757    /// client_balanced.temperature(0.7);
758    ///
759    /// // High creativity for brainstorming
760    /// let mut client_creative = Responses::new();
761    /// client_creative.temperature(1.1);
762    /// ```
763    pub temperature: Option<f64>,
764
765    /// Optional maximum number of tokens to generate in the response
766    ///
767    /// Controls the maximum length of the generated response. The actual response
768    /// may be shorter if the model naturally concludes or hits other stopping conditions.
769    ///
770    /// # Range
771    ///
772    /// - Minimum: 1
773    /// - Maximum: Depends on the model (typically 4096-8192 for most models)
774    ///
775    /// # Default Behavior
776    ///
777    /// If not specified, the model will use its default maximum output length.
778    ///
779    /// # Examples
780    ///
781    /// - `Some(100)` - Short responses, good for summaries or brief answers
782    /// - `Some(1000)` - Medium responses, suitable for detailed explanations
783    /// - `Some(4000)` - Long responses, for comprehensive analysis or long-form content
784    pub max_output_tokens: Option<usize>,
785
786    /// Optional maximum number of tool calls to make
787    ///
788    /// Limits how many tools the model can invoke during response generation.
789    /// This helps control cost and response time when using multiple tools.
790    ///
791    /// # Range
792    ///
793    /// - Minimum: 0 (no tool calls allowed)
794    /// - Maximum: Implementation-dependent
795    ///
796    /// # Use Cases
797    ///
798    /// - Set to `Some(1)` for single tool usage
799    /// - Set to `Some(0)` to disable tool usage entirely
800    /// - Leave as `None` for unlimited tool usage (subject to other constraints)
801    pub max_tool_calls: Option<usize>,
802
803    /// Optional metadata to include with the request
804    ///
805    /// Arbitrary key-value pairs that can be attached to the request for
806    /// tracking, logging, or passing additional context that doesn't affect
807    /// the model's behavior.
808    ///
809    /// # Common Use Cases
810    ///
811    /// - Request tracking: `{"request_id": "req_123", "user_id": "user_456"}`
812    /// - A/B testing: `{"experiment": "variant_a", "test_group": "control"}`
813    /// - Analytics: `{"session_id": "sess_789", "feature": "chat"}`
814    ///
815    /// # Examples
816    ///
817    /// ```rust
818    /// use std::collections::HashMap;
819    /// use serde_json::Value;
820    ///
821    /// let mut metadata = HashMap::new();
822    /// metadata.insert("user_id".to_string(), Value::String("user123".to_string()));
823    /// metadata.insert("session_id".to_string(), Value::String("sess456".to_string()));
824    /// metadata.insert("priority".to_string(), Value::Number(serde_json::Number::from(1)));
825    /// ```
826    pub metadata: Option<HashMap<String, serde_json::Value>>,
827
828    /// Optional flag to enable parallel tool calls
829    ///
830    /// When enabled, the model can make multiple tool calls simultaneously
831    /// rather than sequentially. This can significantly improve response time
832    /// when multiple independent tools need to be used.
833    ///
834    /// # Default
835    ///
836    /// If not specified, defaults to the model's default behavior (usually `true`).
837    ///
838    /// # When to Use
839    ///
840    /// - `Some(true)`: Enable when tools are independent and can run in parallel
841    /// - `Some(false)`: Disable when tools have dependencies or order matters
842    ///
843    /// # Examples
844    ///
845    /// - Weather + Stock prices: Can run in parallel (`true`)
846    /// - File read + File analysis: Should run sequentially (`false`)
847    pub parallel_tool_calls: Option<bool>,
848
849    /// Optional fields to include in the output
850    ///
851    /// Specifies additional metadata and information to include in the response
852    /// beyond the main generated content. This can include tool call details,
853    /// reasoning traces, log probabilities, and more.
854    ///
855    /// # Available Inclusions
856    ///
857    /// - Web search call sources and results
858    /// - Code interpreter execution outputs
859    /// - Image URLs from various sources
860    /// - Log probabilities for generated tokens
861    /// - Reasoning traces and encrypted content
862    ///
863    /// # Examples
864    ///
865    /// ```rust
866    /// use openai_tools::responses::request::Include;
867    ///
868    /// let includes = vec![
869    ///     Include::WebSearchCall,
870    ///     Include::LogprobsInOutput,
871    ///     Include::ReasoningEncryptedContent,
872    /// ];
873    /// ```
874    pub include: Option<Vec<Include>>,
875
876    /// Optional flag to enable background processing
877    ///
878    /// When enabled, allows the request to be processed in the background,
879    /// potentially improving throughput for non-urgent requests.
880    ///
881    /// # Use Cases
882    ///
883    /// - `Some(true)`: Batch processing, non-interactive requests
884    /// - `Some(false)` or `None`: Real-time, interactive requests
885    ///
886    /// # Trade-offs
887    ///
888    /// - Background processing may have lower latency guarantees
889    /// - May be more cost-effective for bulk operations
890    /// - May have different rate limiting behavior
891    pub background: Option<bool>,
892
893    /// Optional conversation ID for tracking
894    ///
895    /// Identifier for grouping related requests as part of the same conversation
896    /// or session. This helps with context management and analytics.
897    ///
898    /// # Format
899    ///
900    /// Typically a UUID or other unique identifier string.
901    ///
902    /// # Examples
903    ///
904    /// - `Some("conv_123e4567-e89b-12d3-a456-426614174000".to_string())`
905    /// - `Some("user123_session456".to_string())`
906    pub conversation: Option<String>,
907
908    /// Optional ID of the previous response for context
909    ///
910    /// References a previous response in the same conversation to maintain
911    /// context and enable features like response chaining or follow-up handling.
912    ///
913    /// # Use Cases
914    ///
915    /// - Multi-turn conversations with context preservation
916    /// - Follow-up questions or clarifications
917    /// - Response refinement or iteration
918    ///
919    /// # Examples
920    ///
921    /// - `Some("resp_abc123def456".to_string())`
922    pub previous_response_id: Option<String>,
923
924    /// Optional reasoning configuration
925    ///
926    /// Controls how the model approaches complex reasoning tasks, including
927    /// the effort level and format of reasoning explanations.
928    ///
929    /// # Use Cases
930    ///
931    /// - Complex problem-solving requiring deep analysis
932    /// - Mathematical or logical reasoning tasks
933    /// - When you need insight into the model's reasoning process
934    ///
935    /// # Examples
936    ///
937    /// ```rust
938    /// use openai_tools::responses::request::{Reasoning, ReasoningEffort, ReasoningSummary};
939    ///
940    /// let reasoning = Reasoning {
941    ///     effort: Some(ReasoningEffort::High),
942    ///     summary: Some(ReasoningSummary::Detailed),
943    /// };
944    /// ```
945    pub reasoning: Option<Reasoning>,
946
947    /// Optional text output configuration
948    ///
949    /// Controls the characteristics of the generated text output,
950    /// such as verbosity level. Available on GPT-5.2 and newer models.
951    ///
952    /// # Examples
953    ///
954    /// ```rust
955    /// use openai_tools::responses::request::{TextConfig, TextVerbosity};
956    ///
957    /// let text = TextConfig {
958    ///     verbosity: Some(TextVerbosity::High),
959    /// };
960    /// ```
961    pub text: Option<TextConfig>,
962
963    /// Optional safety identifier
964    ///
965    /// Identifier for safety and content filtering configurations.
966    /// Used to specify which safety policies should be applied to the request.
967    ///
968    /// # Examples
969    ///
970    /// - `Some("strict".to_string())` - Apply strict content filtering
971    /// - `Some("moderate".to_string())` - Apply moderate content filtering
972    /// - `Some("permissive".to_string())` - Apply permissive content filtering
973    pub safety_identifier: Option<String>,
974
975    /// Optional service tier specification
976    ///
977    /// Specifies the service tier for the request, which may affect
978    /// processing priority, rate limits, and pricing.
979    ///
980    /// # Common Values
981    ///
982    /// - `Some("default".to_string())` - Standard service tier
983    /// - `Some("scale".to_string())` - High-throughput tier
984    /// - `Some("premium".to_string())` - Premium service tier with enhanced features
985    pub service_tier: Option<String>,
986
987    /// Optional flag to store the conversation
988    ///
989    /// When enabled, the conversation may be stored for future reference,
990    /// training, or analytics purposes (subject to privacy policies).
991    ///
992    /// # Privacy Considerations
993    ///
994    /// - `Some(true)`: Allow storage (check privacy policies)
995    /// - `Some(false)`: Explicitly opt-out of storage
996    /// - `None`: Use default storage policy
997    pub store: Option<bool>,
998
999    /// Optional flag to enable streaming responses
1000    ///
1001    /// When enabled, the response will be streamed back in chunks as it's
1002    /// generated, allowing for real-time display of partial results.
1003    ///
1004    /// # Use Cases
1005    ///
1006    /// - `Some(true)`: Real-time chat interfaces, live text generation
1007    /// - `Some(false)`: Batch processing, when you need the complete response
1008    ///
1009    /// # Considerations
1010    ///
1011    /// - Streaming responses require different handling in client code
1012    /// - May affect some response features or formatting options
1013    pub stream: Option<bool>,
1014
1015    /// Optional streaming configuration options
1016    ///
1017    /// Additional options for controlling streaming response behavior,
1018    /// such as whether to include obfuscated placeholder content.
1019    ///
1020    /// # Only Relevant When Streaming
1021    ///
1022    /// This field is only meaningful when `stream` is `Some(true)`.
1023    pub stream_options: Option<StreamOptions>,
1024
1025    /// Optional number of top log probabilities to include
1026    ///
1027    /// Specifies how many of the most likely alternative tokens to include
1028    /// with their log probabilities for each generated token.
1029    ///
1030    /// # Range
1031    ///
1032    /// - Minimum: 0 (no log probabilities)
1033    /// - Maximum: Model-dependent (typically 5-20)
1034    ///
1035    /// # Use Cases
1036    ///
1037    /// - Model analysis and debugging
1038    /// - Confidence estimation
1039    /// - Alternative response exploration
1040    ///
1041    /// # Examples
1042    ///
1043    /// - `Some(1)` - Include the top alternative for each token
1044    /// - `Some(5)` - Include top 5 alternatives for detailed analysis
1045    pub top_logprobs: Option<usize>,
1046
1047    /// Optional nucleus sampling parameter
1048    ///
1049    /// Controls the randomness of the model's responses by limiting the
1050    /// cumulative probability of considered tokens.
1051    ///
1052    /// # Range
1053    ///
1054    /// - 0.0 to 1.0
1055    /// - Lower values (e.g., 0.1) make responses more focused and deterministic
1056    /// - Higher values (e.g., 0.9) make responses more diverse and creative
1057    ///
1058    /// # Default
1059    ///
1060    /// If not specified, uses the model's default value (typically around 1.0).
1061    ///
1062    /// # Examples
1063    ///
1064    /// - `Some(0.1)` - Very focused, deterministic responses
1065    /// - `Some(0.7)` - Balanced creativity and focus
1066    /// - `Some(0.95)` - High creativity and diversity
1067    pub top_p: Option<f64>,
1068
1069    /// Optional truncation behavior configuration
1070    ///
1071    /// Controls how the system handles inputs that exceed the maximum
1072    /// context length supported by the model.
1073    ///
1074    /// # Options
1075    ///
1076    /// - `Some(Truncation::Auto)` - Automatically truncate long inputs
1077    /// - `Some(Truncation::Disabled)` - Return error for long inputs
1078    /// - `None` - Use system default behavior
1079    ///
1080    /// # Use Cases
1081    ///
1082    /// - `Auto`: When you want to handle long documents gracefully
1083    /// - `Disabled`: When you need to ensure complete input processing
1084    pub truncation: Option<Truncation>,
1085}
1086
1087impl Serialize for Body {
1088    /// Custom serialization implementation for the request body
1089    ///
1090    /// This implementation handles the conversion of either plain text input
1091    /// or messages input into the appropriate "input" field format required
1092    /// by the OpenAI API. It also conditionally includes optional fields
1093    /// like tools and text formatting.
1094    ///
1095    /// # Errors
1096    ///
1097    /// Returns a serialization error if neither plain_text_input nor
1098    /// messages_input is set, as one of them is required.
1099    fn serialize<S>(&self, serializer: S) -> anyhow::Result<S::Ok, S::Error>
1100    where
1101        S: serde::Serializer,
1102    {
1103        let mut state = serializer.serialize_struct("ResponsesBody", 4)?;
1104        state.serialize_field("model", &self.model)?;
1105
1106        // Set input
1107        if self.plain_text_input.is_some() {
1108            state.serialize_field("input", &self.plain_text_input.clone().unwrap())?;
1109        } else if self.messages_input.is_some() {
1110            state.serialize_field("input", &self.messages_input.clone().unwrap())?;
1111        } else {
1112            return Err(serde::ser::Error::custom("Either plain_text_input or messages_input must be set."));
1113        };
1114
1115        // Optional fields
1116        if self.temperature.is_some() {
1117            state.serialize_field("temperature", &self.temperature)?;
1118        }
1119        if self.instructions.is_some() {
1120            state.serialize_field("instructions", &self.instructions)?;
1121        }
1122        if self.tools.is_some() {
1123            state.serialize_field("tools", &self.tools)?;
1124        }
1125        if self.tool_choice.is_some() {
1126            state.serialize_field("tool_choice", &self.tool_choice)?;
1127        }
1128        if self.prompt.is_some() {
1129            state.serialize_field("prompt", &self.prompt)?;
1130        }
1131        if self.prompt_cache_key.is_some() {
1132            state.serialize_field("prompt_cache_key", &self.prompt_cache_key)?;
1133        }
1134        if self.prompt_cache_retention.is_some() {
1135            state.serialize_field("prompt_cache_retention", &self.prompt_cache_retention)?;
1136        }
1137        if self.structured_output.is_some() {
1138            state.serialize_field("text", &self.structured_output)?;
1139        }
1140        if self.max_output_tokens.is_some() {
1141            state.serialize_field("max_output_tokens", &self.max_output_tokens)?;
1142        }
1143        if self.max_tool_calls.is_some() {
1144            state.serialize_field("max_tool_calls", &self.max_tool_calls)?;
1145        }
1146        if self.metadata.is_some() {
1147            state.serialize_field("metadata", &self.metadata)?;
1148        }
1149        if self.parallel_tool_calls.is_some() {
1150            state.serialize_field("parallel_tool_calls", &self.parallel_tool_calls)?;
1151        }
1152        if self.include.is_some() {
1153            state.serialize_field("include", &self.include)?;
1154        }
1155        if self.background.is_some() {
1156            state.serialize_field("background", &self.background)?;
1157        }
1158        if self.conversation.is_some() {
1159            state.serialize_field("conversation", &self.conversation)?;
1160        }
1161        if self.previous_response_id.is_some() {
1162            state.serialize_field("previous_response_id", &self.previous_response_id)?;
1163        }
1164        if self.reasoning.is_some() {
1165            state.serialize_field("reasoning", &self.reasoning)?;
1166        }
1167        if self.text.is_some() {
1168            state.serialize_field("text", &self.text)?;
1169        }
1170        if self.safety_identifier.is_some() {
1171            state.serialize_field("safety_identifier", &self.safety_identifier)?;
1172        }
1173        if self.service_tier.is_some() {
1174            state.serialize_field("service_tier", &self.service_tier)?;
1175        }
1176        if self.store.is_some() {
1177            state.serialize_field("store", &self.store)?;
1178        }
1179        if self.stream.is_some() {
1180            state.serialize_field("stream", &self.stream)?;
1181        }
1182        if self.stream_options.is_some() {
1183            state.serialize_field("stream_options", &self.stream_options)?;
1184        }
1185        if self.top_logprobs.is_some() {
1186            state.serialize_field("top_logprobs", &self.top_logprobs)?;
1187        }
1188        if self.top_p.is_some() {
1189            state.serialize_field("top_p", &self.top_p)?;
1190        }
1191        if self.truncation.is_some() {
1192            state.serialize_field("truncation", &self.truncation)?;
1193        }
1194        state.end()
1195    }
1196}
1197
1198/// Default API path for Responses
1199const RESPONSES_PATH: &str = "responses";
1200
1201/// Client for making requests to the OpenAI Responses API
1202///
1203/// This struct provides a convenient interface for building and executing requests
1204/// to the OpenAI Responses API and Azure OpenAI API. It handles authentication,
1205/// request formatting, and response parsing automatically.
1206///
1207/// # Providers
1208///
1209/// The client supports two providers:
1210/// - **OpenAI**: Standard OpenAI API (default)
1211/// - **Azure**: Azure OpenAI Service
1212///
1213/// # Examples
1214///
1215/// ## OpenAI (existing behavior - unchanged)
1216///
1217/// ```rust,no_run
1218/// use openai_tools::responses::request::Responses;
1219///
1220/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1221/// let mut client = Responses::new();
1222/// let response = client
1223///     .model_id("gpt-4")
1224///     .instructions("You are a helpful assistant.")
1225///     .str_message("Hello, how are you?")
1226///     .complete()
1227///     .await?;
1228/// # Ok(())
1229/// # }
1230/// ```
1231///
1232/// ## Azure OpenAI
1233///
1234/// ```rust,no_run
1235/// use openai_tools::responses::request::Responses;
1236///
1237/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1238/// let mut client = Responses::azure()?;
1239/// let response = client
1240///     .str_message("Hello!")
1241///     .complete()
1242///     .await?;
1243/// # Ok(())
1244/// # }
1245/// ```
1246#[derive(Debug, Clone)]
1247pub struct Responses {
1248    /// Authentication provider (OpenAI or Azure)
1249    auth: AuthProvider,
1250    /// The User-Agent string to include in requests
1251    user_agent: String,
1252    /// The request body containing all parameters for the API call
1253    pub request_body: Body,
1254    /// Optional request timeout duration
1255    timeout: Option<Duration>,
1256}
1257
1258impl Default for Responses {
1259    fn default() -> Self {
1260        Self::new()
1261    }
1262}
1263
1264impl Responses {
1265    /// Creates a new instance of the Responses client for OpenAI API
1266    ///
1267    /// This method initializes a new client by loading the OpenAI API key from
1268    /// the `OPENAI_API_KEY` environment variable. Make sure to set this environment
1269    /// variable before calling this method.
1270    ///
1271    /// # Panics
1272    ///
1273    /// Panics if the `OPENAI_API_KEY` environment variable is not set.
1274    pub fn new() -> Self {
1275        let auth = AuthProvider::openai_from_env().map_err(|e| OpenAIToolError::Error(format!("Failed to load OpenAI auth: {}", e))).unwrap();
1276        Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None }
1277    }
1278
1279    /// Creates a new instance of the Responses client with a custom endpoint
1280    #[deprecated(since = "0.3.0", note = "Use `with_auth()` with custom OpenAIAuth for custom endpoints")]
1281    pub fn from_endpoint<T: AsRef<str>>(endpoint: T) -> Self {
1282        let auth = AuthProvider::openai_from_env().map_err(|e| OpenAIToolError::Error(format!("Failed to load OpenAI auth: {}", e))).unwrap();
1283        // Extract the path from the endpoint and use it
1284        let mut responses = Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None };
1285        responses.base_url(endpoint.as_ref().trim_end_matches("/responses"));
1286        responses
1287    }
1288
1289    /// Creates a new Responses client with a specified model.
1290    ///
1291    /// This is the recommended constructor as it enables parameter validation
1292    /// at setter time. When you set parameters like `temperature()` or `top_p()`,
1293    /// the model's parameter support is checked and warnings are logged for
1294    /// unsupported values.
1295    ///
1296    /// # Arguments
1297    ///
1298    /// * `model` - The model to use for response generation
1299    ///
1300    /// # Panics
1301    ///
1302    /// Panics if the `OPENAI_API_KEY` environment variable is not set.
1303    ///
1304    /// # Returns
1305    ///
1306    /// A new Responses instance with the specified model
1307    ///
1308    /// # Example
1309    ///
1310    /// ```rust,no_run
1311    /// use openai_tools::responses::request::Responses;
1312    /// use openai_tools::common::models::ChatModel;
1313    ///
1314    /// // Recommended: specify model at creation time
1315    /// let mut responses = Responses::with_model(ChatModel::Gpt4oMini);
1316    ///
1317    /// // For reasoning models, unsupported parameters are validated at setter time
1318    /// let mut reasoning_responses = Responses::with_model(ChatModel::O3Mini);
1319    /// reasoning_responses.temperature(0.5); // Warning logged, value ignored
1320    /// ```
1321    pub fn with_model(model: ChatModel) -> Self {
1322        let auth = AuthProvider::openai_from_env().map_err(|e| OpenAIToolError::Error(format!("Failed to load OpenAI auth: {}", e))).unwrap();
1323        Self { auth, user_agent: "".into(), request_body: Body { model, ..Default::default() }, timeout: None }
1324    }
1325
1326    /// Creates a new Responses client with a custom authentication provider
1327    ///
1328    /// Use this to explicitly configure OpenAI or Azure authentication.
1329    ///
1330    /// # Arguments
1331    ///
1332    /// * `auth` - The authentication provider
1333    ///
1334    /// # Returns
1335    ///
1336    /// A new Responses instance with the specified auth provider
1337    ///
1338    /// # Example
1339    ///
1340    /// ```rust
1341    /// use openai_tools::responses::request::Responses;
1342    /// use openai_tools::common::auth::{AuthProvider, AzureAuth};
1343    ///
1344    /// // Explicit Azure configuration with complete base URL
1345    /// let auth = AuthProvider::Azure(
1346    ///     AzureAuth::new(
1347    ///         "api-key",
1348    ///         "https://my-resource.openai.azure.com/openai/deployments/gpt-4o?api-version=2024-08-01-preview"
1349    ///     )
1350    /// );
1351    /// let mut responses = Responses::with_auth(auth);
1352    /// ```
1353    pub fn with_auth(auth: AuthProvider) -> Self {
1354        Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None }
1355    }
1356
1357    /// Creates a new Responses client for Azure OpenAI API
1358    ///
1359    /// Loads configuration from Azure-specific environment variables.
1360    ///
1361    /// # Returns
1362    ///
1363    /// `Result<Responses>` - Configured for Azure or error if env vars missing
1364    ///
1365    /// # Environment Variables
1366    ///
1367    /// | Variable | Required | Description |
1368    /// |----------|----------|-------------|
1369    /// | `AZURE_OPENAI_API_KEY` | Yes | Azure API key |
1370    /// | `AZURE_OPENAI_BASE_URL` | Yes | Complete endpoint URL including deployment, API path, and api-version |
1371    ///
1372    /// # Example
1373    ///
1374    /// ```rust,no_run
1375    /// use openai_tools::responses::request::Responses;
1376    ///
1377    /// // With environment variables:
1378    /// // AZURE_OPENAI_API_KEY=xxx
1379    /// // AZURE_OPENAI_BASE_URL=https://my-resource.openai.azure.com/openai/deployments/gpt-4o/responses?api-version=2024-08-01-preview
1380    /// let mut responses = Responses::azure()?;
1381    /// # Ok::<(), openai_tools::common::errors::OpenAIToolError>(())
1382    /// ```
1383    pub fn azure() -> Result<Self> {
1384        let auth = AuthProvider::azure_from_env()?;
1385        Ok(Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None })
1386    }
1387
1388    /// Creates a new Responses client by auto-detecting the provider
1389    ///
1390    /// Tries Azure first (if AZURE_OPENAI_API_KEY is set), then falls back to OpenAI.
1391    ///
1392    /// # Returns
1393    ///
1394    /// `Result<Responses>` - Auto-configured client or error
1395    ///
1396    /// # Example
1397    ///
1398    /// ```rust,no_run
1399    /// use openai_tools::responses::request::Responses;
1400    ///
1401    /// // Uses Azure if AZURE_OPENAI_API_KEY is set, otherwise OpenAI
1402    /// let mut responses = Responses::detect_provider()?;
1403    /// # Ok::<(), openai_tools::common::errors::OpenAIToolError>(())
1404    /// ```
1405    pub fn detect_provider() -> Result<Self> {
1406        let auth = AuthProvider::from_env()?;
1407        Ok(Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None })
1408    }
1409
1410    /// Creates a new Responses instance with URL-based provider detection
1411    ///
1412    /// Analyzes the URL pattern to determine the provider:
1413    /// - URLs containing `.openai.azure.com` → Azure
1414    /// - All other URLs → OpenAI-compatible
1415    ///
1416    /// # Arguments
1417    ///
1418    /// * `base_url` - The complete base URL for API requests
1419    /// * `api_key` - The API key or token
1420    pub fn with_url<S: Into<String>>(base_url: S, api_key: S) -> Self {
1421        let auth = AuthProvider::from_url_with_key(base_url, api_key);
1422        Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None }
1423    }
1424
1425    /// Creates a new Responses instance from URL using environment variables
1426    ///
1427    /// Analyzes the URL pattern to determine the provider, then loads
1428    /// credentials from the appropriate environment variables.
1429    pub fn from_url<S: Into<String>>(url: S) -> Result<Self> {
1430        let auth = AuthProvider::from_url(url)?;
1431        Ok(Self { auth, user_agent: "".into(), request_body: Body::default(), timeout: None })
1432    }
1433
1434    /// Returns the authentication provider
1435    ///
1436    /// # Returns
1437    ///
1438    /// Reference to the authentication provider
1439    pub fn auth(&self) -> &AuthProvider {
1440        &self.auth
1441    }
1442
1443    /// Sets a custom API endpoint URL (OpenAI only)
1444    ///
1445    /// Use this to point to alternative OpenAI-compatible APIs (e.g., proxy servers).
1446    /// For Azure, use `azure()` or `with_auth()` instead.
1447    ///
1448    /// # Arguments
1449    ///
1450    /// * `url` - The base URL (e.g., "https://my-proxy.example.com/v1")
1451    ///
1452    /// # Returns
1453    ///
1454    /// A mutable reference to self for method chaining
1455    ///
1456    /// # Note
1457    ///
1458    /// This method only works with OpenAI authentication. For Azure, the endpoint
1459    /// is constructed from resource name and deployment name.
1460    ///
1461    /// # Example
1462    ///
1463    /// ```rust,no_run
1464    /// use openai_tools::responses::request::Responses;
1465    ///
1466    /// let mut responses = Responses::new();
1467    /// responses.base_url("https://my-proxy.example.com/v1");
1468    /// ```
1469    pub fn base_url<T: AsRef<str>>(&mut self, url: T) -> &mut Self {
1470        // Only modify if OpenAI provider
1471        if let AuthProvider::OpenAI(ref openai_auth) = self.auth {
1472            let new_auth = OpenAIAuth::new(openai_auth.api_key()).with_base_url(url.as_ref());
1473            self.auth = AuthProvider::OpenAI(new_auth);
1474        } else {
1475            tracing::warn!("base_url() is only supported for OpenAI provider. Use azure() or with_auth() for Azure.");
1476        }
1477        self
1478    }
1479
1480    /// Sets the model for the request.
1481    ///
1482    /// # Arguments
1483    ///
1484    /// * `model` - The model to use (e.g., `ChatModel::Gpt4oMini`, `ChatModel::Gpt4o`)
1485    ///
1486    /// # Returns
1487    ///
1488    /// A mutable reference to self for method chaining
1489    ///
1490    /// # Example
1491    ///
1492    /// ```rust,no_run
1493    /// use openai_tools::responses::request::Responses;
1494    /// use openai_tools::common::models::ChatModel;
1495    ///
1496    /// let mut responses = Responses::new();
1497    /// responses.model(ChatModel::Gpt4oMini);
1498    /// ```
1499    pub fn model(&mut self, model: ChatModel) -> &mut Self {
1500        self.request_body.model = model;
1501        self
1502    }
1503
1504    /// Sets the model using a string ID (for backward compatibility).
1505    ///
1506    /// Prefer using [`model`] with `ChatModel` enum for type safety.
1507    ///
1508    /// # Arguments
1509    ///
1510    /// * `model_id` - The ID of the model to use (e.g., "gpt-4o-mini")
1511    ///
1512    /// # Returns
1513    ///
1514    /// A mutable reference to self for method chaining
1515    #[deprecated(since = "0.2.0", note = "Use `model(ChatModel)` instead for type safety")]
1516    pub fn model_id<T: AsRef<str>>(&mut self, model_id: T) -> &mut Self {
1517        self.request_body.model = ChatModel::from(model_id.as_ref());
1518        self
1519    }
1520
1521    /// Sets the request timeout duration
1522    ///
1523    /// # Arguments
1524    ///
1525    /// * `timeout` - The maximum time to wait for a response
1526    ///
1527    /// # Returns
1528    ///
1529    /// A mutable reference to self for method chaining
1530    ///
1531    /// # Example
1532    ///
1533    /// ```rust,no_run
1534    /// use std::time::Duration;
1535    /// use openai_tools::responses::request::Responses;
1536    ///
1537    /// let mut responses = Responses::new();
1538    /// responses.model_id("gpt-4o")
1539    ///     .timeout(Duration::from_secs(30));
1540    /// ```
1541    pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
1542        self.timeout = Some(timeout);
1543        self
1544    }
1545
1546    /// Sets the User-Agent string for the request
1547    ///
1548    /// # Arguments
1549    ///
1550    /// * `user_agent` - The User-Agent string to include in the request headers
1551    ///
1552    /// # Returns
1553    ///
1554    /// A mutable reference to self for method chaining
1555    pub fn user_agent<T: AsRef<str>>(&mut self, user_agent: T) -> &mut Self {
1556        self.user_agent = user_agent.as_ref().to_string();
1557        self
1558    }
1559
1560    /// Sets instructions to guide the model's behavior
1561    ///
1562    /// # Arguments
1563    ///
1564    /// * `instructions` - Instructions that define how the model should behave
1565    ///
1566    /// # Returns
1567    ///
1568    /// A mutable reference to self for method chaining
1569    pub fn instructions<T: AsRef<str>>(&mut self, instructions: T) -> &mut Self {
1570        self.request_body.instructions = Some(instructions.as_ref().to_string());
1571        self
1572    }
1573
1574    /// Sets plain text input for simple text-based requests
1575    ///
1576    /// This method is mutually exclusive with `messages()`. Use this for simple
1577    /// text-based interactions where you don't need conversation history.
1578    ///
1579    /// # Arguments
1580    ///
1581    /// * `input` - The plain text input to send to the model
1582    ///
1583    /// # Returns
1584    ///
1585    /// A mutable reference to self for method chaining
1586    pub fn str_message<T: AsRef<str>>(&mut self, input: T) -> &mut Self {
1587        self.request_body.plain_text_input = Some(input.as_ref().to_string());
1588        self
1589    }
1590
1591    /// Sets structured message input for conversation-style interactions
1592    ///
1593    /// This method is mutually exclusive with `plain_text_input()`. Use this
1594    /// for complex conversations with message history and different roles.
1595    ///
1596    /// # Arguments
1597    ///
1598    /// * `messages` - A vector of messages representing the conversation history
1599    ///
1600    /// # Returns
1601    ///
1602    /// A mutable reference to self for method chaining
1603    pub fn messages(&mut self, messages: Vec<Message>) -> &mut Self {
1604        self.request_body.messages_input = Some(messages);
1605        self
1606    }
1607
1608    /// Sets tools that the model can use during response generation
1609    ///
1610    /// # Arguments
1611    ///
1612    /// * `tools` - A vector of tools available to the model
1613    ///
1614    /// # Returns
1615    ///
1616    /// A mutable reference to self for method chaining
1617    pub fn tools(&mut self, tools: Vec<Tool>) -> &mut Self {
1618        self.request_body.tools = Some(tools);
1619        self
1620    }
1621
1622    /// Sets the tool choice configuration
1623    ///
1624    /// Controls how the model selects which tool to use when tools are available.
1625    /// Can be set to auto (let model decide), none (no tools), required (must use tools),
1626    /// or a specific function name to force calling that function.
1627    ///
1628    /// # Arguments
1629    ///
1630    /// * `tool_choice` - The tool choice configuration
1631    ///
1632    /// # Returns
1633    ///
1634    /// A mutable reference to self for method chaining
1635    ///
1636    /// # Examples
1637    ///
1638    /// ```rust
1639    /// use openai_tools::responses::request::{Responses, ToolChoice, ToolChoiceMode, NamedFunctionChoice};
1640    ///
1641    /// let mut client = Responses::new();
1642    ///
1643    /// // Let the model decide
1644    /// client.tool_choice(ToolChoice::Simple(ToolChoiceMode::Auto));
1645    ///
1646    /// // Force a specific function
1647    /// client.tool_choice(ToolChoice::Function(NamedFunctionChoice::new("get_weather")));
1648    /// ```
1649    pub fn tool_choice(&mut self, tool_choice: ToolChoice) -> &mut Self {
1650        self.request_body.tool_choice = Some(tool_choice);
1651        self
1652    }
1653
1654    /// Sets a prompt template reference
1655    ///
1656    /// Allows you to use pre-defined prompt templates stored in the OpenAI
1657    /// platform, optionally with variable substitution.
1658    ///
1659    /// # Arguments
1660    ///
1661    /// * `prompt` - The prompt template reference
1662    ///
1663    /// # Returns
1664    ///
1665    /// A mutable reference to self for method chaining
1666    ///
1667    /// # Examples
1668    ///
1669    /// ```rust
1670    /// use openai_tools::responses::request::{Responses, Prompt};
1671    /// use std::collections::HashMap;
1672    ///
1673    /// let mut client = Responses::new();
1674    ///
1675    /// // Simple prompt reference
1676    /// client.prompt(Prompt::new("prompt-abc123"));
1677    ///
1678    /// // Prompt with variables
1679    /// let mut vars = HashMap::new();
1680    /// vars.insert("name".to_string(), "Alice".to_string());
1681    /// client.prompt(Prompt::with_variables("prompt-abc123", vars));
1682    /// ```
1683    pub fn prompt(&mut self, prompt: Prompt) -> &mut Self {
1684        self.request_body.prompt = Some(prompt);
1685        self
1686    }
1687
1688    /// Sets the prompt cache key for caching
1689    ///
1690    /// A unique key to use for prompt caching. When provided, the same
1691    /// prompt will be cached and reused for subsequent requests with
1692    /// the same cache key.
1693    ///
1694    /// # Arguments
1695    ///
1696    /// * `key` - The cache key to use
1697    ///
1698    /// # Returns
1699    ///
1700    /// A mutable reference to self for method chaining
1701    ///
1702    /// # Examples
1703    ///
1704    /// ```rust
1705    /// use openai_tools::responses::request::Responses;
1706    ///
1707    /// let mut client = Responses::new();
1708    /// client.prompt_cache_key("my-cache-key");
1709    /// ```
1710    pub fn prompt_cache_key<T: AsRef<str>>(&mut self, key: T) -> &mut Self {
1711        self.request_body.prompt_cache_key = Some(key.as_ref().to_string());
1712        self
1713    }
1714
1715    /// Sets the prompt cache retention duration
1716    ///
1717    /// Controls how long cached prompts should be retained.
1718    ///
1719    /// # Arguments
1720    ///
1721    /// * `retention` - The retention duration string (e.g., "1h", "24h", "7d")
1722    ///
1723    /// # Returns
1724    ///
1725    /// A mutable reference to self for method chaining
1726    ///
1727    /// # Examples
1728    ///
1729    /// ```rust
1730    /// use openai_tools::responses::request::Responses;
1731    ///
1732    /// let mut client = Responses::new();
1733    /// client.prompt_cache_retention("24h");
1734    /// ```
1735    pub fn prompt_cache_retention<T: AsRef<str>>(&mut self, retention: T) -> &mut Self {
1736        self.request_body.prompt_cache_retention = Some(retention.as_ref().to_string());
1737        self
1738    }
1739
1740    /// Sets structured output format specification
1741    ///
1742    /// This allows you to specify the exact format and structure of the
1743    /// model's response output.
1744    ///
1745    /// # Arguments
1746    ///
1747    /// * `text_format` - The schema defining the expected output structure
1748    ///
1749    /// # Returns
1750    ///
1751    /// A mutable reference to self for method chaining
1752    pub fn structured_output(&mut self, text_format: Schema) -> &mut Self {
1753        self.request_body.structured_output = Option::from(Format::new(text_format));
1754        self
1755    }
1756
1757    /// Sets the sampling temperature for controlling response randomness
1758    ///
1759    /// Controls the randomness and creativity of the model's responses.
1760    /// Higher values make the output more random and creative, while lower
1761    /// values make it more focused and deterministic.
1762    ///
1763    /// # Arguments
1764    ///
1765    /// * `temperature` - The temperature value (0.0 to 2.0)
1766    ///   - 0.0: Most deterministic and focused responses
1767    ///   - 1.0: Default balanced behavior
1768    ///   - 2.0: Most random and creative responses
1769    ///
1770    /// **Note:** Reasoning models (GPT-6, GPT-5, o-series) only support temperature=1.0.
1771    /// For these models, other values will be ignored with a warning.
1772    ///
1773    /// # Panics
1774    ///
1775    /// This method will panic if the temperature value is outside the valid
1776    /// range of 0.0 to 2.0, as this would result in an API error.
1777    ///
1778    /// # Returns
1779    ///
1780    /// A mutable reference to self for method chaining
1781    ///
1782    /// # Examples
1783    ///
1784    /// ```rust
1785    /// use openai_tools::responses::request::Responses;
1786    ///
1787    /// // Deterministic responses for factual queries
1788    /// let mut client = Responses::new();
1789    /// client.temperature(0.2);
1790    ///
1791    /// // Creative responses for brainstorming
1792    /// let mut client = Responses::new();
1793    /// client.temperature(1.1);
1794    /// ```
1795    pub fn temperature(&mut self, temperature: f64) -> &mut Self {
1796        assert!((0.0..=2.0).contains(&temperature), "Temperature must be between 0.0 and 2.0, got {}", temperature);
1797        let support = self.request_body.model.parameter_support();
1798        match support.temperature {
1799            ParameterRestriction::FixedValue(fixed) => {
1800                if (temperature - fixed).abs() > f64::EPSILON {
1801                    tracing::warn!("Model '{}' only supports temperature={}. Ignoring temperature={}.", self.request_body.model, fixed, temperature);
1802                    return self;
1803                }
1804            }
1805            ParameterRestriction::NotSupported => {
1806                tracing::warn!("Model '{}' does not support temperature parameter. Ignoring.", self.request_body.model);
1807                return self;
1808            }
1809            ParameterRestriction::Any => {}
1810        }
1811        self.request_body.temperature = Some(temperature);
1812        self
1813    }
1814
1815    /// Sets the maximum number of tokens to generate in the response
1816    ///
1817    /// Controls the maximum length of the generated response. The actual response
1818    /// may be shorter if the model naturally concludes or hits other stopping conditions.
1819    ///
1820    /// # Arguments
1821    ///
1822    /// * `max_tokens` - Maximum number of tokens to generate (minimum: 1)
1823    ///
1824    /// # Returns
1825    ///
1826    /// A mutable reference to self for method chaining
1827    ///
1828    /// # Examples
1829    ///
1830    /// ```rust
1831    /// use openai_tools::responses::request::Responses;
1832    ///
1833    /// let mut client = Responses::new();
1834    /// client.max_output_tokens(100);  // Limit response to 100 tokens
1835    /// ```
1836    pub fn max_output_tokens(&mut self, max_tokens: usize) -> &mut Self {
1837        self.request_body.max_output_tokens = Some(max_tokens);
1838        self
1839    }
1840
1841    /// Sets the maximum number of tool calls allowed during response generation
1842    ///
1843    /// Limits how many tools the model can invoke during response generation.
1844    /// This helps control cost and response time when using multiple tools.
1845    ///
1846    /// # Arguments
1847    ///
1848    /// * `max_tokens` - Maximum number of tool calls allowed (0 = no tool calls)
1849    ///
1850    /// # Returns
1851    ///
1852    /// A mutable reference to self for method chaining
1853    ///
1854    /// # Examples
1855    ///
1856    /// ```rust
1857    /// use openai_tools::responses::request::Responses;
1858    ///
1859    /// let mut client = Responses::new();
1860    /// client.max_tool_calls(3);  // Allow up to 3 tool calls
1861    /// client.max_tool_calls(0);  // Disable tool usage
1862    /// ```
1863    pub fn max_tool_calls(&mut self, max_tokens: usize) -> &mut Self {
1864        self.request_body.max_tool_calls = Some(max_tokens);
1865        self
1866    }
1867
1868    /// Adds or updates a metadata key-value pair for the request
1869    ///
1870    /// Metadata provides arbitrary key-value pairs that can be attached to the request
1871    /// for tracking, logging, or passing additional context that doesn't affect
1872    /// the model's behavior.
1873    ///
1874    /// # Arguments
1875    ///
1876    /// * `key` - The metadata key (string identifier)
1877    /// * `value` - The metadata value (can be string, number, boolean, etc.)
1878    ///
1879    /// # Behavior
1880    ///
1881    /// - If the key already exists, the old value is replaced with the new one
1882    /// - If metadata doesn't exist yet, a new metadata map is created
1883    /// - Values are stored as `serde_json::Value` for flexibility
1884    ///
1885    /// # Returns
1886    ///
1887    /// A mutable reference to self for method chaining
1888    ///
1889    /// # Examples
1890    ///
1891    /// ```rust
1892    /// use openai_tools::responses::request::Responses;
1893    /// use serde_json::Value;
1894    ///
1895    /// let mut client = Responses::new();
1896    /// client.metadata("user_id".to_string(), Value::String("user123".to_string()));
1897    /// client.metadata("priority".to_string(), Value::Number(serde_json::Number::from(1)));
1898    /// client.metadata("debug".to_string(), Value::Bool(true));
1899    /// ```
1900    pub fn metadata(&mut self, key: String, value: serde_json::Value) -> &mut Self {
1901        if self.request_body.metadata.is_none() {
1902            self.request_body.metadata = Some(HashMap::new());
1903        }
1904        if self.request_body.metadata.as_ref().unwrap().keys().any(|k| k == &key) {
1905            self.request_body.metadata.as_mut().unwrap().remove(&key);
1906        }
1907        self.request_body.metadata.as_mut().unwrap().insert(key, value);
1908        self
1909    }
1910
1911    /// Enables or disables parallel tool calls
1912    ///
1913    /// When enabled, the model can make multiple tool calls simultaneously
1914    /// rather than sequentially. This can significantly improve response time
1915    /// when multiple independent tools need to be used.
1916    ///
1917    /// # Arguments
1918    ///
1919    /// * `enable` - Whether to enable parallel tool calls
1920    ///   - `true`: Tools can be called in parallel (faster for independent tools)
1921    ///   - `false`: Tools are called sequentially (better for dependent operations)
1922    ///
1923    /// # Returns
1924    ///
1925    /// A mutable reference to self for method chaining
1926    ///
1927    /// # When to Use
1928    ///
1929    /// - **Enable (true)**: When tools are independent (e.g., weather + stock prices)
1930    /// - **Disable (false)**: When tools have dependencies (e.g., read file → analyze content)
1931    ///
1932    /// # Examples
1933    ///
1934    /// ```rust
1935    /// use openai_tools::responses::request::Responses;
1936    ///
1937    /// let mut client = Responses::new();
1938    /// client.parallel_tool_calls(true);   // Enable parallel execution
1939    /// client.parallel_tool_calls(false);  // Force sequential execution
1940    /// ```
1941    pub fn parallel_tool_calls(&mut self, enable: bool) -> &mut Self {
1942        self.request_body.parallel_tool_calls = Some(enable);
1943        self
1944    }
1945
1946    /// Specifies additional data to include in the response output
1947    ///
1948    /// Defines various types of additional information that can be included
1949    /// in the API response output, such as web search results, code interpreter
1950    /// outputs, image URLs, log probabilities, and reasoning traces.
1951    ///
1952    /// # Arguments
1953    ///
1954    /// * `includes` - A vector of `Include` enum values specifying what to include
1955    ///
1956    /// # Available Inclusions
1957    ///
1958    /// - `Include::WebSearchCall` - Web search results and sources
1959    /// - `Include::CodeInterpreterCall` - Code execution outputs
1960    /// - `Include::FileSearchCall` - File search operation results
1961    /// - `Include::LogprobsInOutput` - Token log probabilities
1962    /// - `Include::ReasoningEncryptedContent` - Reasoning process traces
1963    /// - `Include::ImageUrlInInputMessages` - Image URLs from input
1964    /// - `Include::ImageUrlInComputerCallOutput` - Computer interaction screenshots
1965    ///
1966    /// # Returns
1967    ///
1968    /// A mutable reference to self for method chaining
1969    ///
1970    /// # Examples
1971    ///
1972    /// ```rust
1973    /// use openai_tools::responses::request::{Responses, Include};
1974    ///
1975    /// let mut client = Responses::new();
1976    /// client.include(vec![
1977    ///     Include::WebSearchCall,
1978    ///     Include::LogprobsInOutput,
1979    ///     Include::ReasoningEncryptedContent,
1980    /// ]);
1981    /// ```
1982    pub fn include(&mut self, includes: Vec<Include>) -> &mut Self {
1983        self.request_body.include = Some(includes);
1984        self
1985    }
1986
1987    /// Enables or disables background processing for the request
1988    ///
1989    /// When enabled, allows the request to be processed in the background,
1990    /// potentially improving throughput for non-urgent requests at the cost
1991    /// of potentially higher latency.
1992    ///
1993    /// # Arguments
1994    ///
1995    /// * `enable` - Whether to enable background processing
1996    ///   - `true`: Process in background (lower priority, potentially longer latency)
1997    ///   - `false`: Process with standard priority (default behavior)
1998    ///
1999    /// # Trade-offs
2000    ///
2001    /// - **Background processing**: Better for batch operations, non-interactive requests
2002    /// - **Standard processing**: Better for real-time, interactive applications
2003    ///
2004    /// # Returns
2005    ///
2006    /// A mutable reference to self for method chaining
2007    ///
2008    /// # Examples
2009    ///
2010    /// ```rust
2011    /// use openai_tools::responses::request::Responses;
2012    ///
2013    /// let mut client = Responses::new();
2014    /// client.background(true);   // Enable background processing
2015    /// client.background(false);  // Use standard processing
2016    /// ```
2017    pub fn background(&mut self, enable: bool) -> &mut Self {
2018        self.request_body.background = Some(enable);
2019        self
2020    }
2021
2022    /// Sets the conversation ID for grouping related requests
2023    ///
2024    /// Identifier for grouping related requests as part of the same conversation
2025    /// or session. This helps with context management, analytics, and conversation
2026    /// tracking across multiple API calls.
2027    ///
2028    /// # Arguments
2029    ///
2030    /// * `conversation_id` - The conversation identifier
2031    ///   - Must start with "conv-" prefix according to API requirements
2032    ///   - Should be a unique identifier (UUID recommended)
2033    ///
2034    /// # Returns
2035    ///
2036    /// A mutable reference to self for method chaining
2037    ///
2038    /// # Format Requirements
2039    ///
2040    /// The conversation ID must follow the format: `conv-{identifier}`
2041    ///
2042    /// # Examples
2043    ///
2044    /// ```rust
2045    /// use openai_tools::responses::request::Responses;
2046    ///
2047    /// let mut client = Responses::new();
2048    /// client.conversation("conv-123e4567-e89b-12d3-a456-426614174000");
2049    /// client.conversation("conv-user123-session456");
2050    /// ```
2051    pub fn conversation<T: AsRef<str>>(&mut self, conversation_id: T) -> &mut Self {
2052        self.request_body.conversation = Some(conversation_id.as_ref().to_string());
2053        self
2054    }
2055
2056    /// Sets the ID of the previous response for context continuation
2057    ///
2058    /// References a previous response in the same conversation to maintain
2059    /// context and enable features like response chaining, follow-up handling,
2060    /// or response refinement.
2061    ///
2062    /// # Arguments
2063    ///
2064    /// * `response_id` - The ID of the previous response to reference
2065    ///
2066    /// # Use Cases
2067    ///
2068    /// - **Multi-turn conversations**: Maintaining context across multiple exchanges
2069    /// - **Follow-up questions**: Building on previous responses
2070    /// - **Response refinement**: Iterating on or clarifying previous answers
2071    /// - **Context chaining**: Creating connected sequences of responses
2072    ///
2073    /// # Returns
2074    ///
2075    /// A mutable reference to self for method chaining
2076    ///
2077    /// # Examples
2078    ///
2079    /// ```rust
2080    /// use openai_tools::responses::request::Responses;
2081    ///
2082    /// let mut client = Responses::new();
2083    /// client.previous_response_id("resp_abc123def456");
2084    /// client.previous_response_id("response-uuid-here");
2085    /// ```
2086    pub fn previous_response_id<T: AsRef<str>>(&mut self, response_id: T) -> &mut Self {
2087        self.request_body.previous_response_id = Some(response_id.as_ref().to_string());
2088        self
2089    }
2090
2091    /// Configures reasoning behavior for complex problem-solving
2092    ///
2093    /// Controls how the model approaches complex reasoning tasks, including
2094    /// the computational effort level and format of reasoning explanations.
2095    /// This is particularly useful for mathematical, logical, or analytical tasks.
2096    ///
2097    /// # Arguments
2098    ///
2099    /// * `effort` - The level of reasoning effort to apply:
2100    ///   - `ReasoningEffort::Minimal` - Fastest, for simple queries
2101    ///   - `ReasoningEffort::Low` - Balanced, for moderate complexity
2102    ///   - `ReasoningEffort::Medium` - Thorough, for complex queries
2103    ///   - `ReasoningEffort::High` - Maximum analysis, for very complex problems
2104    ///
2105    /// * `summary` - The format for reasoning explanations:
2106    ///   - `ReasoningSummary::Auto` - Let the model choose the format
2107    ///   - `ReasoningSummary::Concise` - Brief, focused explanations
2108    ///   - `ReasoningSummary::Detailed` - Comprehensive, step-by-step explanations
2109    ///
2110    /// # Returns
2111    ///
2112    /// A mutable reference to self for method chaining
2113    ///
2114    /// # Use Cases
2115    ///
2116    /// - Mathematical problem-solving with step-by-step explanations
2117    /// - Complex logical reasoning tasks
2118    /// - Analysis requiring deep consideration
2119    /// - Tasks where understanding the reasoning process is important
2120    ///
2121    /// # Examples
2122    ///
2123    /// ```rust
2124    /// use openai_tools::responses::request::{Responses, ReasoningEffort, ReasoningSummary};
2125    ///
2126    /// let mut client = Responses::new();
2127    ///
2128    /// // High effort with detailed explanations for complex problems
2129    /// client.reasoning(ReasoningEffort::High, ReasoningSummary::Detailed);
2130    ///
2131    /// // Medium effort with concise explanations for balanced approach
2132    /// client.reasoning(ReasoningEffort::Medium, ReasoningSummary::Concise);
2133    /// ```
2134    pub fn reasoning(&mut self, effort: ReasoningEffort, summary: ReasoningSummary) -> &mut Self {
2135        self.request_body.reasoning = Some(Reasoning { effort: Some(effort), summary: Some(summary) });
2136        self
2137    }
2138
2139    /// Sets the text output verbosity level
2140    ///
2141    /// Controls how detailed and lengthy the model's text responses should be.
2142    /// This is particularly useful for controlling response length and detail level
2143    /// based on your use case requirements.
2144    ///
2145    /// # Arguments
2146    ///
2147    /// * `verbosity` - The verbosity level for text output:
2148    ///   - `TextVerbosity::Low` - Concise, brief responses
2149    ///   - `TextVerbosity::Medium` - Balanced responses (default)
2150    ///   - `TextVerbosity::High` - Comprehensive, detailed responses
2151    ///
2152    /// # Model Support
2153    ///
2154    /// This parameter is available on GPT-5.2 and newer models.
2155    ///
2156    /// # Use Cases
2157    ///
2158    /// - **Low verbosity**: Quick answers, summaries, yes/no questions
2159    /// - **Medium verbosity**: Standard explanations, general queries
2160    /// - **High verbosity**: Detailed tutorials, comprehensive analysis
2161    ///
2162    /// # Examples
2163    ///
2164    /// ```rust
2165    /// use openai_tools::responses::request::{Responses, TextVerbosity};
2166    ///
2167    /// let mut client = Responses::new();
2168    ///
2169    /// // Concise responses for simple queries
2170    /// client.text_verbosity(TextVerbosity::Low);
2171    ///
2172    /// // Detailed responses for complex explanations
2173    /// client.text_verbosity(TextVerbosity::High);
2174    /// ```
2175    pub fn text_verbosity(&mut self, verbosity: TextVerbosity) -> &mut Self {
2176        self.request_body.text = Some(TextConfig { verbosity: Some(verbosity) });
2177        self
2178    }
2179
2180    /// Sets the safety identifier for content filtering configuration
2181    ///
2182    /// Specifies which safety and content filtering policies should be applied
2183    /// to the request. Different safety levels provide varying degrees of content
2184    /// restriction and filtering.
2185    ///
2186    /// # Arguments
2187    ///
2188    /// * `safety_id` - The safety configuration identifier
2189    ///
2190    /// # Common Safety Levels
2191    ///
2192    /// - `"strict"` - Apply strict content filtering (highest safety)
2193    /// - `"moderate"` - Apply moderate content filtering (balanced approach)
2194    /// - `"permissive"` - Apply permissive content filtering (minimal restrictions)
2195    /// - `"default"` - Use system default safety settings
2196    ///
2197    /// # Returns
2198    ///
2199    /// A mutable reference to self for method chaining
2200    ///
2201    /// # Use Cases
2202    ///
2203    /// - Educational content requiring strict filtering
2204    /// - Business applications with moderate restrictions
2205    /// - Research applications needing broader content access
2206    ///
2207    /// # Examples
2208    ///
2209    /// ```rust
2210    /// use openai_tools::responses::request::Responses;
2211    ///
2212    /// let mut client = Responses::new();
2213    /// client.safety_identifier("strict");     // High safety for education
2214    /// client.safety_identifier("moderate");   // Balanced for general use
2215    /// client.safety_identifier("permissive"); // Minimal restrictions
2216    /// ```
2217    pub fn safety_identifier<T: AsRef<str>>(&mut self, safety_id: T) -> &mut Self {
2218        self.request_body.safety_identifier = Some(safety_id.as_ref().to_string());
2219        self
2220    }
2221
2222    /// Sets the service tier for request processing priority and features
2223    ///
2224    /// Specifies the service tier for the request, which affects processing
2225    /// priority, rate limits, pricing, and available features. Different tiers
2226    /// provide different levels of service quality and capabilities.
2227    ///
2228    /// # Arguments
2229    ///
2230    /// * `tier` - The service tier identifier
2231    ///
2232    /// # Common Service Tiers
2233    ///
2234    /// - `"default"` - Standard service tier with regular priority
2235    /// - `"scale"` - High-throughput tier optimized for bulk processing
2236    /// - `"premium"` - Premium service tier with enhanced features and priority
2237    /// - `"enterprise"` - Enterprise tier with dedicated resources
2238    ///
2239    /// # Returns
2240    ///
2241    /// A mutable reference to self for method chaining
2242    ///
2243    /// # Considerations
2244    ///
2245    /// - Higher tiers may have different pricing structures
2246    /// - Some features may only be available in certain tiers
2247    /// - Rate limits and quotas may vary by tier
2248    ///
2249    /// # Examples
2250    ///
2251    /// ```rust
2252    /// use openai_tools::responses::request::Responses;
2253    ///
2254    /// let mut client = Responses::new();
2255    /// client.service_tier("default");   // Standard service
2256    /// client.service_tier("scale");     // High-throughput processing
2257    /// client.service_tier("premium");   // Premium features and priority
2258    /// ```
2259    pub fn service_tier<T: AsRef<str>>(&mut self, tier: T) -> &mut Self {
2260        self.request_body.service_tier = Some(tier.as_ref().to_string());
2261        self
2262    }
2263
2264    /// Enables or disables conversation storage
2265    ///
2266    /// Controls whether the conversation may be stored for future reference,
2267    /// training, or analytics purposes. This setting affects data retention
2268    /// and privacy policies.
2269    ///
2270    /// # Arguments
2271    ///
2272    /// * `enable` - Whether to allow conversation storage
2273    ///   - `true`: Allow storage for training, analytics, etc.
2274    ///   - `false`: Explicitly opt-out of storage
2275    ///
2276    /// # Privacy Considerations
2277    ///
2278    /// - **Enabled storage**: Conversation may be retained according to service policies
2279    /// - **Disabled storage**: Request explicit deletion after processing
2280    /// - **Default behavior**: Varies by service configuration
2281    ///
2282    /// # Returns
2283    ///
2284    /// A mutable reference to self for method chaining
2285    ///
2286    /// # Use Cases
2287    ///
2288    /// - **Enable**: Contributing to model improvement, analytics
2289    /// - **Disable**: Sensitive data, privacy-critical applications
2290    ///
2291    /// # Examples
2292    ///
2293    /// ```rust
2294    /// use openai_tools::responses::request::Responses;
2295    ///
2296    /// let mut client = Responses::new();
2297    /// client.store(false);  // Opt-out of storage for privacy
2298    /// client.store(true);   // Allow storage for improvement
2299    /// ```
2300    pub fn store(&mut self, enable: bool) -> &mut Self {
2301        self.request_body.store = Some(enable);
2302        self
2303    }
2304
2305    /// Enables or disables streaming responses
2306    ///
2307    /// When enabled, the response will be streamed back in chunks as it's
2308    /// generated, allowing for real-time display of partial results instead
2309    /// of waiting for the complete response.
2310    ///
2311    /// # Arguments
2312    ///
2313    /// * `enable` - Whether to enable streaming
2314    ///   - `true`: Stream response in real-time chunks
2315    ///   - `false`: Wait for complete response before returning
2316    ///
2317    /// # Returns
2318    ///
2319    /// A mutable reference to self for method chaining
2320    ///
2321    /// # Use Cases
2322    ///
2323    /// - **Enable streaming**: Real-time chat interfaces, live text generation
2324    /// - **Disable streaming**: Batch processing, when complete response is needed
2325    ///
2326    /// # Implementation Notes
2327    ///
2328    /// - Streaming responses require different handling in client code
2329    /// - May affect some response features or formatting options
2330    /// - Typically used with `stream_options()` for additional configuration
2331    ///
2332    /// # Examples
2333    ///
2334    /// ```rust
2335    /// use openai_tools::responses::request::Responses;
2336    ///
2337    /// let mut client = Responses::new();
2338    /// client.stream(true);   // Enable real-time streaming
2339    /// client.stream(false);  // Wait for complete response
2340    /// ```
2341    pub fn stream(&mut self, enable: bool) -> &mut Self {
2342        self.request_body.stream = Some(enable);
2343        self
2344    }
2345
2346    /// Configures streaming response options
2347    ///
2348    /// Additional options for controlling streaming response behavior,
2349    /// such as whether to include obfuscated placeholder content during
2350    /// the streaming process.
2351    ///
2352    /// # Arguments
2353    ///
2354    /// * `include_obfuscation` - Whether to include obfuscated content
2355    ///   - `true`: Include placeholder/obfuscated content in streams
2356    ///   - `false`: Only include final, non-obfuscated content
2357    ///
2358    /// # Returns
2359    ///
2360    /// A mutable reference to self for method chaining
2361    ///
2362    /// # Relevance
2363    ///
2364    /// This setting is only meaningful when `stream(true)` is also set.
2365    /// It has no effect on non-streaming responses.
2366    ///
2367    /// # Use Cases
2368    ///
2369    /// - **Include obfuscation**: Better user experience with placeholder content
2370    /// - **Exclude obfuscation**: Cleaner streams with only final content
2371    ///
2372    /// # Examples
2373    ///
2374    /// ```rust
2375    /// use openai_tools::responses::request::Responses;
2376    ///
2377    /// let mut client = Responses::new();
2378    /// client.stream(true);                    // Enable streaming
2379    /// client.stream_options(true);            // Include placeholder content
2380    /// client.stream_options(false);           // Only final content
2381    /// ```
2382    pub fn stream_options(&mut self, include_obfuscation: bool) -> &mut Self {
2383        self.request_body.stream_options = Some(StreamOptions { include_obfuscation });
2384        self
2385    }
2386
2387    /// Sets the number of top log probabilities to include in the response
2388    ///
2389    /// Specifies how many of the most likely alternative tokens to include
2390    /// with their log probabilities for each generated token. This provides
2391    /// insight into the model's confidence and alternative choices.
2392    ///
2393    /// # Arguments
2394    ///
2395    /// * `n` - Number of top alternatives to include (typically 1-20)
2396    ///   - `0`: No log probabilities included
2397    ///   - `1-5`: Common range for most use cases
2398    ///   - `>5`: Detailed analysis scenarios
2399    ///
2400    /// # Returns
2401    ///
2402    /// A mutable reference to self for method chaining
2403    ///
2404    /// # Use Cases
2405    ///
2406    /// - **Model analysis**: Understanding model decision-making
2407    /// - **Confidence estimation**: Measuring response certainty
2408    /// - **Alternative exploration**: Seeing what else the model considered
2409    /// - **Debugging**: Analyzing unexpected model behavior
2410    ///
2411    /// # Performance Note
2412    ///
2413    /// Higher values increase response size and may affect latency.
2414    ///
2415    /// **Note:** Reasoning models (GPT-6, GPT-5, o-series) do not support top_logprobs.
2416    /// For these models, this parameter will be ignored with a warning.
2417    ///
2418    /// # Examples
2419    ///
2420    /// ```rust
2421    /// use openai_tools::responses::request::Responses;
2422    ///
2423    /// let mut client = Responses::new();
2424    /// client.top_logprobs(1);   // Include top alternative for each token
2425    /// client.top_logprobs(5);   // Include top 5 alternatives (detailed analysis)
2426    /// client.top_logprobs(0);   // No log probabilities
2427    /// ```
2428    pub fn top_logprobs(&mut self, n: usize) -> &mut Self {
2429        let support = self.request_body.model.parameter_support();
2430        if !support.top_logprobs {
2431            tracing::warn!("Model '{}' does not support top_logprobs parameter. Ignoring.", self.request_body.model);
2432            return self;
2433        }
2434        self.request_body.top_logprobs = Some(n);
2435        self
2436    }
2437
2438    /// Sets the nucleus sampling parameter for controlling response diversity
2439    ///
2440    /// Controls the randomness of the model's responses by limiting the
2441    /// cumulative probability of considered tokens. This is an alternative
2442    /// to temperature-based sampling that can provide more stable results.
2443    ///
2444    /// # Arguments
2445    ///
2446    /// * `p` - The nucleus sampling parameter (0.0 to 1.0)
2447    ///   - `0.1`: Very focused, deterministic responses
2448    ///   - `0.7`: Balanced creativity and focus (good default)
2449    ///   - `0.9`: More diverse and creative responses
2450    ///   - `1.0`: Consider all possible tokens (no truncation)
2451    ///
2452    /// # Returns
2453    ///
2454    /// A mutable reference to self for method chaining
2455    ///
2456    /// # How It Works
2457    ///
2458    /// The model considers only the tokens whose cumulative probability
2459    /// reaches the specified threshold, filtering out unlikely options.
2460    ///
2461    /// # Interaction with Temperature
2462    ///
2463    /// Can be used together with `temperature()` for fine-tuned control:
2464    /// - Low top_p + Low temperature = Very focused responses
2465    /// - High top_p + High temperature = Very creative responses
2466    ///
2467    /// **Note:** Reasoning models (GPT-6, GPT-5, o-series) only support top_p=1.0.
2468    /// For these models, other values will be ignored with a warning.
2469    ///
2470    /// # Examples
2471    ///
2472    /// ```rust
2473    /// use openai_tools::responses::request::Responses;
2474    ///
2475    /// let mut client = Responses::new();
2476    /// client.top_p(0.1);   // Very focused responses
2477    /// client.top_p(0.7);   // Balanced (recommended default)
2478    /// client.top_p(0.95);  // High diversity
2479    /// ```
2480    pub fn top_p(&mut self, p: f64) -> &mut Self {
2481        let support = self.request_body.model.parameter_support();
2482        match support.top_p {
2483            ParameterRestriction::FixedValue(fixed) => {
2484                if (p - fixed).abs() > f64::EPSILON {
2485                    tracing::warn!("Model '{}' only supports top_p={}. Ignoring top_p={}.", self.request_body.model, fixed, p);
2486                    return self;
2487                }
2488            }
2489            ParameterRestriction::NotSupported => {
2490                tracing::warn!("Model '{}' does not support top_p parameter. Ignoring.", self.request_body.model);
2491                return self;
2492            }
2493            ParameterRestriction::Any => {}
2494        }
2495        self.request_body.top_p = Some(p);
2496        self
2497    }
2498
2499    /// Sets the truncation behavior for handling long inputs
2500    ///
2501    /// Controls how the system handles inputs that exceed the maximum
2502    /// context length supported by the model. This helps manage cases
2503    /// where input content is too large to process entirely.
2504    ///
2505    /// # Arguments
2506    ///
2507    /// * `truncation` - The truncation mode to use:
2508    ///   - `Truncation::Auto`: Automatically truncate long inputs to fit
2509    ///   - `Truncation::Disabled`: Return error if input exceeds context length
2510    ///
2511    /// # Returns
2512    ///
2513    /// A mutable reference to self for method chaining
2514    ///
2515    /// # Use Cases
2516    ///
2517    /// - **Auto truncation**: When you want to handle long documents gracefully
2518    /// - **Disabled truncation**: When you need to ensure complete input processing
2519    ///
2520    /// # Considerations
2521    ///
2522    /// - Auto truncation may remove important context from long inputs
2523    /// - Disabled truncation ensures complete processing but may cause errors
2524    /// - Consider breaking long inputs into smaller chunks when possible
2525    ///
2526    /// # Examples
2527    ///
2528    /// ```rust
2529    /// use openai_tools::responses::request::{Responses, Truncation};
2530    ///
2531    /// let mut client = Responses::new();
2532    /// client.truncation(Truncation::Auto);      // Handle long inputs gracefully
2533    /// client.truncation(Truncation::Disabled);  // Ensure complete processing
2534    /// ```
2535    pub fn truncation(&mut self, truncation: Truncation) -> &mut Self {
2536        self.request_body.truncation = Some(truncation);
2537        self
2538    }
2539
2540    /// Checks if the model is a reasoning model that doesn't support custom temperature
2541    ///
2542    /// Reasoning models (o1, o3, o4 series) only support the default temperature value of 1.0.
2543    /// This method checks if the current model is one of these reasoning models.
2544    ///
2545    /// # Returns
2546    ///
2547    /// `true` if the model is a reasoning model, `false` otherwise
2548    ///
2549    /// # Supported Reasoning Models
2550    ///
2551    /// - `o1`, `o1-pro`, and variants
2552    /// - `o3`, `o3-mini`, and variants
2553    /// - `o4-mini` and variants
2554    fn is_reasoning_model(&self) -> bool {
2555        self.request_body.model.is_reasoning_model()
2556    }
2557
2558    /// Executes the request and returns the response
2559    ///
2560    /// This method sends the configured request to the OpenAI Responses API
2561    /// and returns the parsed response. It performs validation of required
2562    /// fields before sending the request.
2563    ///
2564    /// # Returns
2565    ///
2566    /// A `Result` containing the `Response` on success, or an `OpenAIToolError` on failure
2567    ///
2568    /// # Errors
2569    ///
2570    /// Returns an error if:
2571    /// - The API key is not set or is empty
2572    /// - The model ID is not set or is empty
2573    /// - Neither messages nor plain text input is provided
2574    /// - Both messages and plain text input are provided (mutually exclusive)
2575    /// - The HTTP request fails
2576    /// - The response cannot be parsed
2577    ///
2578    /// # Parameter Validation
2579    ///
2580    /// For reasoning models (GPT-6, GPT-5, o-series), certain parameters have restrictions:
2581    /// - `temperature`: only 1.0 supported
2582    /// - `top_p`: only 1.0 supported
2583    /// - `top_logprobs`: not supported
2584    ///
2585    /// **Validation occurs at two points:**
2586    /// 1. At setter time (when using `with_model()` constructor) - immediate warning
2587    /// 2. At API call time (fallback) - for cases where model is changed after setting params
2588    ///
2589    /// Unsupported parameter values are ignored with a warning and the request proceeds.
2590    ///
2591    /// # Examples
2592    ///
2593    /// ```rust,no_run
2594    /// use openai_tools::responses::request::Responses;
2595    ///
2596    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2597    /// let mut client = Responses::new();
2598    /// let response = client
2599    ///     .model_id("gpt-4")
2600    ///     .str_message("Hello!")
2601    ///     .complete()
2602    ///     .await?;
2603    /// # Ok(())
2604    /// # }
2605    /// ```
2606    pub async fn complete(&self) -> Result<Response> {
2607        // Validate that either messages or plain text input is set
2608        if self.request_body.messages_input.is_none() && self.request_body.plain_text_input.is_none() {
2609            return Err(OpenAIToolError::Error("Messages are not set.".into()));
2610        } else if self.request_body.plain_text_input.is_none() && self.request_body.messages_input.is_none() {
2611            return Err(OpenAIToolError::Error("Both plain text input and messages are set. Please use one of them.".into()));
2612        }
2613
2614        // Handle reasoning models that don't support certain parameters
2615        // See: https://platform.openai.com/docs/guides/reasoning
2616        let mut request_body = self.request_body.clone();
2617        if self.is_reasoning_model() {
2618            let model = &self.request_body.model;
2619
2620            // Temperature: only default (1.0) is supported
2621            if let Some(temp) = request_body.temperature {
2622                if (temp - 1.0).abs() > f64::EPSILON {
2623                    tracing::warn!(
2624                        "Reasoning model '{}' does not support custom temperature. \
2625                         Ignoring temperature={} and using default (1.0).",
2626                        model,
2627                        temp
2628                    );
2629                    request_body.temperature = None;
2630                }
2631            }
2632
2633            // Top P: only default (1.0) is supported
2634            if let Some(top_p) = request_body.top_p {
2635                if (top_p - 1.0).abs() > f64::EPSILON {
2636                    tracing::warn!(
2637                        "Reasoning model '{}' does not support custom top_p. \
2638                         Ignoring top_p={} and using default (1.0).",
2639                        model,
2640                        top_p
2641                    );
2642                    request_body.top_p = None;
2643                }
2644            }
2645
2646            // Top logprobs: not supported
2647            if request_body.top_logprobs.is_some() {
2648                tracing::warn!("Reasoning model '{}' does not support top_logprobs. Ignoring top_logprobs parameter.", model);
2649                request_body.top_logprobs = None;
2650            }
2651        }
2652
2653        let body = serde_json::to_string(&request_body)?;
2654
2655        let client = create_http_client(self.timeout)?;
2656
2657        // Set up headers
2658        let mut headers = request::header::HeaderMap::new();
2659        headers.insert("Content-Type", request::header::HeaderValue::from_static("application/json"));
2660        if !self.user_agent.is_empty() {
2661            headers.insert("User-Agent", request::header::HeaderValue::from_str(&self.user_agent).unwrap());
2662        }
2663
2664        // Apply provider-specific authentication headers
2665        self.auth.apply_headers(&mut headers)?;
2666
2667        // Get the endpoint URL from the auth provider
2668        let endpoint = self.auth.endpoint(RESPONSES_PATH);
2669
2670        if cfg!(test) {
2671            tracing::info!("Endpoint: {}", endpoint);
2672            // Replace API key with a placeholder for security
2673            let body_for_debug = serde_json::to_string_pretty(&request_body).unwrap().replace(self.auth.api_key(), "*************");
2674            // Log the request body for debugging purposes
2675            tracing::info!("Request body: {}", body_for_debug);
2676        }
2677
2678        // Send the request and handle the response
2679        match client.post(&endpoint).headers(headers).body(body).send().await.map_err(OpenAIToolError::RequestError) {
2680            Err(e) => {
2681                tracing::error!("Request error: {}", e);
2682                Err(e)
2683            }
2684            Ok(response) if !response.status().is_success() => {
2685                let status = response.status();
2686                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
2687                tracing::error!("API error (status: {}): {}", status, error_text);
2688                Err(OpenAIToolError::Error(format!("API request failed with status {}: {}", status, error_text)))
2689            }
2690            Ok(response) => {
2691                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
2692
2693                if cfg!(test) {
2694                    tracing::info!("Response content: {}", content);
2695                }
2696
2697                serde_json::from_str::<Response>(&content).map_err(OpenAIToolError::SerdeJsonError)
2698            }
2699        }
2700    }
2701
2702    // ========================================
2703    // CRUD Endpoint Methods
2704    // ========================================
2705
2706    /// Creates the HTTP client and headers for API requests
2707    ///
2708    /// This is a helper method that sets up the common HTTP client and headers
2709    /// needed for all API requests.
2710    ///
2711    /// # Returns
2712    ///
2713    /// A tuple of the HTTP client and headers
2714    fn create_api_client(&self) -> Result<(request::Client, request::header::HeaderMap)> {
2715        let client = create_http_client(self.timeout)?;
2716        let mut headers = request::header::HeaderMap::new();
2717        headers.insert("Content-Type", request::header::HeaderValue::from_static("application/json"));
2718        if !self.user_agent.is_empty() {
2719            headers.insert(
2720                "User-Agent",
2721                request::header::HeaderValue::from_str(&self.user_agent).map_err(|e| OpenAIToolError::Error(format!("Invalid user agent: {}", e)))?,
2722            );
2723        }
2724        self.auth.apply_headers(&mut headers)?;
2725        Ok((client, headers))
2726    }
2727
2728    /// Handles API error responses
2729    ///
2730    /// This is a helper method that formats API error responses into a
2731    /// standardized error type.
2732    ///
2733    /// # Arguments
2734    ///
2735    /// * `status` - The HTTP status code
2736    /// * `content` - The error response content
2737    ///
2738    /// # Returns
2739    ///
2740    /// An OpenAIToolError containing the error details
2741    fn handle_api_error(status: request::StatusCode, content: &str) -> OpenAIToolError {
2742        tracing::error!("API error (status: {}): {}", status, content);
2743        OpenAIToolError::Error(format!("API request failed with status {}: {}", status, content))
2744    }
2745
2746    /// Retrieves a response by its ID
2747    ///
2748    /// Fetches the details of a specific response, including its output,
2749    /// status, and metadata.
2750    ///
2751    /// # Arguments
2752    ///
2753    /// * `response_id` - The ID of the response to retrieve
2754    ///
2755    /// # Returns
2756    ///
2757    /// A `Result` containing the `Response` on success
2758    ///
2759    /// # API Reference
2760    ///
2761    /// <https://platform.openai.com/docs/api-reference/responses/get>
2762    ///
2763    /// # Examples
2764    ///
2765    /// ```rust,no_run
2766    /// use openai_tools::responses::request::Responses;
2767    ///
2768    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2769    /// let client = Responses::new();
2770    /// let response = client.retrieve("resp_abc123").await?;
2771    /// println!("Status: {:?}", response.status);
2772    /// # Ok(())
2773    /// # }
2774    /// ```
2775    pub async fn retrieve(&self, response_id: &str) -> Result<Response> {
2776        let (client, headers) = self.create_api_client()?;
2777        let endpoint = format!("{}/{}", self.auth.endpoint(RESPONSES_PATH), response_id);
2778
2779        match client.get(&endpoint).headers(headers).send().await.map_err(OpenAIToolError::RequestError) {
2780            Err(e) => {
2781                tracing::error!("Request error: {}", e);
2782                Err(e)
2783            }
2784            Ok(response) if !response.status().is_success() => {
2785                let status = response.status();
2786                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
2787                Err(Self::handle_api_error(status, &error_text))
2788            }
2789            Ok(response) => {
2790                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
2791                serde_json::from_str::<Response>(&content).map_err(OpenAIToolError::SerdeJsonError)
2792            }
2793        }
2794    }
2795
2796    /// Deletes a response by its ID
2797    ///
2798    /// Permanently removes a response from the OpenAI platform.
2799    ///
2800    /// # Arguments
2801    ///
2802    /// * `response_id` - The ID of the response to delete
2803    ///
2804    /// # Returns
2805    ///
2806    /// A `Result` containing `DeleteResponseResult` on success
2807    ///
2808    /// # API Reference
2809    ///
2810    /// <https://platform.openai.com/docs/api-reference/responses/delete>
2811    ///
2812    /// # Examples
2813    ///
2814    /// ```rust,no_run
2815    /// use openai_tools::responses::request::Responses;
2816    ///
2817    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2818    /// let client = Responses::new();
2819    /// let result = client.delete("resp_abc123").await?;
2820    /// assert!(result.deleted);
2821    /// # Ok(())
2822    /// # }
2823    /// ```
2824    pub async fn delete(&self, response_id: &str) -> Result<DeleteResponseResult> {
2825        let (client, headers) = self.create_api_client()?;
2826        let endpoint = format!("{}/{}", self.auth.endpoint(RESPONSES_PATH), response_id);
2827
2828        match client.delete(&endpoint).headers(headers).send().await.map_err(OpenAIToolError::RequestError) {
2829            Err(e) => {
2830                tracing::error!("Request error: {}", e);
2831                Err(e)
2832            }
2833            Ok(response) if !response.status().is_success() => {
2834                let status = response.status();
2835                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
2836                Err(Self::handle_api_error(status, &error_text))
2837            }
2838            Ok(response) => {
2839                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
2840                serde_json::from_str::<DeleteResponseResult>(&content).map_err(OpenAIToolError::SerdeJsonError)
2841            }
2842        }
2843    }
2844
2845    /// Cancels an in-progress response
2846    ///
2847    /// Cancels a response that is currently being generated. This is useful
2848    /// for background responses that are taking too long.
2849    ///
2850    /// # Arguments
2851    ///
2852    /// * `response_id` - The ID of the response to cancel
2853    ///
2854    /// # Returns
2855    ///
2856    /// A `Result` containing the cancelled `Response` on success
2857    ///
2858    /// # API Reference
2859    ///
2860    /// <https://platform.openai.com/docs/api-reference/responses/cancel>
2861    ///
2862    /// # Examples
2863    ///
2864    /// ```rust,no_run
2865    /// use openai_tools::responses::request::Responses;
2866    ///
2867    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2868    /// let client = Responses::new();
2869    /// let response = client.cancel("resp_abc123").await?;
2870    /// println!("Response cancelled: {:?}", response.status);
2871    /// # Ok(())
2872    /// # }
2873    /// ```
2874    pub async fn cancel(&self, response_id: &str) -> Result<Response> {
2875        let (client, headers) = self.create_api_client()?;
2876        let endpoint = format!("{}/{}/cancel", self.auth.endpoint(RESPONSES_PATH), response_id);
2877
2878        match client.post(&endpoint).headers(headers).send().await.map_err(OpenAIToolError::RequestError) {
2879            Err(e) => {
2880                tracing::error!("Request error: {}", e);
2881                Err(e)
2882            }
2883            Ok(response) if !response.status().is_success() => {
2884                let status = response.status();
2885                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
2886                Err(Self::handle_api_error(status, &error_text))
2887            }
2888            Ok(response) => {
2889                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
2890                serde_json::from_str::<Response>(&content).map_err(OpenAIToolError::SerdeJsonError)
2891            }
2892        }
2893    }
2894
2895    /// Lists input items for a response
2896    ///
2897    /// Retrieves a paginated list of input items that were part of the request
2898    /// that generated the response.
2899    ///
2900    /// # Arguments
2901    ///
2902    /// * `response_id` - The ID of the response to get input items for
2903    /// * `limit` - Maximum number of items to return (default: 20)
2904    /// * `after` - Cursor for pagination (return items after this ID)
2905    /// * `before` - Cursor for pagination (return items before this ID)
2906    ///
2907    /// # Returns
2908    ///
2909    /// A `Result` containing `InputItemsListResponse` on success
2910    ///
2911    /// # API Reference
2912    ///
2913    /// <https://platform.openai.com/docs/api-reference/responses/list-input-items>
2914    ///
2915    /// # Examples
2916    ///
2917    /// ```rust,no_run
2918    /// use openai_tools::responses::request::Responses;
2919    ///
2920    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2921    /// let client = Responses::new();
2922    /// let items = client.list_input_items("resp_abc123", Some(10), None, None).await?;
2923    /// for item in items.data {
2924    ///     println!("Item: {} (type: {})", item.id, item.item_type);
2925    /// }
2926    /// # Ok(())
2927    /// # }
2928    /// ```
2929    pub async fn list_input_items(
2930        &self,
2931        response_id: &str,
2932        limit: Option<u32>,
2933        after: Option<&str>,
2934        before: Option<&str>,
2935    ) -> Result<InputItemsListResponse> {
2936        let (client, headers) = self.create_api_client()?;
2937        let base_endpoint = format!("{}/{}/input_items", self.auth.endpoint(RESPONSES_PATH), response_id);
2938
2939        // Build query parameters
2940        let mut query_params = Vec::new();
2941        if let Some(limit) = limit {
2942            query_params.push(format!("limit={}", limit));
2943        }
2944        if let Some(after) = after {
2945            query_params.push(format!("after={}", after));
2946        }
2947        if let Some(before) = before {
2948            query_params.push(format!("before={}", before));
2949        }
2950
2951        let endpoint = if query_params.is_empty() { base_endpoint } else { format!("{}?{}", base_endpoint, query_params.join("&")) };
2952
2953        match client.get(&endpoint).headers(headers).send().await.map_err(OpenAIToolError::RequestError) {
2954            Err(e) => {
2955                tracing::error!("Request error: {}", e);
2956                Err(e)
2957            }
2958            Ok(response) if !response.status().is_success() => {
2959                let status = response.status();
2960                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
2961                Err(Self::handle_api_error(status, &error_text))
2962            }
2963            Ok(response) => {
2964                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
2965                serde_json::from_str::<InputItemsListResponse>(&content).map_err(OpenAIToolError::SerdeJsonError)
2966            }
2967        }
2968    }
2969
2970    /// Compacts a response to reduce its size
2971    ///
2972    /// Creates a compacted version of a response, which can be useful
2973    /// for long-running conversations to reduce token usage.
2974    ///
2975    /// # Arguments
2976    ///
2977    /// * `previous_response_id` - The ID of the response to compact
2978    /// * `model` - Optional model to use for compaction (defaults to original model)
2979    ///
2980    /// # Returns
2981    ///
2982    /// A `Result` containing `CompactedResponse` on success
2983    ///
2984    /// # API Reference
2985    ///
2986    /// <https://platform.openai.com/docs/api-reference/responses/compact>
2987    ///
2988    /// # Examples
2989    ///
2990    /// ```rust,no_run
2991    /// use openai_tools::responses::request::Responses;
2992    ///
2993    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2994    /// let client = Responses::new();
2995    /// let compacted = client.compact("resp_abc123", None).await?;
2996    /// println!("Compacted response ID: {}", compacted.id);
2997    /// # Ok(())
2998    /// # }
2999    /// ```
3000    pub async fn compact(&self, previous_response_id: &str, model: Option<&str>) -> Result<CompactedResponse> {
3001        let (client, headers) = self.create_api_client()?;
3002        let endpoint = format!("{}/compact", self.auth.endpoint(RESPONSES_PATH));
3003
3004        // Build request body
3005        let mut body = serde_json::json!({
3006            "previous_response_id": previous_response_id
3007        });
3008        if let Some(model) = model {
3009            body["model"] = serde_json::json!(model);
3010        }
3011
3012        match client.post(&endpoint).headers(headers).body(serde_json::to_string(&body)?).send().await.map_err(OpenAIToolError::RequestError) {
3013            Err(e) => {
3014                tracing::error!("Request error: {}", e);
3015                Err(e)
3016            }
3017            Ok(response) if !response.status().is_success() => {
3018                let status = response.status();
3019                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
3020                Err(Self::handle_api_error(status, &error_text))
3021            }
3022            Ok(response) => {
3023                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
3024                serde_json::from_str::<CompactedResponse>(&content).map_err(OpenAIToolError::SerdeJsonError)
3025            }
3026        }
3027    }
3028
3029    /// Counts the number of input tokens for a potential request
3030    ///
3031    /// Useful for estimating token usage before sending a request.
3032    ///
3033    /// # Arguments
3034    ///
3035    /// * `model` - The model to use for token counting
3036    /// * `input` - The input to count tokens for (can be a string or messages array)
3037    ///
3038    /// # Returns
3039    ///
3040    /// A `Result` containing `InputTokensResponse` on success
3041    ///
3042    /// # API Reference
3043    ///
3044    /// <https://platform.openai.com/docs/api-reference/responses/input-tokens>
3045    ///
3046    /// # Examples
3047    ///
3048    /// ```rust,no_run
3049    /// use openai_tools::responses::request::Responses;
3050    /// use serde_json::json;
3051    ///
3052    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3053    /// let client = Responses::new();
3054    /// let tokens = client.get_input_tokens("gpt-4o-mini", json!("Hello, world!")).await?;
3055    /// println!("Input tokens: {}", tokens.input_tokens);
3056    /// # Ok(())
3057    /// # }
3058    /// ```
3059    pub async fn get_input_tokens(&self, model: &str, input: serde_json::Value) -> Result<InputTokensResponse> {
3060        let (client, headers) = self.create_api_client()?;
3061        let endpoint = format!("{}/input_tokens", self.auth.endpoint(RESPONSES_PATH));
3062
3063        let body = serde_json::json!({
3064            "model": model,
3065            "input": input
3066        });
3067
3068        match client.post(&endpoint).headers(headers).body(serde_json::to_string(&body)?).send().await.map_err(OpenAIToolError::RequestError) {
3069            Err(e) => {
3070                tracing::error!("Request error: {}", e);
3071                Err(e)
3072            }
3073            Ok(response) if !response.status().is_success() => {
3074                let status = response.status();
3075                let error_text = response.text().await.unwrap_or_else(|_| "Failed to read error response".to_string());
3076                Err(Self::handle_api_error(status, &error_text))
3077            }
3078            Ok(response) => {
3079                let content = response.text().await.map_err(OpenAIToolError::RequestError)?;
3080                serde_json::from_str::<InputTokensResponse>(&content).map_err(OpenAIToolError::SerdeJsonError)
3081            }
3082        }
3083    }
3084}
3085
3086#[cfg(test)]
3087mod tests {
3088    use super::ReasoningEffort;
3089
3090    #[test]
3091    fn reasoning_effort_max_serializes_as_max() {
3092        assert_eq!(serde_json::to_string(&ReasoningEffort::Max).unwrap(), "\"max\"");
3093        assert_eq!(ReasoningEffort::Max.to_string(), "max");
3094        assert_eq!("max".parse::<ReasoningEffort>().unwrap(), ReasoningEffort::Max);
3095    }
3096}