1use std::collections::{BTreeMap, HashSet};
20use std::marker::PhantomData;
21
22use crate::completion::{CompletionRequest, CompletionResponse, FinishReason, Usage};
23use crate::error::{MalformedToolInput, ProviderError};
24use crate::message::{
25 AdditionalParams, AssistantContent, CallId, Image, Issuer, LocalCallId, ProviderCallId,
26 Reasoning, ReasoningContent, Text, ToolCall, ToolFunction, ToolName,
27};
28use crate::streaming::{Item, Part, PartKind, StreamEvent};
29use crate::telemetry::{GenAiOperation, SpanBuilder, SpanCombinator};
30use crate::wire::{Assembled, Call, Emit, Fold, Mode, Operation, Out, Reply, Shared};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct Completion;
35
36impl Operation for Completion {
37 type Request = CompletionRequest;
38 type Event = StreamEvent;
39 type End = Finish;
40 type Response = CompletionResponse;
41 type Fold = Turn;
42 type Emit = Assembled;
43
44 fn fold(request: &Self::Request, call: &mut Call<'_>) -> Self::Fold {
47 let telemetry = call.wire.telemetry.map_or_else(
48 || match call.mode {
49 Mode::Unary => GenAiOperation::Chat,
50 Mode::Streaming => GenAiOperation::ChatStreaming,
51 },
52 |telemetry| telemetry(call.mode),
53 );
54 debug_assert!(telemetry.is_completion());
55 let model = request
56 .model
57 .as_deref()
58 .or(call.wire.model)
59 .unwrap_or_default();
60 let span = SpanBuilder::new(call.wire.name, model, telemetry)
61 .system_instructions(
62 request.system_instructions(),
63 request.record_telemetry_content,
64 )
65 .build();
66 call.instrument(span.clone());
67 Turn {
68 span,
69 ..Turn::new(call.wire.name)
70 }
71 }
72
73 fn validate(request: &Self::Request) -> Result<(), ProviderError> {
75 request.validate_message_content()
76 }
77}
78
79impl crate::wire::reply::Closing<Completion> for Assembled {
80 fn close(shared: &mut Shared<Completion>) {
81 let Shared { fold, items, .. } = shared;
82 fold.close_open(items);
83 }
84}
85
86impl Emit<Completion> for Assembled {}
87
88#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
90pub struct Finish {
91 pub usage: Usage,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub reason: Option<FinishReason>,
97 pub message_id: Option<String>,
100 pub response_id: Option<String>,
102 pub model: Option<String>,
104}
105
106pub struct Turn {
113 span: tracing::Span,
114 provider: String,
117 drafts: Vec<Draft>,
119 next_part: u32,
120 issuer: Option<Issuer>,
121 message_id: Option<String>,
122 pending: BTreeMap<usize, Pending>,
123 provider_ids: HashSet<ProviderCallId>,
124 choice: Vec<Option<AssistantContent>>,
126 open_text: BTreeMap<usize, String>,
128}
129
130enum Draft {
132 Text {
133 part: Option<Part>,
134 text: String,
135 params: Option<AdditionalParams>,
136 },
137 Reasoning {
138 part: Option<Part>,
139 text: String,
140 },
141 Call {
142 id: CallId,
143 name: ToolName,
144 arguments: Arguments,
145 signature: Option<String>,
146 additional_params: Option<serde_json::Value>,
147 },
148 Closed,
149}
150
151#[derive(Default)]
153struct Arguments {
154 text: String,
155 overflowed: bool,
156 substantive: bool,
158 announced: Option<serde_json::Value>,
161}
162
163const MAX_TOOL_INPUT_BYTES: usize = 32 * 1024 * 1024;
165
166impl Arguments {
167 fn push(&mut self, fragment: &str, name: &str) {
168 self.substantive |= !fragment.trim().is_empty();
169 if self.text.trim() == "null" && !fragment.trim().is_empty() {
172 self.text.clear();
173 }
174 if self.text.len().saturating_add(fragment.len()) > MAX_TOOL_INPUT_BYTES {
175 if !self.overflowed {
176 self.overflowed = true;
177 tracing::warn!(
178 tool = name,
179 "streamed tool-call input exceeded the accumulation bound; truncating"
180 );
181 }
182 } else {
183 self.text.push_str(fragment);
184 }
185 }
186
187 fn parse(&self) -> Result<serde_json::Value, serde_json::Error> {
189 if self.text.is_empty()
190 && let Some(announced) = &self.announced
191 {
192 return Ok(announced.clone());
193 }
194 let arguments = crate::json_utils::parse_tool_arguments(&self.text)?;
195 if self.overflowed {
196 return Err(serde::de::Error::custom(
197 "tool-call input exceeded the accumulation bound",
198 ));
199 }
200 Ok(arguments)
201 }
202}
203
204#[derive(Default)]
207struct Pending {
208 id: Option<String>,
209 item_id: Option<String>,
210 name: String,
211 arguments: Arguments,
212 signature: Option<String>,
213 additional_params: Option<serde_json::Value>,
214 open: Option<usize>,
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220pub enum IfMalformed {
221 Fail,
224 EmptyObject,
227 Drop,
229 KeepOpen,
231}
232
233#[derive(Debug, Clone, Copy, Default)]
235pub struct CallFragment<'a> {
236 pub id: Option<&'a str>,
238 pub item_id: Option<&'a str>,
240 pub name: Option<&'a str>,
242 pub arguments: Option<&'a str>,
244}
245
246#[derive(Debug, Clone, Default, PartialEq)]
249pub struct Seal {
250 pub id: Option<String>,
252 pub signature: Option<String>,
254 pub restated: Option<Reasoning>,
257}
258
259type Brand<'id> = PhantomData<fn(&'id ()) -> &'id ()>;
260
261#[must_use = "an open part is closed when the reply ends"]
285#[derive(Debug)]
286pub struct TextPart<'id> {
287 slot: usize,
288 brand: Brand<'id>,
289}
290
291#[must_use = "an open part is closed when the reply ends"]
293#[derive(Debug)]
294pub struct ReasoningPart<'id> {
295 slot: usize,
296 brand: Brand<'id>,
297}
298
299#[must_use = "an open call that is not closed never becomes visible"]
313#[derive(Debug)]
314pub struct CallPart<'id> {
315 slot: usize,
316 brand: Brand<'id>,
317}
318
319impl Turn {
320 pub(crate) fn new(provider: impl Into<String>) -> Self {
322 Self {
323 span: tracing::Span::none(),
324 provider: provider.into(),
325 drafts: Vec::new(),
326 next_part: 0,
327 issuer: None,
328 message_id: None,
329 pending: BTreeMap::new(),
330 provider_ids: HashSet::new(),
331 choice: Vec::new(),
332 open_text: BTreeMap::new(),
333 }
334 }
335
336 fn issuer(&self) -> Issuer {
338 self.issuer
339 .clone()
340 .unwrap_or_else(|| Issuer::from(self.provider.clone()))
341 }
342
343 fn draft(&mut self, draft: Draft) -> usize {
344 self.drafts.push(draft);
345 self.drafts.len() - 1
346 }
347
348 fn next(&mut self) -> Part {
350 let part = Part::new(self.next_part);
351 self.next_part += 1;
352 part
353 }
354
355 fn start(&mut self, items: &mut Items, kind: PartKind) -> Part {
356 let part = self.next();
357 emit(items, StreamEvent::Start { part, kind });
358 part
359 }
360
361 fn push_text(&mut self, items: &mut Items, slot: usize, fragment: &str) {
362 if fragment.is_empty() {
363 return;
364 }
365 let started = match self.drafts.get(slot) {
366 Some(Draft::Text { part, .. }) => *part,
367 _ => return,
368 };
369 let part = match started {
370 Some(part) => part,
371 None => {
372 let part = self.start(items, PartKind::Text);
373 if let Some(Draft::Text {
374 part: slot_part, ..
375 }) = self.drafts.get_mut(slot)
376 {
377 *slot_part = Some(part);
378 }
379 part
380 }
381 };
382 if let Some(Draft::Text { text, .. }) = self.drafts.get_mut(slot) {
383 text.push_str(fragment);
384 }
385 emit(
386 items,
387 StreamEvent::Text {
388 part,
389 text: fragment.to_owned(),
390 },
391 );
392 }
393
394 fn close_text(&mut self, items: &mut Items, slot: usize) {
395 let Some(Draft::Text { part, text, params }) = self
396 .drafts
397 .get_mut(slot)
398 .map(|draft| std::mem::replace(draft, Draft::Closed))
399 else {
400 return;
401 };
402 if text.is_empty() && params.is_none() {
404 return;
405 }
406 let part = part.unwrap_or_else(|| self.start(items, PartKind::Text));
407 emit(
408 items,
409 StreamEvent::End {
410 part,
411 content: AssistantContent::Text(Text {
412 text,
413 additional_params: params,
414 }),
415 },
416 );
417 }
418
419 fn push_reasoning(&mut self, items: &mut Items, slot: usize, fragment: &str) {
420 if fragment.is_empty() {
421 return;
422 }
423 let started = match self.drafts.get(slot) {
424 Some(Draft::Reasoning { part, .. }) => *part,
425 _ => return,
426 };
427 let part = match started {
428 Some(part) => part,
429 None => {
430 let part = self.start(items, PartKind::Reasoning);
431 if let Some(Draft::Reasoning {
432 part: slot_part, ..
433 }) = self.drafts.get_mut(slot)
434 {
435 *slot_part = Some(part);
436 }
437 part
438 }
439 };
440 if let Some(Draft::Reasoning { text, .. }) = self.drafts.get_mut(slot) {
441 text.push_str(fragment);
442 }
443 emit(
444 items,
445 StreamEvent::Reasoning {
446 part,
447 text: fragment.to_owned(),
448 },
449 );
450 }
451
452 fn close_reasoning(&mut self, items: &mut Items, slot: usize, seal: Seal) {
453 let Some(Draft::Reasoning { part, text }) = self
454 .drafts
455 .get_mut(slot)
456 .map(|draft| std::mem::replace(draft, Draft::Closed))
457 else {
458 return;
459 };
460 let Seal {
461 id,
462 signature,
463 restated,
464 } = seal;
465 let reasoning = match restated {
466 Some(mut restated) => {
467 if restated.id.is_none() {
469 restated.id = id;
470 }
471 if let Some(signature) = signature {
472 attach_signature(&mut restated, signature);
473 }
474 restated
475 }
476 None if !text.is_empty() => Reasoning {
477 id,
478 content: vec![ReasoningContent::Text { text, signature }],
479 },
480 None => match signature {
483 Some(signature) => Reasoning {
484 id,
485 content: vec![ReasoningContent::Text {
486 text: String::new(),
487 signature: Some(signature),
488 }],
489 },
490 None => return,
491 },
492 };
493 let part = part.unwrap_or_else(|| self.start(items, PartKind::Reasoning));
494 let content = AssistantContent::Reasoning(reasoning.sealed(self.issuer()));
495 emit(items, StreamEvent::End { part, content });
496 }
497
498 fn open_call(&mut self, id: CallId, name: ToolName) -> Result<usize, ProviderError> {
499 if let Some(provider) = id.provider()
500 && !self.provider_ids.insert(provider.clone())
501 {
502 return Err(ProviderError::DuplicateCallId(id));
503 }
504 Ok(self.draft(Draft::Call {
505 id,
506 name,
507 arguments: Arguments::default(),
508 signature: None,
509 additional_params: None,
510 }))
511 }
512
513 fn push_arguments(&mut self, slot: usize, fragment: &str) {
514 if let Some(Draft::Call {
515 name, arguments, ..
516 }) = self.drafts.get_mut(slot)
517 {
518 arguments.push(fragment, name.as_str());
519 }
520 }
521
522 fn close_call(
525 &mut self,
526 items: &mut Items,
527 slot: usize,
528 if_malformed: IfMalformed,
529 ) -> Result<(), ProviderError> {
530 let parsed = match self.drafts.get(slot) {
531 Some(Draft::Call { arguments, .. }) => arguments.parse(),
532 _ => return Ok(()),
533 };
534 let parsed = match (parsed, if_malformed) {
535 (Ok(arguments), _) => arguments,
536 (Err(_), IfMalformed::KeepOpen) => return Ok(()),
537 (Err(_), IfMalformed::EmptyObject) => serde_json::Value::Object(Default::default()),
538 (Err(_), IfMalformed::Drop) => {
539 if let Some(draft) = self.drafts.get_mut(slot) {
540 *draft = Draft::Closed;
541 }
542 return Ok(());
543 }
544 (Err(error), IfMalformed::Fail) => {
545 let Some(Draft::Call {
546 id,
547 name,
548 arguments,
549 ..
550 }) = self
551 .drafts
552 .get_mut(slot)
553 .map(|draft| std::mem::replace(draft, Draft::Closed))
554 else {
555 return Ok(());
556 };
557 return Err(ProviderError::MalformedToolInput(MalformedToolInput {
558 name: name.into(),
559 id,
560 raw: arguments.text,
561 error: error.to_string(),
562 }));
563 }
564 };
565 let Some(Draft::Call {
566 id,
567 name,
568 arguments,
569 signature,
570 additional_params,
571 }) = self
572 .drafts
573 .get_mut(slot)
574 .map(|draft| std::mem::replace(draft, Draft::Closed))
575 else {
576 return Ok(());
577 };
578 let json = if arguments.text.is_empty() {
579 parsed.to_string()
580 } else {
581 arguments.text
582 };
583 let part = self.start(items, PartKind::ToolCall);
584 emit(items, StreamEvent::Arguments { part, json });
585 emit(
586 items,
587 StreamEvent::End {
588 part,
589 content: AssistantContent::ToolCall(ToolCall {
590 id,
591 function: ToolFunction {
592 name,
593 arguments: parsed,
594 },
595 signature,
596 additional_params,
597 }),
598 },
599 );
600 Ok(())
601 }
602
603 fn open_pending(&mut self, index: usize, issue: bool) -> Result<Option<usize>, ProviderError> {
607 let Some(pending) = self.pending.get(&index) else {
608 return Ok(None);
609 };
610 if let Some(slot) = pending.open {
611 return Ok(Some(slot));
612 }
613 let Ok(name) = ToolName::new(pending.name.clone()) else {
614 return Ok(None);
615 };
616 let id = match (&pending.id, &pending.item_id) {
617 (Some(call_id), item_id) => match ProviderCallId::new(call_id.clone()) {
618 Some(provider) => CallId::Provider(match item_id {
619 Some(item_id) => provider.with_item_id(item_id.clone()),
620 None => provider,
621 }),
622 None => CallId::Local(LocalCallId::new()),
623 },
624 (None, _) if issue => CallId::Local(LocalCallId::new()),
625 (None, _) => return Ok(None),
626 };
627 let slot = self.open_call(id, name)?;
628 let Some(pending) = self.pending.get_mut(&index) else {
629 return Ok(None);
630 };
631 pending.open = Some(slot);
632 let arguments = std::mem::take(&mut pending.arguments);
633 let signature = pending.signature.take();
634 let additional_params = pending.additional_params.take();
635 if let Some(Draft::Call {
636 arguments: open,
637 signature: open_signature,
638 additional_params: open_params,
639 ..
640 }) = self.drafts.get_mut(slot)
641 {
642 *open = arguments;
643 *open_signature = signature;
644 *open_params = additional_params;
645 }
646 Ok(Some(slot))
647 }
648
649 pub(crate) fn close_open(&mut self, items: &mut Items) {
653 self.pending.clear();
654 for slot in 0..self.drafts.len() {
655 match self.drafts.get(slot) {
656 Some(Draft::Text { .. }) => self.close_text(items, slot),
657 Some(Draft::Reasoning { .. }) => self.close_reasoning(items, slot, Seal::default()),
658 Some(Draft::Call { .. }) => {
659 let _ = self.close_call(items, slot, IfMalformed::Drop);
660 }
661 Some(Draft::Closed) | None => {}
662 }
663 }
664 }
665
666 pub fn snapshot(&self) -> Vec<AssistantContent> {
669 self.choice.iter().flatten().cloned().collect()
670 }
671
672 pub fn message_id(&self) -> Option<&str> {
674 self.message_id.as_deref()
675 }
676
677 pub fn reasoning_issuer(&self) -> Issuer {
679 self.issuer()
680 }
681
682 pub fn provider(&self) -> &str {
684 &self.provider
685 }
686
687 pub(crate) fn partial(&self, end: Option<&Finish>, reply: &Reply) -> CompletionResponse {
691 let mut response = self.response(end.cloned().unwrap_or_default(), reply.clone());
692 response.choice = self
693 .choice
694 .iter()
695 .enumerate()
696 .filter_map(|(index, part)| {
697 part.clone().or_else(|| {
698 self.open_text
699 .get(&index)
700 .map(|text| AssistantContent::text(text.clone()))
701 })
702 })
703 .map(|part| match part {
704 AssistantContent::Reasoning(reasoning) => {
705 AssistantContent::Reasoning(reasoning.reseal(self.issuer()))
706 }
707 part => part,
708 })
709 .collect();
710 response
711 }
712
713 fn response(&self, end: Finish, reply: Reply) -> CompletionResponse {
714 let issuer = self.issuer();
715 let choice = self
716 .snapshot()
717 .into_iter()
718 .map(|part| match part {
719 AssistantContent::Reasoning(reasoning) => {
720 AssistantContent::Reasoning(reasoning.reseal(issuer.clone()))
721 }
722 part => part,
723 })
724 .collect();
725 let Finish {
726 usage,
727 reason,
728 message_id,
729 response_id,
730 model,
731 } = end;
732 use crate::provider_response::reported;
733 let mut response = CompletionResponse::new(choice, usage, reply.provider, reply.raw)
734 .with_optional_finish_reason(reason);
735 response.message_id = reported(self.message_id.clone().or(message_id));
737 response.response_id = reported(response_id);
738 response.model = reported(model);
739 response.provider_request_id = reported(reply.provider_request_id);
740 response
741 }
742}
743
744pub(crate) type Items = std::collections::VecDeque<Result<Item<StreamEvent>, ProviderError>>;
745
746fn emit(items: &mut Items, event: StreamEvent) {
747 items.push_back(Ok(Item::Event(event)));
748}
749
750fn attach_signature(reasoning: &mut Reasoning, signature: String) {
753 match reasoning
754 .content
755 .iter_mut()
756 .rev()
757 .find_map(|content| match content {
758 ReasoningContent::Text {
759 signature: slot @ None,
760 ..
761 } => Some(slot),
762 _ => None,
763 }) {
764 Some(slot) => *slot = Some(signature),
765 None => reasoning.content.push(ReasoningContent::Text {
766 text: String::new(),
767 signature: Some(signature),
768 }),
769 }
770}
771
772impl Fold<Completion> for Turn {
773 fn absorb(&mut self, event: &StreamEvent) -> Result<(), ProviderError> {
774 match event {
775 StreamEvent::Start { part, .. } => {
776 if self.choice.len() <= part.index() {
777 self.choice.resize(part.index() + 1, None);
778 }
779 }
780 StreamEvent::End { part, content } => {
781 if self.choice.len() <= part.index() {
782 self.choice.resize(part.index() + 1, None);
783 }
784 if let Some(slot) = self.choice.get_mut(part.index()) {
785 *slot = Some(content.clone());
786 }
787 if let (None, AssistantContent::Reasoning(reasoning)) = (&self.issuer, content) {
789 self.issuer = Some(reasoning.issuer().clone());
790 }
791 self.open_text.remove(&part.index());
792 }
793 StreamEvent::Text { part, text } => {
794 self.open_text
795 .entry(part.index())
796 .or_default()
797 .push_str(text);
798 }
799 StreamEvent::Reasoning { .. } | StreamEvent::Arguments { .. } => {}
800 }
801 Ok(())
802 }
803
804 fn finish(self, end: Finish, reply: Reply) -> Result<CompletionResponse, ProviderError> {
805 let response = self.response(end, reply);
806 self.span.record_response(
807 response
808 .response_id
809 .as_deref()
810 .or(response.message_id.as_deref()),
811 response.model.as_deref(),
812 &response.usage,
813 );
814 Ok(response)
815 }
816}
817
818impl<'id> Out<'id, Completion> {
819 pub fn text(&mut self) -> TextPart<'id> {
821 let slot = self.lock().fold.draft(Draft::Text {
822 part: None,
823 text: String::new(),
824 params: None,
825 });
826 TextPart {
827 slot,
828 brand: PhantomData,
829 }
830 }
831
832 pub fn push_text(&mut self, part: &TextPart<'id>, text: &str) {
834 let mut shared = self.lock();
835 let Shared { fold, items, .. } = &mut *shared;
836 fold.push_text(items, part.slot, text);
837 }
838
839 pub fn text_params(&mut self, part: &TextPart<'id>, additional_params: AdditionalParams) {
842 let mut shared = self.lock();
843 let Shared { fold, items, .. } = &mut *shared;
844 let unstarted = matches!(
845 fold.drafts.get(part.slot),
846 Some(Draft::Text { part: None, .. })
847 );
848 if unstarted {
849 let started = fold.start(items, PartKind::Text);
850 if let Some(Draft::Text { part, .. }) = fold.drafts.get_mut(part.slot) {
851 *part = Some(started);
852 }
853 }
854 if let Some(Draft::Text { params, .. }) = fold.drafts.get_mut(part.slot) {
855 match params {
856 Some(params) => params.merge(additional_params),
857 None => *params = Some(additional_params),
858 }
859 }
860 }
861
862 pub fn close_text(&mut self, part: TextPart<'id>) {
864 let mut shared = self.lock();
865 let Shared { fold, items, .. } = &mut *shared;
866 fold.close_text(items, part.slot);
867 }
868
869 pub fn reasoning(&mut self) -> ReasoningPart<'id> {
871 let slot = self.lock().fold.draft(Draft::Reasoning {
872 part: None,
873 text: String::new(),
874 });
875 ReasoningPart {
876 slot,
877 brand: PhantomData,
878 }
879 }
880
881 pub fn push_reasoning(&mut self, part: &ReasoningPart<'id>, text: &str) {
883 let mut shared = self.lock();
884 let Shared { fold, items, .. } = &mut *shared;
885 fold.push_reasoning(items, part.slot, text);
886 }
887
888 pub fn close_reasoning(&mut self, part: ReasoningPart<'id>, seal: Seal) {
891 let mut shared = self.lock();
892 let Shared { fold, items, .. } = &mut *shared;
893 fold.close_reasoning(items, part.slot, seal);
894 }
895
896 pub fn reasoning_block(&mut self, reasoning: Reasoning) {
898 let part = self.reasoning();
899 self.close_reasoning(
900 part,
901 Seal {
902 restated: Some(reasoning),
903 ..Seal::default()
904 },
905 );
906 }
907
908 pub fn call(&mut self, id: CallId, name: ToolName) -> Result<CallPart<'id>, ProviderError> {
935 let slot = self.lock().fold.open_call(id, name)?;
936 Ok(CallPart {
937 slot,
938 brand: PhantomData,
939 })
940 }
941
942 pub fn push_arguments(&mut self, part: &CallPart<'id>, json: &str) {
944 self.lock().fold.push_arguments(part.slot, json);
945 }
946
947 pub fn decorate_call(
949 &mut self,
950 part: &CallPart<'id>,
951 signature: Option<String>,
952 additional_params: Option<serde_json::Value>,
953 ) {
954 if let Some(Draft::Call {
955 signature: open_signature,
956 additional_params: open_params,
957 ..
958 }) = self.lock().fold.drafts.get_mut(part.slot)
959 {
960 if signature.is_some() {
961 *open_signature = signature;
962 }
963 if additional_params.is_some() {
964 *open_params = additional_params;
965 }
966 }
967 }
968
969 pub fn close_call(&mut self, part: CallPart<'id>) -> Result<(), ProviderError> {
972 let mut shared = self.lock();
973 let Shared { fold, items, .. } = &mut *shared;
974 fold.close_call(items, part.slot, IfMalformed::Fail)
975 }
976
977 pub fn abandon_call(&mut self, part: CallPart<'id>) {
979 if let Some(draft) = self.lock().fold.drafts.get_mut(part.slot) {
980 *draft = Draft::Closed;
981 }
982 }
983
984 pub fn tool_call(&mut self, call: ToolCall) -> Result<(), ProviderError> {
986 let ToolCall {
987 id,
988 function,
989 signature,
990 additional_params,
991 } = call;
992 let part = self.call(id, function.name)?;
993 self.decorate_call(&part, signature, additional_params);
994 let mut shared = self.lock();
995 let Shared { fold, items, .. } = &mut *shared;
996 if let Some(Draft::Call { arguments, .. }) = fold.drafts.get_mut(part.slot) {
997 arguments.announced = Some(function.arguments);
998 }
999 fold.close_call(items, part.slot, IfMalformed::Fail)
1000 }
1001
1002 pub fn image(&mut self, image: Image) {
1004 let mut shared = self.lock();
1005 let Shared { fold, items, .. } = &mut *shared;
1006 let part = fold.start(items, PartKind::Image);
1007 emit(
1008 items,
1009 StreamEvent::End {
1010 part,
1011 content: AssistantContent::Image(image),
1012 },
1013 );
1014 }
1015
1016 pub fn content(&mut self, content: AssistantContent) -> Result<(), ProviderError> {
1018 match content {
1019 AssistantContent::Text(text) => {
1020 let part = self.text();
1021 self.push_text(&part, &text.text);
1022 if let Some(params) = text.additional_params {
1023 self.text_params(&part, params);
1024 }
1025 self.close_text(part);
1026 }
1027 AssistantContent::Reasoning(reasoning) => {
1028 let issuer = reasoning.issuer().clone();
1029 self.issued_by(issuer.clone());
1030 if let Some(reasoning) = reasoning.open(&issuer) {
1031 self.reasoning_block(reasoning.clone());
1032 }
1033 }
1034 AssistantContent::ToolCall(call) => self.tool_call(call)?,
1035 AssistantContent::Image(image) => self.image(image),
1036 }
1037 Ok(())
1038 }
1039
1040 pub fn message_id(&mut self, id: impl Into<String>) {
1042 let id = id.into();
1043 if !id.is_empty() {
1044 self.lock().fold.message_id = Some(id);
1045 }
1046 }
1047
1048 pub fn issued_by(&mut self, issuer: impl Into<Issuer>) {
1051 self.lock().fold.issuer = Some(issuer.into());
1052 }
1053
1054 pub fn call_fragment(
1059 &mut self,
1060 index: usize,
1061 fragment: CallFragment<'_>,
1062 ) -> Result<(), ProviderError> {
1063 let mut shared = self.lock();
1064 let turn = &mut shared.fold;
1065 let pending = turn.pending.entry(index).or_default();
1066 if let Some(id) = fragment.id.filter(|id| !id.is_empty()) {
1067 pending.id = Some(id.to_owned());
1068 }
1069 if let Some(item_id) = fragment.item_id.filter(|id| !id.is_empty()) {
1070 pending.item_id = Some(item_id.to_owned());
1071 }
1072 if let Some(name) = fragment.name.filter(|name| !name.is_empty()) {
1073 name.clone_into(&mut pending.name);
1074 }
1075 let open = pending.open;
1076 if let Some(arguments) = fragment.arguments.filter(|arguments| !arguments.is_empty()) {
1077 match open {
1078 Some(slot) => turn.push_arguments(slot, arguments),
1079 None => {
1080 let name = pending.name.clone();
1081 pending.arguments.push(arguments, &name);
1082 }
1083 }
1084 }
1085 turn.open_pending(index, false)?;
1086 Ok(())
1087 }
1088
1089 pub fn announce_pending(&mut self, index: usize, arguments: serde_json::Value) {
1092 let mut shared = self.lock();
1093 let turn = &mut shared.fold;
1094 let Some(pending) = turn.pending.get_mut(&index) else {
1095 return;
1096 };
1097 match pending.open {
1098 Some(slot) => {
1099 if let Some(Draft::Call {
1100 arguments: open, ..
1101 }) = turn.drafts.get_mut(slot)
1102 {
1103 open.announced = Some(arguments);
1104 }
1105 }
1106 None => pending.arguments.announced = Some(arguments),
1107 }
1108 }
1109
1110 pub fn decorate_pending(
1113 &mut self,
1114 provider_id: &str,
1115 signature: Option<String>,
1116 additional_params: Option<serde_json::Value>,
1117 ) {
1118 if provider_id.is_empty() {
1119 return;
1120 }
1121 let mut shared = self.lock();
1122 let turn = &mut shared.fold;
1123 let Some(pending) = turn
1124 .pending
1125 .values_mut()
1126 .find(|pending| pending.id.as_deref() == Some(provider_id))
1127 else {
1128 return;
1129 };
1130 match pending.open {
1131 Some(slot) => {
1132 if let Some(Draft::Call {
1133 signature: open_signature,
1134 additional_params: open_params,
1135 ..
1136 }) = turn.drafts.get_mut(slot)
1137 {
1138 if open_signature.is_none() {
1139 *open_signature = signature;
1140 }
1141 if open_params.is_none() {
1142 *open_params = additional_params;
1143 }
1144 }
1145 }
1146 None => {
1147 if pending.signature.is_none() {
1148 pending.signature = signature;
1149 }
1150 if pending.additional_params.is_none() {
1151 pending.additional_params = additional_params;
1152 }
1153 }
1154 }
1155 }
1156
1157 pub fn decorate_pending_at(
1159 &mut self,
1160 index: usize,
1161 signature: Option<String>,
1162 additional_params: Option<serde_json::Value>,
1163 ) {
1164 let mut shared = self.lock();
1165 let turn = &mut shared.fold;
1166 let Some(pending) = turn.pending.get_mut(&index) else {
1167 return;
1168 };
1169 match pending.open {
1170 Some(slot) => {
1171 if let Some(Draft::Call {
1172 signature: open_signature,
1173 additional_params: open_params,
1174 ..
1175 }) = turn.drafts.get_mut(slot)
1176 {
1177 if signature.is_some() {
1178 *open_signature = signature;
1179 }
1180 if additional_params.is_some() {
1181 *open_params = additional_params;
1182 }
1183 }
1184 }
1185 None => {
1186 if signature.is_some() {
1187 pending.signature = signature;
1188 }
1189 if additional_params.is_some() {
1190 pending.additional_params = additional_params;
1191 }
1192 }
1193 }
1194 }
1195
1196 pub fn pending_calls(&self) -> Vec<usize> {
1198 self.lock().fold.pending.keys().copied().collect()
1199 }
1200
1201 pub fn pending_id(&self, index: usize) -> Option<String> {
1203 self.lock()
1204 .fold
1205 .pending
1206 .get(&index)
1207 .and_then(|pending| pending.id.clone())
1208 }
1209
1210 pub fn pending_name(&self, index: usize) -> String {
1212 self.lock()
1213 .fold
1214 .pending
1215 .get(&index)
1216 .map(|pending| pending.name.clone())
1217 .unwrap_or_default()
1218 }
1219
1220 pub fn pending_has_arguments(&self, index: usize) -> bool {
1223 let shared = self.lock();
1224 let turn = &shared.fold;
1225 let Some(pending) = turn.pending.get(&index) else {
1226 return false;
1227 };
1228 let arguments = match pending.open {
1229 Some(slot) => match turn.drafts.get(slot) {
1230 Some(Draft::Call { arguments, .. }) => arguments,
1231 _ => return false,
1232 },
1233 None => &pending.arguments,
1234 };
1235 arguments.substantive || arguments.announced.is_some()
1236 }
1237
1238 pub fn close_pending(
1243 &mut self,
1244 index: usize,
1245 if_malformed: IfMalformed,
1246 ) -> Result<(), ProviderError> {
1247 let mut shared = self.lock();
1248 let Shared { fold, items, .. } = &mut *shared;
1249 let Some(slot) = fold.open_pending(index, true)? else {
1250 fold.pending.remove(&index);
1251 return Ok(());
1252 };
1253 let result = fold.close_call(items, slot, if_malformed);
1254 let kept_open = matches!(fold.drafts.get(slot), Some(Draft::Call { .. }));
1255 if !kept_open {
1256 fold.pending.remove(&index);
1257 }
1258 result
1259 }
1260
1261 pub fn drop_pending(&mut self, index: usize) {
1263 let mut shared = self.lock();
1264 let turn = &mut shared.fold;
1265 if let Some(pending) = turn.pending.remove(&index)
1266 && let Some(slot) = pending.open
1267 && let Some(draft) = turn.drafts.get_mut(slot)
1268 {
1269 *draft = Draft::Closed;
1270 }
1271 }
1272}
1273
1274impl Turn {
1275 pub(crate) fn relayed(provider: impl Into<String>) -> Self {
1278 Self::new(provider)
1279 }
1280
1281 pub(crate) fn open_text(&mut self) -> usize {
1285 self.draft(Draft::Text {
1286 part: None,
1287 text: String::new(),
1288 params: None,
1289 })
1290 }
1291
1292 pub(crate) fn write_text(&mut self, items: &mut Items, slot: usize, text: &str) {
1293 self.push_text(items, slot, text);
1294 }
1295
1296 pub(crate) fn end_text(&mut self, items: &mut Items, slot: usize) {
1297 self.close_text(items, slot);
1298 }
1299
1300 pub(crate) fn open_reasoning(&mut self) -> usize {
1301 self.draft(Draft::Reasoning {
1302 part: None,
1303 text: String::new(),
1304 })
1305 }
1306
1307 pub(crate) fn write_reasoning(&mut self, items: &mut Items, slot: usize, text: &str) {
1308 self.push_reasoning(items, slot, text);
1309 }
1310
1311 pub(crate) fn end_reasoning(&mut self, items: &mut Items, slot: usize) {
1312 self.close_reasoning(items, slot, Seal::default());
1313 }
1314
1315 pub(crate) fn write_call(
1316 &mut self,
1317 items: &mut Items,
1318 call: ToolCall,
1319 ) -> Result<(), ProviderError> {
1320 let ToolCall {
1321 id,
1322 function,
1323 signature,
1324 additional_params,
1325 } = call;
1326 let slot = self.open_call(id, function.name)?;
1327 if let Some(Draft::Call {
1328 arguments,
1329 signature: open_signature,
1330 additional_params: open_params,
1331 ..
1332 }) = self.drafts.get_mut(slot)
1333 {
1334 arguments.announced = Some(function.arguments);
1335 *open_signature = signature;
1336 *open_params = additional_params;
1337 }
1338 self.close_call(items, slot, IfMalformed::Fail)
1339 }
1340}
1341
1342pub(crate) fn events_of(
1345 response: &CompletionResponse,
1346) -> Result<Vec<Item<StreamEvent>>, ProviderError> {
1347 let shared = std::sync::Mutex::new(Shared::new(Turn::new(response.provider.clone())));
1348 {
1349 let mut out = Out::new(&shared);
1350 for content in &response.choice {
1351 out.content(content.clone())?;
1352 }
1353 }
1354 shared
1355 .into_inner()
1356 .unwrap_or_else(std::sync::PoisonError::into_inner)
1357 .items
1358 .into_iter()
1359 .collect()
1360}
1361
1362#[cfg(test)]
1363mod tests;