Skip to main content

openai_tools/responses/
request.rs

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