openai_sdk_rs/types/
responses.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4pub struct ResponsesRequest {
5    pub model: String,
6    #[serde(skip_serializing_if = "Option::is_none")]
7    pub input: Option<serde_json::Value>,
8    #[serde(skip_serializing_if = "Option::is_none")]
9    pub temperature: Option<f32>,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub top_p: Option<f32>,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub max_output_tokens: Option<u32>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub user: Option<String>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub stream: Option<bool>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub stream_options: Option<StreamOptions>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub tools: Option<Vec<ToolSpec>>, // tool calling
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub tool_choice: Option<serde_json::Value>, // leave generic for now
24    // 新增参数
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub instructions: Option<String>, // System instructions
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub metadata: Option<serde_json::Value>, // Request metadata
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub parallel_tool_calls: Option<bool>, // Whether to enable parallel tool calls
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub text: Option<TextConfig>, // Text configuration (replaces response_format)
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub seed: Option<i32>, // Random seed for deterministic outputs
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub store: Option<bool>, // Whether to store the conversation
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub reasoning: Option<ReasoningConfig>, // Reasoning configuration
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub top_logprobs: Option<u32>, // Number of most likely tokens to return at each position
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub logprobs: Option<bool>, // Whether to return log probabilities
43}
44
45impl ResponsesRequest {
46    pub fn text<T: Into<String>>(model: T, input: T) -> Self {
47        Self {
48            model: model.into(),
49            input: Some(serde_json::Value::String(input.into())),
50            ..Default::default()
51        }
52    }
53
54    // 创建带有系统指令的请求
55    pub fn with_instructions<T: Into<String>>(model: T, input: T, instructions: T) -> Self {
56        Self {
57            model: model.into(),
58            input: Some(serde_json::Value::String(input.into())),
59            instructions: Some(instructions.into()),
60            ..Default::default()
61        }
62    }
63
64    // 创建需要 JSON 响应的请求
65    pub fn json<T: Into<String>>(model: T, input: T) -> Self {
66        Self {
67            model: model.into(),
68            input: Some(serde_json::Value::String(input.into())),
69            text: Some(TextConfig {
70                format: Some(TextFormat::JsonObject),
71                verbosity: None,
72            }),
73            ..Default::default()
74        }
75    }
76
77    // 创建带有特定温度的请求
78    pub fn with_temperature<T: Into<String>>(model: T, input: T, temperature: f32) -> Self {
79        Self {
80            model: model.into(),
81            input: Some(serde_json::Value::String(input.into())),
82            temperature: Some(temperature),
83            ..Default::default()
84        }
85    }
86
87    // 设置最大输出 tokens
88    pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
89        self.max_output_tokens = Some(max_tokens);
90        self
91    }
92
93    // 设置种子以获得确定性输出
94    pub fn with_seed(mut self, seed: i32) -> Self {
95        self.seed = Some(seed);
96        self
97    }
98
99    // 启用并行工具调用
100    pub fn with_parallel_tools(mut self, parallel: bool) -> Self {
101        self.parallel_tool_calls = Some(parallel);
102        self
103    }
104
105    // 添加工具
106    pub fn with_tools(mut self, tools: Vec<ToolSpec>) -> Self {
107        self.tools = Some(tools);
108        self
109    }
110
111    // 设置工具选择策略
112    pub fn with_tool_choice<T: serde::Serialize>(mut self, choice: T) -> Self {
113        self.tool_choice = Some(serde_json::to_value(choice).unwrap_or(serde_json::Value::Null));
114        self
115    }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, Default)]
119pub struct StreamOptions {
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub include_usage: Option<bool>,
122}
123
124// Response format specification (legacy, kept for compatibility)
125#[derive(Debug, Clone, Serialize, Deserialize)]
126#[serde(tag = "type", rename_all = "snake_case")]
127pub enum ResponseFormat {
128    Text,
129    JsonObject,
130    JsonSchema { json_schema: JsonSchemaSpec },
131}
132
133// Text configuration for Responses API
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct TextConfig {
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub format: Option<TextFormat>,
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub verbosity: Option<String>, // "low", "medium", "high"
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(tag = "type", rename_all = "snake_case")]
144pub enum TextFormat {
145    Text,
146    JsonObject,
147    JsonSchema { json_schema: JsonSchemaSpec },
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct JsonSchemaSpec {
152    pub name: String,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub description: Option<String>,
155    pub schema: serde_json::Value,
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub strict: Option<bool>,
158}
159
160// Reasoning configuration
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ReasoningConfig {
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub effort: Option<String>, // "low", "medium", "high"
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub summary: Option<String>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct ResponsesResponse {
171    pub id: String,
172    pub object: String,
173    #[serde(default)]
174    pub created: Option<u64>,
175    pub model: String,
176    // Keep generic for forward-compat
177    #[serde(default)]
178    pub output: Option<serde_json::Value>,
179    #[serde(default)]
180    pub usage: Option<serde_json::Value>,
181}
182
183impl ResponsesResponse {
184    // Best-effort extraction of plain text from common shapes.
185    pub fn output_text(&self) -> Option<String> {
186        let v = self.output.as_ref()?;
187        // If it's already a string
188        if let Some(s) = v.as_str() {
189            return Some(s.to_string());
190        }
191        // Try known shapes (message/content/text)
192        // Many SDKs expose a convenience text; we attempt similar.
193        // This is intentionally lenient.
194        let mut buf = String::new();
195        collect_text(v, &mut buf);
196        if buf.is_empty() {
197            None
198        } else {
199            Some(buf)
200        }
201    }
202
203    pub fn output_json(&self) -> Option<serde_json::Value> {
204        let text = self.output_text()?;
205        serde_json::from_str(&text).ok()
206    }
207}
208
209fn collect_text(v: &serde_json::Value, out: &mut String) {
210    match v {
211        serde_json::Value::String(s) => {
212            if !out.is_empty() {
213                out.push_str("\n");
214            }
215            out.push_str(s);
216        }
217        serde_json::Value::Array(arr) => {
218            for item in arr {
219                collect_text(item, out);
220            }
221        }
222        serde_json::Value::Object(map) => {
223            for (k, val) in map {
224                if k.contains("text") || k == "content" {
225                    collect_text(val, out);
226                }
227            }
228        }
229        _ => {}
230    }
231}
232
233// Streaming events for Responses API; keep generic but typed by 'type'
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct ResponseStreamEvent {
236    #[serde(rename = "type")]
237    pub type_: String,
238    #[serde(default)]
239    pub response: Option<serde_json::Value>,
240    #[serde(default)]
241    pub output_text: Option<String>,
242    #[serde(default)]
243    pub delta: Option<serde_json::Value>,
244    #[serde(default)]
245    pub message: Option<serde_json::Value>,
246    #[serde(default)]
247    pub usage: Option<serde_json::Value>,
248}
249
250// Tool specification for function calling
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct ToolSpec {
253    #[serde(rename = "type")]
254    pub type_: String,
255    pub name: String,
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub description: Option<String>,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub parameters: Option<serde_json::Value>, // JSON Schema
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct FunctionCall {
264    pub name: String,
265    pub arguments: serde_json::Value,
266}
267
268impl super::responses::ResponsesResponse {
269    pub fn function_calls(&self) -> Vec<FunctionCall> {
270        let mut out = Vec::new();
271        if let Some(v) = &self.output {
272            collect_function_calls(v, &mut out);
273        }
274        out
275    }
276}
277
278impl super::responses::ResponseStreamEvent {
279    pub fn function_calls(&self) -> Vec<FunctionCall> {
280        let mut out = Vec::new();
281        if let Some(v) = &self.response {
282            collect_function_calls(v, &mut out);
283        }
284        if let Some(v) = &self.delta {
285            collect_function_calls(v, &mut out);
286        }
287        if let Some(v) = &self.message {
288            collect_function_calls(v, &mut out);
289        }
290        out
291    }
292}
293
294fn collect_function_calls(v: &serde_json::Value, out: &mut Vec<FunctionCall>) {
295    match v {
296        serde_json::Value::Object(map) => {
297            if let Some(serde_json::Value::String(t)) = map.get("type") {
298                if t == "function" {
299                    if let Some(f) = map.get("function") {
300                        if let Some(fc) = as_function_call(f) {
301                            out.push(fc);
302                        }
303                    }
304                    // Recurse other keys except `function` to find additional, avoid double count
305                    for (k, val) in map {
306                        if k != "function" {
307                            collect_function_calls(val, out);
308                        }
309                    }
310                    return;
311                }
312            }
313            if let Some(fc) = as_function_call(v) {
314                out.push(fc);
315                return;
316            }
317            for (_k, val) in map {
318                collect_function_calls(val, out);
319            }
320        }
321        serde_json::Value::Array(arr) => {
322            for item in arr {
323                collect_function_calls(item, out);
324            }
325        }
326        _ => {}
327    }
328}
329
330fn as_function_call(v: &serde_json::Value) -> Option<FunctionCall> {
331    let m = v.as_object()?;
332    if let Some(f) = m.get("function") {
333        let fobj = f.as_object()?;
334        let name = fobj.get("name")?.as_str()?.to_string();
335        let args = fobj
336            .get("arguments")
337            .cloned()
338            .unwrap_or(serde_json::Value::Null);
339        return Some(FunctionCall {
340            name,
341            arguments: normalize_args(args),
342        });
343    }
344    if let Some(name) = m.get("name").and_then(|s| s.as_str()) {
345        let args = m
346            .get("arguments")
347            .cloned()
348            .unwrap_or(serde_json::Value::Null);
349        return Some(FunctionCall {
350            name: name.to_string(),
351            arguments: normalize_args(args),
352        });
353    }
354    None
355}
356
357fn normalize_args(args: serde_json::Value) -> serde_json::Value {
358    match args {
359        serde_json::Value::String(s) => {
360            serde_json::from_str(&s).unwrap_or(serde_json::Value::String(s))
361        }
362        other => other,
363    }
364}