Skip to main content

openai_core/resources/
responses.rs

1//! Responses and Realtime namespace implementations and builders.
2
3use std::collections::BTreeMap;
4#[cfg(feature = "structured-output")]
5use std::marker::PhantomData;
6use std::time::Duration;
7
8use http::Method;
9#[cfg(feature = "structured-output")]
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use tokio_util::sync::CancellationToken;
14
15use crate::Client;
16use crate::config::RequestOptions;
17use crate::error::{Error, Result};
18use crate::generated::endpoints;
19#[cfg(feature = "structured-output")]
20use crate::helpers::{ParsedResponse, parse_json_payload};
21use crate::json_payload::JsonPayload;
22use crate::response_meta::ApiResponse;
23use crate::stream::{ResponseEventStream, ResponseStream};
24use crate::transport::{RequestSpec, merge_json_body};
25#[cfg(feature = "realtime")]
26use crate::websocket::RealtimeSocket;
27#[cfg(feature = "responses-ws")]
28use crate::websocket::ResponsesSocket;
29
30use super::{
31    ChatToolDefinition, ConversationItem, DeleteResponse, InputTokenCount, JsonRequestBuilder,
32    ListRequestBuilder, NoContentRequestBuilder, RealtimeCallsResource,
33    RealtimeClientSecretsResource, RealtimeResource, RealtimeSessionPayload, Response,
34    ResponseCreateParams, ResponseInputItemPayload, ResponseInputItemsResource,
35    ResponseInputPayload, ResponseInputTokensResource, ResponsesResource, encode_path_segment,
36    value_from,
37};
38
39/// Realtime API 返回的临时 client secret。
40#[derive(Debug, Clone, Serialize, Deserialize, Default)]
41pub struct RealtimeSessionClientSecret {
42    /// 过期时间。
43    pub expires_at: u64,
44    /// 可下发给前端的临时密钥。
45    #[serde(default)]
46    pub value: String,
47    /// 额外字段。
48    #[serde(flatten)]
49    pub extra: BTreeMap<String, Value>,
50}
51
52/// `realtime.client_secrets.create` 的返回值。
53#[derive(Debug, Clone, Serialize, Deserialize, Default)]
54pub struct RealtimeClientSecretCreateResponse {
55    /// 官方 documented spec 返回的顶层 secret 值。
56    pub value: Option<String>,
57    /// 官方 documented spec 返回的顶层过期时间。
58    pub expires_at: Option<u64>,
59    /// 新版结构化 client secret。
60    pub client_secret: Option<RealtimeSessionClientSecret>,
61    /// 某些兼容 Provider 返回的扁平 secret 字段。
62    pub secret: Option<String>,
63    /// 会话配置类型。
64    #[serde(rename = "type")]
65    pub session_type: Option<String>,
66    /// 生效后的会话配置。
67    pub session: Option<RealtimeSessionPayload>,
68    /// 额外字段。
69    #[serde(flatten)]
70    pub extra: BTreeMap<String, Value>,
71}
72
73impl RealtimeClientSecretCreateResponse {
74    /// 返回兼容不同 Provider 形态的 secret 值。
75    pub fn secret_value(&self) -> Option<&str> {
76        self.client_secret
77            .as_ref()
78            .map(|secret| secret.value.as_str())
79            .or(self.value.as_deref())
80            .or(self.secret.as_deref())
81    }
82}
83
84impl ResponsesResource {
85    /// 创建 responses 请求构建器。
86    pub fn create(&self) -> ResponseCreateRequestBuilder {
87        ResponseCreateRequestBuilder::new(self.client.clone())
88    }
89
90    /// 创建 responses 结构化解析构建器。
91    #[cfg(feature = "structured-output")]
92    #[cfg_attr(docsrs, doc(cfg(feature = "structured-output")))]
93    pub fn parse<T>(&self) -> ResponseParseRequestBuilder<T> {
94        ResponseParseRequestBuilder::new(self.client.clone())
95    }
96
97    /// 创建 responses 流式构建器。
98    pub fn stream(&self) -> ResponseStreamRequestBuilder {
99        ResponseStreamRequestBuilder::new(self.client.clone())
100    }
101
102    /// 按响应 ID 继续一个已有的 Responses SSE 流。
103    pub fn stream_response(&self, response_id: impl Into<String>) -> ResponseStreamRequestBuilder {
104        ResponseStreamRequestBuilder::new(self.client.clone()).response_id(response_id)
105    }
106
107    /// 创建 Responses WebSocket 连接构建器。
108    #[cfg(feature = "responses-ws")]
109    #[cfg_attr(docsrs, doc(cfg(feature = "responses-ws")))]
110    pub fn ws(&self) -> ResponsesSocketRequestBuilder {
111        ResponsesSocketRequestBuilder::new(self.client.clone())
112    }
113
114    /// 获取 response。
115    pub fn retrieve(&self, response_id: impl Into<String>) -> JsonRequestBuilder<Response> {
116        JsonRequestBuilder::new(
117            self.client.clone(),
118            "responses.retrieve",
119            Method::GET,
120            format!("/responses/{}", encode_path_segment(response_id.into())),
121        )
122    }
123
124    /// 删除 response。
125    pub fn delete(&self, response_id: impl Into<String>) -> JsonRequestBuilder<DeleteResponse> {
126        JsonRequestBuilder::new(
127            self.client.clone(),
128            "responses.delete",
129            Method::DELETE,
130            format!("/responses/{}", encode_path_segment(response_id.into())),
131        )
132    }
133
134    /// 取消后台 response。
135    pub fn cancel(&self, response_id: impl Into<String>) -> JsonRequestBuilder<Response> {
136        JsonRequestBuilder::new(
137            self.client.clone(),
138            "responses.cancel",
139            Method::POST,
140            format!(
141                "/responses/{}/cancel",
142                encode_path_segment(response_id.into())
143            ),
144        )
145    }
146
147    /// 压缩 response。
148    pub fn compact(&self) -> JsonRequestBuilder<Response> {
149        JsonRequestBuilder::new(
150            self.client.clone(),
151            "responses.compact",
152            Method::POST,
153            "/responses/compact",
154        )
155    }
156
157    /// 返回 input_items 子资源。
158    pub fn input_items(&self) -> ResponseInputItemsResource {
159        ResponseInputItemsResource::new(self.client.clone())
160    }
161
162    /// 返回 input_tokens 子资源。
163    pub fn input_tokens(&self) -> ResponseInputTokensResource {
164        ResponseInputTokensResource::new(self.client.clone())
165    }
166}
167
168impl ResponseInputItemsResource {
169    /// 列出 response 输入项。
170    pub fn list(&self, response_id: impl Into<String>) -> ListRequestBuilder<ConversationItem> {
171        let endpoint = endpoints::responses::RESPONSES_INPUT_ITEMS_LIST;
172        ListRequestBuilder::new(
173            self.client.clone(),
174            endpoint.id,
175            endpoint.render(&[("response_id", &encode_path_segment(response_id.into()))]),
176        )
177    }
178}
179
180impl ResponseInputTokensResource {
181    /// 统计输入 token。
182    pub fn count(&self) -> JsonRequestBuilder<InputTokenCount> {
183        let endpoint = endpoints::responses::RESPONSES_INPUT_TOKENS_COUNT;
184        JsonRequestBuilder::new(
185            self.client.clone(),
186            endpoint.id,
187            Method::POST,
188            endpoint.template,
189        )
190    }
191}
192
193impl RealtimeResource {
194    /// 创建 Realtime WebSocket 连接构建器。
195    #[cfg(feature = "realtime")]
196    #[cfg_attr(docsrs, doc(cfg(feature = "realtime")))]
197    pub fn ws(&self) -> RealtimeSocketRequestBuilder {
198        RealtimeSocketRequestBuilder::new(self.client.clone())
199    }
200
201    /// 返回 client_secrets 子资源。
202    pub fn client_secrets(&self) -> RealtimeClientSecretsResource {
203        RealtimeClientSecretsResource::new(self.client.clone())
204    }
205
206    /// 返回 calls 子资源。
207    pub fn calls(&self) -> RealtimeCallsResource {
208        RealtimeCallsResource::new(self.client.clone())
209    }
210}
211
212impl RealtimeClientSecretsResource {
213    /// 创建 client secret。
214    pub fn create(&self) -> JsonRequestBuilder<RealtimeClientSecretCreateResponse> {
215        let endpoint = endpoints::responses::REALTIME_CLIENT_SECRETS_CREATE;
216        JsonRequestBuilder::new(
217            self.client.clone(),
218            endpoint.id,
219            Method::POST,
220            endpoint.template,
221        )
222    }
223}
224
225impl RealtimeCallsResource {
226    /// 接听通话。
227    pub fn accept(&self, call_id: impl Into<String>) -> NoContentRequestBuilder {
228        realtime_call_action(
229            self.client.clone(),
230            endpoints::responses::REALTIME_CALLS_ACCEPT,
231            call_id,
232        )
233    }
234
235    /// 挂断通话。
236    pub fn hangup(&self, call_id: impl Into<String>) -> NoContentRequestBuilder {
237        realtime_call_action(
238            self.client.clone(),
239            endpoints::responses::REALTIME_CALLS_HANGUP,
240            call_id,
241        )
242    }
243
244    /// 转接通话。
245    pub fn refer(&self, call_id: impl Into<String>) -> NoContentRequestBuilder {
246        realtime_call_action(
247            self.client.clone(),
248            endpoints::responses::REALTIME_CALLS_REFER,
249            call_id,
250        )
251    }
252
253    /// 拒绝通话。
254    pub fn reject(&self, call_id: impl Into<String>) -> NoContentRequestBuilder {
255        realtime_call_action(
256            self.client.clone(),
257            endpoints::responses::REALTIME_CALLS_REJECT,
258            call_id,
259        )
260    }
261}
262
263fn realtime_call_action(
264    client: Client,
265    endpoint: endpoints::PathTemplateEndpoint,
266    call_id: impl Into<String>,
267) -> NoContentRequestBuilder {
268    NoContentRequestBuilder::new(
269        client,
270        endpoint.id,
271        Method::POST,
272        endpoint.render(&[("call_id", &encode_path_segment(call_id.into()))]),
273    )
274    .extra_header("accept", "*/*")
275}
276
277/// 表示 Responses 创建构建器。
278#[derive(Debug, Clone, Default)]
279pub struct ResponseCreateRequestBuilder {
280    client: Option<Client>,
281    pub(crate) params: ResponseCreateParams,
282    options: RequestOptions,
283    extra_body: BTreeMap<String, Value>,
284    provider_options: BTreeMap<String, Value>,
285}
286
287/// 表示 Responses 流式构建器。
288#[derive(Debug, Clone)]
289pub struct ResponseStreamRequestBuilder {
290    inner: ResponseCreateRequestBuilder,
291    response_id: Option<String>,
292    starting_after: Option<u64>,
293}
294
295/// 表示 Realtime WebSocket 连接构建器。
296#[cfg(feature = "realtime")]
297#[cfg_attr(docsrs, doc(cfg(feature = "realtime")))]
298#[derive(Debug, Clone)]
299pub struct RealtimeSocketRequestBuilder {
300    client: Client,
301    model: Option<String>,
302    options: RequestOptions,
303}
304
305/// 表示 Responses WebSocket 连接构建器。
306#[cfg(feature = "responses-ws")]
307#[cfg_attr(docsrs, doc(cfg(feature = "responses-ws")))]
308#[derive(Debug, Clone)]
309pub struct ResponsesSocketRequestBuilder {
310    client: Client,
311    options: RequestOptions,
312}
313
314impl ResponseStreamRequestBuilder {
315    pub(crate) fn new(client: Client) -> Self {
316        Self {
317            inner: ResponseCreateRequestBuilder::new(client),
318            response_id: None,
319            starting_after: None,
320        }
321    }
322
323    /// 设置模型。
324    pub fn model(mut self, model: impl Into<String>) -> Self {
325        self.inner = self.inner.model(model);
326        self
327    }
328
329    /// 设置输入文本。
330    pub fn input_text(mut self, input: impl Into<String>) -> Self {
331        self.inner = self.inner.input_text(input);
332        self
333    }
334
335    /// 设置输入项数组。
336    pub fn input_items(mut self, items: Vec<ResponseInputItemPayload>) -> Self {
337        self.inner = self.inner.input_items(items);
338        self
339    }
340
341    /// 直接设置输入载荷。
342    pub fn input(mut self, input: impl Into<ResponseInputPayload>) -> Self {
343        self.inner = self.inner.input(input);
344        self
345    }
346
347    /// 设置温度。
348    pub fn temperature(mut self, temperature: f32) -> Self {
349        self.inner = self.inner.temperature(temperature);
350        self
351    }
352
353    /// 追加工具定义。
354    pub fn tool(mut self, tool: ChatToolDefinition) -> Self {
355        self.inner = self.inner.tool(tool);
356        self
357    }
358
359    /// 添加请求体字段。
360    pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<JsonPayload>) -> Self {
361        self.inner = self.inner.extra_body(key, value);
362        self
363    }
364
365    /// 添加 provider 选项。
366    pub fn provider_option(
367        mut self,
368        key: impl Into<String>,
369        value: impl Into<JsonPayload>,
370    ) -> Self {
371        self.inner = self.inner.provider_option(key, value);
372        self
373    }
374
375    /// 添加额外请求头。
376    pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
377        self.inner.options.insert_header(key, value);
378        self
379    }
380
381    /// 添加额外查询参数。
382    pub fn extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
383        self.inner.options.insert_query(key, value);
384        self
385    }
386
387    /// 覆盖请求超时时间。
388    pub fn timeout(mut self, timeout: Duration) -> Self {
389        self.inner.options.timeout = Some(timeout);
390        self
391    }
392
393    /// 覆盖最大重试次数。
394    pub fn max_retries(mut self, max_retries: u32) -> Self {
395        self.inner.options.max_retries = Some(max_retries);
396        self
397    }
398
399    /// 设置取消令牌。
400    pub fn cancellation_token(mut self, token: CancellationToken) -> Self {
401        self.inner.options.cancellation_token = Some(token);
402        self
403    }
404
405    /// 按响应 ID 继续一个已有的 Responses SSE 流。
406    pub fn response_id(mut self, response_id: impl Into<String>) -> Self {
407        self.response_id = Some(response_id.into());
408        self
409    }
410
411    /// 当继续已有流时,只消费给定序号之后的事件。
412    pub fn starting_after(mut self, sequence_number: u64) -> Self {
413        self.starting_after = Some(sequence_number);
414        self
415    }
416
417    /// 发送流式 Responses 请求。
418    ///
419    /// # Errors
420    ///
421    /// 当参数校验失败、请求失败或流初始化失败时返回错误。
422    pub async fn send(mut self) -> Result<ResponseStream> {
423        let (client, spec) = if let Some(response_id) = self.response_id.take() {
424            if self.inner.params.model.is_some()
425                || self.inner.params.input.is_some()
426                || self.inner.params.temperature.is_some()
427                || !self.inner.params.tools.is_empty()
428                || !self.inner.extra_body.is_empty()
429                || !self.inner.provider_options.is_empty()
430            {
431                return Err(Error::InvalidConfig(
432                    "按 response_id 继续流时,不应再设置创建期参数或请求体扩展字段".into(),
433                ));
434            }
435
436            let client = self
437                .inner
438                .client
439                .take()
440                .ok_or_else(|| Error::InvalidConfig("Responses 构建器缺少客户端".into()))?;
441            let mut spec = RequestSpec::new(
442                "responses.stream.retrieve",
443                Method::GET,
444                format!("/responses/{}", encode_path_segment(response_id)),
445            );
446            spec.options = self.inner.options;
447            spec.options.insert_query("stream", "true");
448            if let Some(sequence_number) = self.starting_after {
449                spec.options
450                    .insert_query("starting_after", sequence_number.to_string());
451            }
452            (client, spec)
453        } else {
454            if self.starting_after.is_some() {
455                return Err(Error::InvalidConfig(
456                    "`starting_after` 只能与 `response_id` 一起使用".into(),
457                ));
458            }
459            self.inner.build_spec(true)?
460        };
461        Ok(ResponseStream::new(client.execute_sse(spec).await?))
462    }
463
464    /// 发送流式 Responses 请求,并返回带高层语义事件的运行时流。
465    ///
466    /// # Errors
467    ///
468    /// 当参数校验失败、请求失败或流初始化失败时返回错误。
469    pub async fn send_events(self) -> Result<ResponseEventStream> {
470        Ok(self.send().await?.events())
471    }
472}
473
474#[cfg(feature = "realtime")]
475impl RealtimeSocketRequestBuilder {
476    pub(crate) fn new(client: Client) -> Self {
477        Self {
478            client,
479            model: None,
480            options: RequestOptions::default(),
481        }
482    }
483
484    /// 设置 Realtime 连接所使用的模型或 deployment。
485    pub fn model(mut self, model: impl Into<String>) -> Self {
486        self.model = Some(model.into());
487        self
488    }
489
490    /// 添加额外请求头。
491    pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
492        self.options.insert_header(key, value);
493        self
494    }
495
496    /// 添加额外查询参数。
497    pub fn extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
498        self.options.insert_query(key, value);
499        self
500    }
501
502    /// 建立 Realtime WebSocket 连接。
503    ///
504    /// # Errors
505    ///
506    /// 当参数校验失败或握手失败时返回错误。
507    pub async fn connect(self) -> Result<RealtimeSocket> {
508        RealtimeSocket::connect(&self.client, self.model, self.options).await
509    }
510}
511
512#[cfg(feature = "responses-ws")]
513impl ResponsesSocketRequestBuilder {
514    pub(crate) fn new(client: Client) -> Self {
515        Self {
516            client,
517            options: RequestOptions::default(),
518        }
519    }
520
521    /// 添加额外请求头。
522    pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
523        self.options.insert_header(key, value);
524        self
525    }
526
527    /// 添加额外查询参数。
528    pub fn extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
529        self.options.insert_query(key, value);
530        self
531    }
532
533    /// 建立 Responses WebSocket 连接。
534    ///
535    /// # Errors
536    ///
537    /// 当握手失败时返回错误。
538    pub async fn connect(self) -> Result<ResponsesSocket> {
539        ResponsesSocket::connect(&self.client, self.options).await
540    }
541}
542
543impl ResponseCreateRequestBuilder {
544    pub(crate) fn new(client: Client) -> Self {
545        Self {
546            client: Some(client),
547            ..Self::default()
548        }
549    }
550
551    /// 设置模型。
552    pub fn model(mut self, model: impl Into<String>) -> Self {
553        self.params.model = Some(model.into());
554        self
555    }
556
557    /// 直接设置输入文本。
558    pub fn input_text(mut self, input: impl Into<String>) -> Self {
559        self.params.input = Some(ResponseInputPayload::from(input.into()));
560        self
561    }
562
563    /// 设置输入项数组。
564    pub fn input_items(mut self, items: Vec<ResponseInputItemPayload>) -> Self {
565        self.params.input = Some(ResponseInputPayload::from(items));
566        self
567    }
568
569    /// 直接设置输入载荷。
570    pub fn input(mut self, input: impl Into<ResponseInputPayload>) -> Self {
571        self.params.input = Some(input.into());
572        self
573    }
574
575    /// 设置温度。
576    pub fn temperature(mut self, temperature: f32) -> Self {
577        self.params.temperature = Some(temperature);
578        self
579    }
580
581    /// 追加工具定义。
582    pub fn tool(mut self, tool: ChatToolDefinition) -> Self {
583        self.params.tools.push(tool);
584        self
585    }
586
587    /// 追加请求体字段。
588    pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<JsonPayload>) -> Self {
589        self.extra_body.insert(key.into(), value.into().into_raw());
590        self
591    }
592
593    /// 追加 provider 选项。
594    pub fn provider_option(
595        mut self,
596        key: impl Into<String>,
597        value: impl Into<JsonPayload>,
598    ) -> Self {
599        self.provider_options
600            .insert(key.into(), value.into().into_raw());
601        self
602    }
603
604    /// 添加额外请求头。
605    pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
606        self.options.insert_header(key, value);
607        self
608    }
609
610    /// 添加额外查询参数。
611    pub fn extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
612        self.options.insert_query(key, value);
613        self
614    }
615
616    /// 覆盖请求超时时间。
617    pub fn timeout(mut self, timeout: Duration) -> Self {
618        self.options.timeout = Some(timeout);
619        self
620    }
621
622    /// 覆盖最大重试次数。
623    pub fn max_retries(mut self, max_retries: u32) -> Self {
624        self.options.max_retries = Some(max_retries);
625        self
626    }
627
628    /// 设置取消令牌。
629    pub fn cancellation_token(mut self, token: CancellationToken) -> Self {
630        self.options.cancellation_token = Some(token);
631        self
632    }
633
634    pub(crate) fn build_spec(mut self, stream: bool) -> Result<(Client, RequestSpec)> {
635        let client = self
636            .client
637            .take()
638            .ok_or_else(|| Error::InvalidConfig("Responses 构建器缺少客户端".into()))?;
639        if self.params.model.as_deref().unwrap_or_default().is_empty() {
640            return Err(Error::MissingRequiredField { field: "model" });
641        }
642        if self.params.input.is_none() {
643            return Err(Error::MissingRequiredField { field: "input" });
644        }
645
646        self.params.stream = Some(stream);
647        let provider_key = client.provider().kind().as_key();
648        let mut body = merge_json_body(
649            Some(value_from(&self.params)?),
650            &self.extra_body,
651            provider_key,
652            &self.provider_options,
653        );
654        if !self.params.tools.is_empty()
655            && let Some(object) = body.as_object_mut()
656        {
657            object.insert(
658                "tools".into(),
659                Value::Array(
660                    self.params
661                        .tools
662                        .iter()
663                        .map(ChatToolDefinition::as_response_tool_value)
664                        .collect(),
665                ),
666            );
667        }
668        let mut spec = RequestSpec::new(
669            if stream {
670                "responses.stream"
671            } else {
672                "responses.create"
673            },
674            Method::POST,
675            "/responses",
676        );
677        spec.body = Some(body);
678        spec.options = self.options;
679        Ok((client, spec))
680    }
681
682    /// 发送普通 Responses 请求。
683    ///
684    /// # Errors
685    ///
686    /// 当参数校验失败、请求失败或反序列化失败时返回错误。
687    pub async fn send(self) -> Result<Response> {
688        Ok(self.send_with_meta().await?.data)
689    }
690
691    /// 发送普通 Responses 请求并保留元信息。
692    ///
693    /// # Errors
694    ///
695    /// 当参数校验失败、请求失败或反序列化失败时返回错误。
696    pub async fn send_with_meta(self) -> Result<ApiResponse<Response>> {
697        let (client, spec) = self.build_spec(false)?;
698        client.execute_json(spec).await
699    }
700}
701
702/// 表示 Responses 结构化解析构建器。
703#[cfg(feature = "structured-output")]
704#[derive(Debug, Clone)]
705pub struct ResponseParseRequestBuilder<T> {
706    inner: ResponseCreateRequestBuilder,
707    _marker: PhantomData<T>,
708}
709
710#[cfg(feature = "structured-output")]
711impl<T> ResponseParseRequestBuilder<T> {
712    pub(crate) fn new(client: Client) -> Self {
713        Self {
714            inner: ResponseCreateRequestBuilder::new(client),
715            _marker: PhantomData,
716        }
717    }
718
719    /// 设置模型。
720    pub fn model(mut self, model: impl Into<String>) -> Self {
721        self.inner = self.inner.model(model);
722        self
723    }
724
725    /// 设置输入文本。
726    pub fn input_text(mut self, input: impl Into<String>) -> Self {
727        self.inner = self.inner.input_text(input);
728        self
729    }
730
731    /// 设置输入数组。
732    pub fn input_items(mut self, items: Vec<ResponseInputItemPayload>) -> Self {
733        self.inner = self.inner.input_items(items);
734        self
735    }
736}
737
738#[cfg(feature = "structured-output")]
739impl<T> ResponseParseRequestBuilder<T>
740where
741    T: JsonSchema + serde::de::DeserializeOwned,
742{
743    /// 发送请求并解析结构化输出。
744    ///
745    /// # Errors
746    ///
747    /// 当响应缺少可解析文本或 JSON 解析失败时返回错误。
748    pub async fn send(self) -> Result<ParsedResponse<T>> {
749        let response = self.inner.send().await?;
750        let output_text = response
751            .output_text()
752            .ok_or_else(|| Error::InvalidConfig("Responses 返回中缺少可解析文本".into()))?;
753        let parsed = parse_json_payload(&output_text)?;
754        Ok(ParsedResponse { response, parsed })
755    }
756}