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