1use std::collections::BTreeMap;
4#[cfg(feature = "structured-output")]
5use std::marker::PhantomData;
6use std::time::Duration;
7
8use bytes::Bytes;
9use http::Method;
10#[cfg(feature = "structured-output")]
11use schemars::JsonSchema;
12#[cfg(feature = "tool-runner")]
13use serde::Deserialize;
14use serde::Serialize;
15use serde_json::Value;
16use tokio_util::sync::CancellationToken;
17
18use crate::Client;
19use crate::config::RequestOptions;
20use crate::error::{Error, Result};
21use crate::generated::endpoints;
22#[cfg(feature = "structured-output")]
23use crate::helpers::{ParsedChatCompletion, parse_json_payload};
24#[cfg(feature = "tool-runner")]
25use crate::helpers::{ToolDefinition, ToolRegistry};
26use crate::json_payload::JsonPayload;
27use crate::response_meta::ApiResponse;
28#[cfg(feature = "tool-runner")]
29use crate::stream::ChatCompletionRuntimeEvent;
30use crate::stream::{
31 AssistantEventStream, AssistantStream, ChatCompletionEventStream, ChatCompletionStream,
32};
33use crate::transport::{RequestSpec, merge_json_body};
34#[cfg(feature = "tool-runner")]
35use futures_util::StreamExt;
36
37#[cfg(feature = "tool-runner")]
38use super::ChatCompletionToolCall;
39#[cfg(feature = "tool-runner")]
40use super::CompletionUsage;
41use super::{
42 ChatCompletion, ChatCompletionCreateParams, ChatCompletionMessage,
43 ChatCompletionMessagesResource, ChatCompletionStoreContentPart, ChatCompletionsResource,
44 ChatResource, ChatToolChoice, ChatToolDefinition, DeleteResponse, JsonRequestBuilder,
45 ListRequestBuilder, encode_path_segment, value_from,
46};
47
48#[derive(Debug, Clone, Serialize, serde::Deserialize, Default)]
50pub struct ChatCompletionStoreMessage {
51 pub id: String,
53 #[serde(default)]
55 pub role: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub content: Option<String>,
59 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub content_parts: Vec<ChatCompletionStoreContentPart>,
62 #[serde(default, skip_serializing_if = "Vec::is_empty")]
64 pub tool_calls: Vec<super::ChatCompletionToolCall>,
65 #[serde(flatten)]
67 pub extra: BTreeMap<String, Value>,
68}
69
70impl ChatResource {
71 pub fn completions(&self) -> ChatCompletionsResource {
73 ChatCompletionsResource::new(self.client.clone())
74 }
75}
76
77impl ChatCompletionsResource {
78 pub fn create(&self) -> ChatCompletionCreateRequestBuilder {
80 ChatCompletionCreateRequestBuilder::new(self.client.clone())
81 }
82
83 pub fn stream(&self) -> ChatCompletionStreamRequestBuilder {
85 ChatCompletionStreamRequestBuilder::new(self.client.clone())
86 }
87
88 #[cfg(feature = "structured-output")]
90 #[cfg_attr(docsrs, doc(cfg(feature = "structured-output")))]
91 pub fn parse<T>(&self) -> ChatCompletionParseRequestBuilder<T> {
92 ChatCompletionParseRequestBuilder::new(self.client.clone())
93 }
94
95 #[cfg(feature = "tool-runner")]
97 #[cfg_attr(docsrs, doc(cfg(feature = "tool-runner")))]
98 pub fn run_tools(&self) -> ChatCompletionRunToolsRequestBuilder {
99 ChatCompletionRunToolsRequestBuilder::new(self.client.clone())
100 }
101
102 pub fn retrieve(&self, id: impl Into<String>) -> JsonRequestBuilder<ChatCompletion> {
104 JsonRequestBuilder::new(
105 self.client.clone(),
106 "chat.completions.retrieve",
107 Method::GET,
108 format!("/chat/completions/{}", encode_path_segment(id.into())),
109 )
110 }
111
112 pub fn update(&self, id: impl Into<String>) -> JsonRequestBuilder<ChatCompletion> {
114 JsonRequestBuilder::new(
115 self.client.clone(),
116 "chat.completions.update",
117 Method::POST,
118 format!("/chat/completions/{}", encode_path_segment(id.into())),
119 )
120 }
121
122 pub fn list(&self) -> ListRequestBuilder<ChatCompletion> {
124 ListRequestBuilder::new(
125 self.client.clone(),
126 "chat.completions.list",
127 "/chat/completions",
128 )
129 }
130
131 pub fn delete(&self, id: impl Into<String>) -> JsonRequestBuilder<DeleteResponse> {
133 JsonRequestBuilder::new(
134 self.client.clone(),
135 "chat.completions.delete",
136 Method::DELETE,
137 format!("/chat/completions/{}", encode_path_segment(id.into())),
138 )
139 }
140
141 pub fn messages(&self) -> ChatCompletionMessagesResource {
143 ChatCompletionMessagesResource::new(self.client.clone())
144 }
145}
146
147impl ChatCompletionMessagesResource {
148 pub fn list(
150 &self,
151 completion_id: impl Into<String>,
152 ) -> ListRequestBuilder<ChatCompletionStoreMessage> {
153 let endpoint = endpoints::chat::CHAT_COMPLETIONS_MESSAGES_LIST;
154 ListRequestBuilder::new(
155 self.client.clone(),
156 endpoint.id,
157 endpoint.render(&[("completion_id", &encode_path_segment(completion_id.into()))]),
158 )
159 }
160}
161
162#[derive(Debug, Clone, Default)]
164pub struct ChatCompletionCreateRequestBuilder {
165 client: Option<Client>,
166 pub(crate) params: ChatCompletionCreateParams,
167 options: RequestOptions,
168 extra_body: BTreeMap<String, Value>,
169 provider_options: BTreeMap<String, Value>,
170}
171
172impl ChatCompletionCreateRequestBuilder {
173 pub(crate) fn new(client: Client) -> Self {
174 Self {
175 client: Some(client),
176 ..Self::default()
177 }
178 }
179
180 pub fn model(mut self, model: impl Into<String>) -> Self {
182 self.params.model = Some(model.into());
183 self
184 }
185
186 pub fn messages(mut self, messages: Vec<ChatCompletionMessage>) -> Self {
188 self.params.messages = messages;
189 self
190 }
191
192 pub fn message_system(mut self, content: impl Into<String>) -> Self {
194 self.params
195 .messages
196 .push(ChatCompletionMessage::system(content));
197 self
198 }
199
200 pub fn message_user(mut self, content: impl Into<String>) -> Self {
202 self.params
203 .messages
204 .push(ChatCompletionMessage::user(content));
205 self
206 }
207
208 pub fn message_assistant(mut self, content: impl Into<String>) -> Self {
210 self.params
211 .messages
212 .push(ChatCompletionMessage::assistant(content));
213 self
214 }
215
216 pub fn temperature(mut self, temperature: f32) -> Self {
218 self.params.temperature = Some(temperature);
219 self
220 }
221
222 pub fn n(mut self, n: u32) -> Self {
224 self.params.n = Some(n);
225 self
226 }
227
228 pub fn max_tokens(mut self, max_tokens: u32) -> Self {
230 self.params.max_tokens = Some(max_tokens);
231 self
232 }
233
234 pub fn tool(mut self, tool: ChatToolDefinition) -> Self {
236 self.params.tools.push(tool);
237 self
238 }
239
240 pub fn tool_choice(mut self, tool_choice: impl Into<ChatToolChoice>) -> Self {
242 self.params.tool_choice = Some(tool_choice.into());
243 self
244 }
245
246 pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
248 self.options.insert_header(key, value);
249 self
250 }
251
252 pub fn extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
254 self.options.insert_query(key, value);
255 self
256 }
257
258 pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<JsonPayload>) -> Self {
260 self.extra_body.insert(key.into(), value.into().into_raw());
261 self
262 }
263
264 pub fn provider_option(
266 mut self,
267 key: impl Into<String>,
268 value: impl Into<JsonPayload>,
269 ) -> Self {
270 self.provider_options
271 .insert(key.into(), value.into().into_raw());
272 self
273 }
274
275 pub fn timeout(mut self, timeout: Duration) -> Self {
277 self.options.timeout = Some(timeout);
278 self
279 }
280
281 pub fn cancellation_token(mut self, token: CancellationToken) -> Self {
283 self.options.cancellation_token = Some(token);
284 self
285 }
286
287 pub(crate) fn build_spec(mut self, stream: bool) -> Result<(Client, RequestSpec)> {
288 let client = self
289 .client
290 .take()
291 .ok_or_else(|| Error::InvalidConfig("聊天补全构建器缺少客户端".into()))?;
292 if self.params.model.as_deref().unwrap_or_default().is_empty() {
293 return Err(Error::MissingRequiredField { field: "model" });
294 }
295 if self.params.messages.is_empty() {
296 return Err(Error::MissingRequiredField { field: "messages" });
297 }
298 self.params.stream = Some(stream);
299 let provider_key = client.provider().kind().as_key();
300 let body = merge_json_body(
301 Some(value_from(&self.params)?),
302 &self.extra_body,
303 provider_key,
304 &self.provider_options,
305 );
306 let mut spec = RequestSpec::new(
307 if stream {
308 "chat.completions.stream"
309 } else {
310 "chat.completions.create"
311 },
312 Method::POST,
313 "/chat/completions",
314 );
315 spec.body = Some(body);
316 spec.options = self.options;
317 Ok((client, spec))
318 }
319
320 pub async fn send(self) -> Result<ChatCompletion> {
326 Ok(self.send_with_meta().await?.data)
327 }
328
329 pub async fn send_with_meta(self) -> Result<ApiResponse<ChatCompletion>> {
335 let (client, spec) = self.build_spec(false)?;
336 client.execute_json(spec).await
337 }
338
339 pub async fn send_raw(self) -> Result<http::Response<Bytes>> {
345 let (client, spec) = self.build_spec(false)?;
346 client.execute_raw_http(spec).await
347 }
348}
349
350#[derive(Debug, Clone)]
352pub struct ChatCompletionStreamRequestBuilder {
353 inner: ChatCompletionCreateRequestBuilder,
354}
355
356#[derive(Debug, Clone)]
358pub struct AssistantStreamRequestBuilder {
359 inner: JsonRequestBuilder<Value>,
360}
361
362impl ChatCompletionStreamRequestBuilder {
363 pub(crate) fn new(client: Client) -> Self {
364 Self {
365 inner: ChatCompletionCreateRequestBuilder::new(client),
366 }
367 }
368
369 pub fn model(mut self, model: impl Into<String>) -> Self {
371 self.inner = self.inner.model(model);
372 self
373 }
374
375 pub fn messages(mut self, messages: Vec<ChatCompletionMessage>) -> Self {
377 self.inner = self.inner.messages(messages);
378 self
379 }
380
381 pub fn message_system(mut self, content: impl Into<String>) -> Self {
383 self.inner = self.inner.message_system(content);
384 self
385 }
386
387 pub fn message_user(mut self, content: impl Into<String>) -> Self {
389 self.inner = self.inner.message_user(content);
390 self
391 }
392
393 pub fn message_assistant(mut self, content: impl Into<String>) -> Self {
395 self.inner = self.inner.message_assistant(content);
396 self
397 }
398
399 pub fn temperature(mut self, temperature: f32) -> Self {
401 self.inner = self.inner.temperature(temperature);
402 self
403 }
404
405 pub fn n(mut self, n: u32) -> Self {
407 self.inner = self.inner.n(n);
408 self
409 }
410
411 pub fn max_tokens(mut self, max_tokens: u32) -> Self {
413 self.inner = self.inner.max_tokens(max_tokens);
414 self
415 }
416
417 pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<JsonPayload>) -> Self {
419 self.inner = self.inner.extra_body(key, value);
420 self
421 }
422
423 pub fn provider_option(
425 mut self,
426 key: impl Into<String>,
427 value: impl Into<JsonPayload>,
428 ) -> Self {
429 self.inner = self.inner.provider_option(key, value);
430 self
431 }
432
433 pub async fn send(self) -> Result<ChatCompletionStream> {
439 let (client, spec) = self.inner.build_spec(true)?;
440 Ok(ChatCompletionStream::new(client.execute_sse(spec).await?))
441 }
442
443 pub async fn send_events(self) -> Result<ChatCompletionEventStream> {
449 Ok(self.send().await?.events())
450 }
451}
452
453impl AssistantStreamRequestBuilder {
454 pub(crate) fn new(
455 client: Client,
456 endpoint_id: &'static str,
457 method: Method,
458 path: impl Into<String>,
459 ) -> Self {
460 Self {
461 inner: JsonRequestBuilder::new(client, endpoint_id, method, path),
462 }
463 }
464
465 pub fn body_value(mut self, body: impl Into<JsonPayload>) -> Self {
467 self.inner = self.inner.body_value(body);
468 self
469 }
470
471 pub fn json_body<U>(mut self, body: &U) -> Result<Self>
477 where
478 U: Serialize,
479 {
480 self.inner = self.inner.json_body(body)?;
481 Ok(self)
482 }
483
484 pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
486 self.inner = self.inner.extra_header(key, value);
487 self
488 }
489
490 pub fn remove_header(mut self, key: impl Into<String>) -> Self {
492 self.inner = self.inner.remove_header(key);
493 self
494 }
495
496 pub fn extra_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
498 self.inner = self.inner.extra_query(key, value);
499 self
500 }
501
502 pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<JsonPayload>) -> Self {
504 self.inner = self.inner.extra_body(key, value);
505 self
506 }
507
508 pub fn provider_option(
510 mut self,
511 key: impl Into<String>,
512 value: impl Into<JsonPayload>,
513 ) -> Self {
514 self.inner = self.inner.provider_option(key, value);
515 self
516 }
517
518 pub fn timeout(mut self, timeout: Duration) -> Self {
520 self.inner = self.inner.timeout(timeout);
521 self
522 }
523
524 pub fn max_retries(mut self, max_retries: u32) -> Self {
526 self.inner = self.inner.max_retries(max_retries);
527 self
528 }
529
530 pub fn cancellation_token(mut self, token: CancellationToken) -> Self {
532 self.inner = self.inner.cancellation_token(token);
533 self
534 }
535
536 pub async fn send(self) -> Result<AssistantStream> {
542 let client = self.inner.client.clone();
543 let stream = client.execute_raw_sse(self.inner.into_spec()).await?;
544 Ok(AssistantStream::new(stream))
545 }
546
547 pub async fn send_events(self) -> Result<AssistantEventStream> {
553 Ok(self.send().await?.events())
554 }
555}
556
557#[cfg(feature = "structured-output")]
559#[derive(Debug, Clone)]
560pub struct ChatCompletionParseRequestBuilder<T> {
561 inner: ChatCompletionCreateRequestBuilder,
562 _marker: PhantomData<T>,
563}
564
565#[cfg(feature = "structured-output")]
566impl<T> ChatCompletionParseRequestBuilder<T> {
567 pub(crate) fn new(client: Client) -> Self {
568 Self {
569 inner: ChatCompletionCreateRequestBuilder::new(client),
570 _marker: PhantomData,
571 }
572 }
573
574 pub fn model(mut self, model: impl Into<String>) -> Self {
576 self.inner = self.inner.model(model);
577 self
578 }
579
580 pub fn messages(mut self, messages: Vec<ChatCompletionMessage>) -> Self {
582 self.inner = self.inner.messages(messages);
583 self
584 }
585
586 pub fn message_user(mut self, content: impl Into<String>) -> Self {
588 self.inner = self.inner.message_user(content);
589 self
590 }
591
592 pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<JsonPayload>) -> Self {
594 self.inner = self.inner.extra_body(key, value);
595 self
596 }
597}
598
599#[cfg(feature = "structured-output")]
600impl<T> ChatCompletionParseRequestBuilder<T>
601where
602 T: JsonSchema + serde::de::DeserializeOwned,
603{
604 pub async fn send(self) -> Result<ParsedChatCompletion<T>> {
610 let response = self.inner.send().await?;
611 response.ensure_not_truncated()?;
612 let choice = response
613 .choices
614 .first()
615 .ok_or_else(|| Error::InvalidConfig("聊天补全返回中缺少 choice".into()))?;
616 let parsed = if let Some(content) = choice.message.content.as_deref() {
617 parse_json_payload(content)?
618 } else if let Some(parsed_arguments) = choice.message.parse_tool_arguments()? {
619 parsed_arguments
620 } else {
621 return Err(Error::InvalidConfig(
622 "聊天补全返回中既没有 assistant 文本,也没有可解析的工具参数".into(),
623 ));
624 };
625 Ok(ParsedChatCompletion { response, parsed })
626 }
627}
628
629#[cfg(feature = "tool-runner")]
631#[derive(Debug, Clone)]
632pub struct ChatCompletionRunToolsRequestBuilder {
633 inner: ChatCompletionCreateRequestBuilder,
634 registry: ToolRegistry,
635 max_rounds: usize,
636}
637
638#[cfg(feature = "tool-runner")]
639impl ChatCompletionRunToolsRequestBuilder {
640 pub(crate) fn new(client: Client) -> Self {
641 Self {
642 inner: ChatCompletionCreateRequestBuilder::new(client),
643 registry: ToolRegistry::new(),
644 max_rounds: 8,
645 }
646 }
647
648 pub fn model(mut self, model: impl Into<String>) -> Self {
650 self.inner = self.inner.model(model);
651 self
652 }
653
654 pub fn messages(mut self, messages: Vec<ChatCompletionMessage>) -> Self {
656 self.inner = self.inner.messages(messages);
657 self
658 }
659
660 pub fn message_user(mut self, content: impl Into<String>) -> Self {
662 self.inner = self.inner.message_user(content);
663 self
664 }
665
666 pub fn register_tool(mut self, tool: ToolDefinition) -> Self {
668 self.registry.register(tool);
669 self
670 }
671
672 pub fn max_rounds(mut self, max_rounds: usize) -> Self {
674 self.max_rounds = max_rounds;
675 self
676 }
677
678 pub async fn into_runner(self) -> Result<ChatCompletionRunner> {
684 let execution = self.execute(false).await?;
685 Ok(ChatCompletionRunner::from_execution(execution))
686 }
687
688 pub async fn into_streaming_runner(self) -> Result<ChatCompletionStreamingRunner> {
694 let execution = self.execute(true).await?;
695 Ok(ChatCompletionStreamingRunner::from_execution(execution))
696 }
697
698 pub async fn send(self) -> Result<ChatCompletion> {
704 self.into_runner()
705 .await?
706 .final_chat_completion()
707 .cloned()
708 .ok_or_else(|| Error::InvalidConfig("工具运行未返回最终聊天补全结果".into()))
709 }
710
711 pub async fn send_streaming(self) -> Result<ChatCompletion> {
717 self.into_streaming_runner()
718 .await?
719 .final_chat_completion()
720 .cloned()
721 .ok_or_else(|| Error::InvalidConfig("工具运行未返回最终聊天补全结果".into()))
722 }
723
724 async fn execute(self, stream: bool) -> Result<ChatCompletionRunExecution> {
725 let mut inner = self.inner;
726 if inner.params.tools.is_empty() {
727 inner.params.tools = self
728 .registry
729 .all()
730 .map(ChatToolDefinition::from_tool)
731 .collect();
732 }
733
734 let mut messages = inner.params.messages.clone();
735 let mut execution = ChatCompletionRunExecution {
736 messages: messages.clone(),
737 ..ChatCompletionRunExecution::default()
738 };
739 for _ in 0..self.max_rounds {
740 let request = ChatCompletionCreateRequestBuilder {
741 params: ChatCompletionCreateParams {
742 messages: messages.clone(),
743 ..inner.params.clone()
744 },
745 ..inner.clone()
746 };
747 let response = if stream {
748 let (client, spec) = request.build_spec(true)?;
749 let mut event_stream =
750 ChatCompletionStream::new(client.execute_sse(spec).await?).events();
751 while let Some(event) = event_stream.next().await {
752 execution.stream_events.push(event?);
753 }
754 let response = event_stream
755 .snapshot()
756 .ok_or_else(|| Error::InvalidConfig("流式聊天补全未返回最终结果".into()))?;
757 response.ensure_not_truncated()?;
758 response
759 } else {
760 request.send().await?
761 };
762 execution.chat_completions.push(response.clone());
763 let Some(choice) = response.choices.first() else {
764 return Ok(execution);
765 };
766 response.ensure_not_truncated()?;
767 execution.messages.push(choice.message.clone());
768 if choice.message.tool_calls.is_empty() {
769 return Ok(execution);
770 }
771
772 messages.push(choice.message.clone());
773
774 for tool_call in &choice.message.tool_calls {
775 let tool = self.registry.get(&tool_call.function.name).ok_or_else(|| {
776 Error::InvalidConfig(format!("未注册工具: {}", tool_call.function.name))
777 })?;
778 let arguments = if tool_call.function.arguments.trim().is_empty() {
779 Value::Object(Default::default())
780 } else {
781 serde_json::from_str(&tool_call.function.arguments)
782 .unwrap_or_else(|_| Value::String(tool_call.function.arguments.clone()))
783 };
784 let output = tool.invoke(arguments).await?;
785 let content = if output.is_string() {
786 output.as_str().unwrap_or_default().to_owned()
787 } else {
788 output.to_string()
789 };
790 let tool_message =
791 ChatCompletionMessage::tool(tool_call.id.clone(), content.clone());
792 messages.push(tool_message.clone());
793 execution.messages.push(tool_message);
794 execution.tool_results.push(ChatCompletionToolResult {
795 tool_call: tool_call.clone(),
796 output: content,
797 });
798 }
799 }
800
801 Err(Error::InvalidConfig("工具调用轮次已超过上限".into()))
802 }
803}
804
805#[cfg(feature = "tool-runner")]
807#[derive(Debug, Clone, Serialize, Deserialize)]
808pub struct ChatCompletionToolResult {
809 pub tool_call: ChatCompletionToolCall,
811 pub output: String,
813}
814
815#[cfg(feature = "tool-runner")]
816#[derive(Debug, Clone, Default)]
817struct ChatCompletionRunExecution {
818 messages: Vec<ChatCompletionMessage>,
819 chat_completions: Vec<ChatCompletion>,
820 tool_results: Vec<ChatCompletionToolResult>,
821 stream_events: Vec<ChatCompletionRuntimeEvent>,
822}
823
824#[cfg(feature = "tool-runner")]
826#[derive(Debug, Clone, Default)]
827pub struct ChatCompletionRunner {
828 messages: Vec<ChatCompletionMessage>,
829 chat_completions: Vec<ChatCompletion>,
830 tool_results: Vec<ChatCompletionToolResult>,
831}
832
833#[cfg(feature = "tool-runner")]
834impl ChatCompletionRunner {
835 fn from_execution(execution: ChatCompletionRunExecution) -> Self {
836 Self {
837 messages: execution.messages,
838 chat_completions: execution.chat_completions,
839 tool_results: execution.tool_results,
840 }
841 }
842
843 pub fn messages(&self) -> &[ChatCompletionMessage] {
845 &self.messages
846 }
847
848 pub fn all_chat_completions(&self) -> &[ChatCompletion] {
850 &self.chat_completions
851 }
852
853 pub fn tool_results(&self) -> &[ChatCompletionToolResult] {
855 &self.tool_results
856 }
857
858 pub fn final_chat_completion(&self) -> Option<&ChatCompletion> {
860 self.chat_completions.last()
861 }
862
863 pub fn final_message(&self) -> Option<&ChatCompletionMessage> {
865 self.messages
866 .iter()
867 .rev()
868 .find(|message| message.role == "assistant")
869 }
870
871 pub fn final_content(&self) -> Option<&str> {
873 self.final_message()
874 .and_then(|message| message.content.as_deref())
875 }
876
877 pub fn final_function_tool_call(&self) -> Option<&ChatCompletionToolCall> {
879 self.tool_results.last().map(|result| &result.tool_call)
880 }
881
882 pub fn final_function_tool_call_result(&self) -> Option<&str> {
884 self.tool_results
885 .last()
886 .map(|result| result.output.as_str())
887 }
888
889 pub fn total_usage(&self) -> Option<CompletionUsage> {
891 let mut completion_tokens = 0u64;
892 let mut prompt_tokens = 0u64;
893 let mut total_tokens = 0u64;
894 let mut found = false;
895 for completion in &self.chat_completions {
896 let Some(usage) = completion.usage.as_ref() else {
897 continue;
898 };
899 completion_tokens += usage.completion_tokens;
900 prompt_tokens += usage.prompt_tokens;
901 total_tokens += usage.total_tokens;
902 found = true;
903 }
904
905 found.then(|| CompletionUsage {
906 completion_tokens,
907 prompt_tokens,
908 total_tokens,
909 ..CompletionUsage::default()
910 })
911 }
912}
913
914#[cfg(feature = "tool-runner")]
916#[derive(Debug, Clone, Default)]
917pub struct ChatCompletionStreamingRunner {
918 runner: ChatCompletionRunner,
919 stream_events: Vec<ChatCompletionRuntimeEvent>,
920}
921
922#[cfg(feature = "tool-runner")]
923impl ChatCompletionStreamingRunner {
924 fn from_execution(execution: ChatCompletionRunExecution) -> Self {
925 Self {
926 runner: ChatCompletionRunner::from_execution(ChatCompletionRunExecution {
927 messages: execution.messages,
928 chat_completions: execution.chat_completions,
929 tool_results: execution.tool_results,
930 stream_events: Vec::new(),
931 }),
932 stream_events: execution.stream_events,
933 }
934 }
935
936 pub fn events(&self) -> &[ChatCompletionRuntimeEvent] {
938 &self.stream_events
939 }
940
941 pub fn messages(&self) -> &[ChatCompletionMessage] {
943 self.runner.messages()
944 }
945
946 pub fn all_chat_completions(&self) -> &[ChatCompletion] {
948 self.runner.all_chat_completions()
949 }
950
951 pub fn tool_results(&self) -> &[ChatCompletionToolResult] {
953 self.runner.tool_results()
954 }
955
956 pub fn final_chat_completion(&self) -> Option<&ChatCompletion> {
958 self.runner.final_chat_completion()
959 }
960
961 pub fn final_message(&self) -> Option<&ChatCompletionMessage> {
963 self.runner.final_message()
964 }
965
966 pub fn final_content(&self) -> Option<&str> {
968 self.runner.final_content()
969 }
970
971 pub fn final_function_tool_call(&self) -> Option<&ChatCompletionToolCall> {
973 self.runner.final_function_tool_call()
974 }
975
976 pub fn final_function_tool_call_result(&self) -> Option<&str> {
978 self.runner.final_function_tool_call_result()
979 }
980
981 pub fn total_usage(&self) -> Option<CompletionUsage> {
983 self.runner.total_usage()
984 }
985}