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