1use std::borrow::Cow;
16use std::collections::{HashMap, HashSet};
17
18use serde_json::{Value, json};
19
20use crate::completion::{Cost, FinishReason, Usage};
21use crate::error::ProviderError;
22use crate::json_utils::Lenient;
23use crate::json_utils::remove_preserving_order;
24use crate::message::{Source, SourceLocation};
25use crate::operation::{Block, CallFragment, Completion, Finish};
26use crate::providers::internal::wire;
27use crate::wire::{Decoder, Flow, Out, WireCitation, WireEvent, WireFrame};
28
29fn is_known_responses_event_type(kind: &str) -> bool {
33 matches!(kind, "error" | "keepalive")
34 || is_lifecycle_event(kind)
35 || matches!(
36 kind.strip_prefix("response."),
37 Some(
38 "output_item.added"
39 | "output_item.done"
40 | "content_part.added"
41 | "content_part.done"
42 | "output_text.delta"
43 | "output_text.done"
44 | "refusal.delta"
45 | "refusal.done"
46 | "function_call_arguments.delta"
47 | "function_call_arguments.done"
48 | "custom_tool_call_input.delta"
49 | "custom_tool_call_input.done"
50 | "reasoning_summary_part.added"
51 | "reasoning_summary_part.done"
52 | "reasoning_summary_text.delta"
53 | "reasoning_summary_text.done"
54 | "reasoning_text.delta"
55 | "reasoning_text.done"
56 )
57 )
58}
59
60pub(crate) fn is_lifecycle_event(kind: &str) -> bool {
63 matches!(
64 kind.strip_prefix("response."),
65 Some("created" | "queued" | "in_progress" | "completed" | "failed" | "incomplete")
66 )
67}
68
69#[derive(Debug)]
71pub enum ResponsesEvent {
72 Frame {
74 kind: String,
76 frame: Value,
78 raw: Option<String>,
81 },
82 Whole(Value),
85 Failure(String),
88 Sentinel,
90}
91
92const WHOLE_BODY_MARKERS: &[&str] = &["object", "output", "status", "id", "error"];
95
96struct Tagged(Value);
98
99impl<'de> serde::Deserialize<'de> for Tagged {
100 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101 where
102 D: serde::Deserializer<'de>,
103 {
104 let value = Value::deserialize(deserializer)?;
105 if value.str("type").is_some() {
106 Ok(Self(value))
107 } else {
108 Err(serde::de::Error::custom("the payload names no `type`"))
109 }
110 }
111}
112
113pub fn classify_responses_payload(data: &str) -> WireEvent<ResponsesEvent> {
116 if data.trim() == "[DONE]" {
117 return WireEvent::Known(ResponsesEvent::Sentinel);
118 }
119 wire::classify_or_untagged(
120 data,
121 "type",
122 is_known_responses_event_type,
123 |Tagged(frame)| match frame.str("type") {
124 Some("error") => ResponsesEvent::Failure(data.to_owned()),
125 kind => ResponsesEvent::Frame {
126 raw: (kind == Some("response.failed")).then(|| data.to_owned()),
127 kind: kind.unwrap_or_default().to_owned(),
128 frame,
129 },
130 },
131 |data| {
132 wire::classify_marker_keyed_frame::<Value>(data, WHOLE_BODY_MARKERS).map(|body| {
133 let error = body.get("error").is_some_and(|error| !error.is_null());
134 if error && body.get("output").is_none() && body.get("status").is_none() {
135 ResponsesEvent::Failure(data.to_owned())
136 } else {
137 ResponsesEvent::Whole(body)
138 }
139 })
140 },
141 )
142}
143
144#[derive(Clone, Copy, PartialEq, Eq, Debug)]
146enum Kind {
147 Message,
148 Reasoning,
149 Call,
150 Opaque,
151}
152
153impl Kind {
154 fn of(item: &Value) -> Self {
155 match item.str("type") {
156 Some("message") => Self::Message,
157 Some("reasoning") => Self::Reasoning,
158 Some("function_call" | "custom_tool_call") => Self::Call,
159 _ => Self::Opaque,
160 }
161 }
162}
163
164struct Slot {
166 at: usize,
168 kind: Kind,
169 id: Option<String>,
171 open: bool,
172 text: String,
176 field: Option<&'static str>,
177 part: u64,
178 arguments: String,
180 custom: bool,
181 named: bool,
183 titled: bool,
185}
186
187const CLIENT_EXECUTED: &[&str] = &[
190 "computer_call",
191 "local_shell_call",
192 "shell_call",
193 "apply_patch_call",
194 "mcp_approval_request",
195];
196
197fn item_id(item: &Value) -> Option<&str> {
198 item.str("id").filter(|id| !id.is_empty())
199}
200
201fn arguments_of(item: &Value) -> Option<String> {
204 if item.str("type") == Some("custom_tool_call") {
205 return item
206 .str("input")
207 .map(|input| json!({ "input": input }).to_string());
208 }
209 match item.get("arguments")? {
210 Value::String(arguments) if arguments.is_empty() => None,
211 Value::String(arguments) => Some(arguments.clone()),
212 Value::Null => None,
213 arguments => Some(arguments.to_string()),
214 }
215}
216
217fn text_of(item: &Value) -> String {
220 let texts = |key: &str| -> Vec<&str> {
221 match item.get(key) {
222 Some(Value::String(text)) => vec![text.as_str()],
223 _ => item
224 .arr(key)
225 .iter()
226 .filter_map(|part| {
227 part.as_str()
228 .or_else(|| part.str("text"))
229 .or_else(|| part.str("refusal"))
230 })
231 .collect(),
232 }
233 };
234 match Kind::of(item) {
235 Kind::Message => texts("content").concat(),
236 Kind::Reasoning => Some(texts("summary"))
237 .filter(|summary| !summary.is_empty())
238 .unwrap_or_else(|| texts("content"))
239 .join("\n\n"),
240 Kind::Call | Kind::Opaque => String::new(),
241 }
242}
243
244fn stating(mut item: Value, kind: Kind, text: &str) -> Value {
247 let (key, part) = match kind {
248 Kind::Message => (
249 "content",
250 json!({"type": "output_text", "text": text, "annotations": []}),
251 ),
252 _ => ("summary", json!({"type": "summary_text", "text": text})),
253 };
254 if let Some(fields) = item.as_object_mut() {
255 fields.insert(key.to_owned(), json!([part]));
256 }
257 item
258}
259
260fn provider_message(response: &Value, fallback: &str) -> String {
262 let parts: Vec<&str> = ["/error/code", "/error/message"]
263 .iter()
264 .filter_map(|pointer| response.at(pointer).and_then(Value::as_str))
265 .collect();
266 if parts.is_empty() {
267 fallback.to_owned()
268 } else {
269 parts.join(": ")
270 }
271}
272
273pub(crate) fn finish_reason_of(response: &Value) -> (FinishReason, Option<String>) {
278 let reason = response
279 .at("/incomplete_details/reason")
280 .and_then(Value::as_str)
281 .filter(|reason| !reason.is_empty());
282 let other = |reason: &str, error: String| (FinishReason::Other(reason.to_owned()), Some(error));
283 match response.str("status") {
284 None | Some("completed") => (FinishReason::Stop, None),
285 Some("incomplete") => match reason {
286 Some("max_output_tokens") => (FinishReason::Length, None),
287 Some("content_filter") => (FinishReason::ContentFilter, None),
288 Some(reason) => other(
289 &format!("incomplete: {reason}"),
290 format!("Response incomplete: {reason}"),
291 ),
292 None => other(
293 "incomplete",
294 "Response incomplete without a provider reason".to_owned(),
295 ),
296 },
297 Some(status @ ("failed" | "cancelled")) => other(
298 status,
299 provider_message(response, &format!("Response {status}")),
300 ),
301 Some(status @ ("queued" | "in_progress")) => {
302 other(status, format!("Response ended while {status}"))
303 }
304 Some(status) => other(
305 status,
306 format!("Response ended with the unknown status `{status}`"),
307 ),
308 }
309}
310
311pub(crate) fn usage_of(usage: &Value) -> Usage {
314 let count = |pointer: &str| usage.at(pointer).and_then(Lenient::as_u64_lenient);
315 Usage {
316 input_tokens: count("/input_tokens"),
317 output_tokens: count("/output_tokens"),
318 total_tokens: count("/total_tokens"),
319 cached_input_tokens: count("/input_tokens_details/cached_tokens"),
320 cache_creation_input_tokens: count("/input_tokens_details/cache_write_tokens"),
321 reasoning_tokens: count("/output_tokens_details/reasoning_tokens"),
322 ..Usage::default()
323 }
324 .cost(reported_cost(usage))
325}
326
327const TICKS_PER_USD: f64 = 1e10;
329
330fn reported_cost(usage: &Value) -> Option<Cost> {
333 let total = match usage.u64("cost_in_usd_ticks") {
334 Some(ticks) => ticks as f64 / TICKS_PER_USD,
335 None => usage.f64("cost").filter(|cost| cost.is_finite())?,
336 };
337 Some(Cost::from_total(total))
338}
339
340fn citations_of(item: &Value) -> Vec<WireCitation> {
345 item.arr("content")
346 .iter()
347 .flat_map(|part| part.arr("annotations"))
348 .filter_map(source_of)
349 .map(|source| WireCitation::new(None, vec![source]))
350 .collect()
351}
352
353fn source_of(annotation: &Value) -> Option<Source> {
356 let text = |key: &str| {
357 annotation
358 .str(key)
359 .filter(|text| !text.is_empty())
360 .map(str::to_owned)
361 };
362 let location = match annotation.str("type")? {
363 "url_citation" => SourceLocation::Url { url: text("url")? },
364 "file_citation" | "file_path" | "container_file_citation" => SourceLocation::File {
365 file_id: text("file_id")?,
366 filename: text("filename"),
367 container_id: text("container_id"),
368 },
369 _ => return None,
370 };
371 let source = Source::new(location);
372 Some(match text("title") {
373 Some(title) => source.title(title),
374 None => source,
375 })
376}
377
378#[derive(Default)]
392pub struct ResponsesDecoder {
393 slots: Vec<Slot>,
395 indexed: HashMap<usize, usize>,
397 current: Option<usize>,
399 held: Vec<usize>,
402}
403
404impl ResponsesDecoder {
405 pub fn new() -> Self {
407 Self::default()
408 }
409
410 fn addressed(&self, frame: &Value, kind: Kind) -> Result<Option<usize>, ProviderError> {
416 let index = output_index(frame)?;
417 if let Some(slot) = index.and_then(|index| self.indexed.get(&index)) {
418 return Ok(Some(*slot));
419 }
420 let id = frame
421 .at("/item/id")
422 .and_then(Value::as_str)
423 .or_else(|| frame.str("item_id"));
424 if let Some(slot) = id.filter(|id| !id.is_empty()).and_then(|id| {
425 self.slots
426 .iter()
427 .rposition(|slot| slot.id.as_deref() == Some(id))
428 }) {
429 return Ok(Some(slot));
430 }
431 Ok(self.current.filter(|current| {
432 (index.is_none() || !self.indexed.values().any(|slot| slot == current))
433 && self
434 .slots
435 .get(*current)
436 .is_some_and(|slot| slot.open && slot.kind == kind)
437 }))
438 }
439
440 fn added(
443 &mut self,
444 index: Option<usize>,
445 item: &Value,
446 out: &mut Out<'_, Completion>,
447 ) -> Result<usize, ProviderError> {
448 if let Some(previous) = index.and_then(|index| self.indexed.get(&index).copied()) {
449 self.vacate(previous, out)?;
450 }
451 let at = match index {
452 Some(index) if !self.slots.iter().any(|slot| slot.at == index) => index,
453 _ => out.fresh_index(),
454 };
455 let kind = Kind::of(item);
456 let custom = item.str("type") == Some("custom_tool_call");
457 match kind {
458 Kind::Message => out.open(at, Block::Text, Value::Null)?,
459 Kind::Reasoning => out.open(at, Block::Reasoning { redacted: false }, Value::Null)?,
460 Kind::Call => out.fragment(
461 Some(at),
462 CallFragment {
463 id: item.str("call_id"),
464 name: item.str("name"),
465 arguments: None,
466 },
467 )?,
468 Kind::Opaque => {
470 let replay = item
471 .str("type")
472 .is_some_and(|kind| !CLIENT_EXECUTED.contains(&kind));
473 out.open(at, Block::Opaque { replay }, item.clone())?;
474 }
475 }
476 self.slots.push(Slot {
477 at,
478 kind,
479 id: item_id(item).map(str::to_owned),
480 open: true,
481 text: String::new(),
482 field: None,
483 part: 0,
484 arguments: String::new(),
485 custom,
486 named: item.str("call_id").is_some_and(|id| !id.is_empty()),
487 titled: item.str("name").is_some_and(|name| !name.is_empty()),
488 });
489 let slot = self.slots.len() - 1;
490 if let Some(index) = index {
491 self.indexed.insert(index, slot);
492 }
493 self.current = Some(slot);
494 if let Some(call) = self
495 .slots
496 .get_mut(slot)
497 .filter(|slot| slot.kind == Kind::Call)
498 {
499 let arguments = item.str(if custom { "input" } else { "arguments" });
500 send(call, arguments.unwrap_or_default(), out)?;
501 }
502 Ok(slot)
503 }
504
505 fn vacate(&mut self, slot: usize, out: &mut Out<'_, Completion>) -> Result<(), ProviderError> {
508 let Some(open) = self.slots.get_mut(slot).filter(|slot| slot.open) else {
509 return Ok(());
510 };
511 open.open = false;
512 let at = open.at;
513 if open.kind == Kind::Call {
514 flush(open, out)?;
515 }
516 out.close(at)?;
517 self.release(false, out)
518 }
519
520 fn release(
523 &mut self,
524 complete: bool,
525 out: &mut Out<'_, Completion>,
526 ) -> Result<(), ProviderError> {
527 std::mem::take(&mut self.held)
528 .into_iter()
529 .try_for_each(|at| {
530 if complete {
531 out.finish(at)
532 } else {
533 out.close(at)
534 }
535 })
536 }
537
538 fn text(
541 &mut self,
542 frame: &Value,
543 field: &'static str,
544 part: &str,
545 out: &mut Out<'_, Completion>,
546 ) -> Result<(), ProviderError> {
547 let delta = frame.str("delta").unwrap_or_default();
548 if delta.is_empty() {
549 return Ok(());
550 }
551 let part = frame.u64(part).unwrap_or(0);
552 let kind = match field {
553 "message" => Kind::Message,
554 _ => Kind::Reasoning,
555 };
556 let known = self.addressed(frame, kind)?.and_then(|at| {
557 let slot = self.slots.get(at)?;
558 Some((at, slot.open, slot.kind == kind))
559 });
560 let slot = match known {
561 Some((slot, true, true)) => slot,
562 Some((_, false, true)) => return Ok(()),
565 _ => self.added(
566 output_index(frame)?,
567 &json!({"type": if kind == Kind::Message { "message" } else { "reasoning" }}),
568 out,
569 )?,
570 };
571 let Some(streamed) = self.slots.get_mut(slot).filter(|slot| slot.open) else {
572 return Ok(());
573 };
574 if streamed.field.is_some_and(|known| known != field) {
575 return Ok(());
576 }
577 if streamed.part != part && kind != Kind::Message && !streamed.text.is_empty() {
578 streamed.text.push_str("\n\n");
579 out.push(streamed.at, "\n\n")?;
580 }
581 streamed.field = Some(field);
582 streamed.part = part;
583 streamed.text.push_str(delta);
584 out.push(streamed.at, delta)
585 }
586
587 fn arguments(
590 &mut self,
591 frame: &Value,
592 out: &mut Out<'_, Completion>,
593 ) -> Result<(), ProviderError> {
594 let slot = self.addressed(frame, Kind::Call)?;
595 if let Some(call) = slot
596 .and_then(|slot| self.slots.get_mut(slot))
597 .filter(|slot| slot.open && slot.kind == Kind::Call)
598 {
599 send(call, frame.str("delta").unwrap_or_default(), out)?;
600 }
601 Ok(())
602 }
603
604 fn done(
607 &mut self,
608 slot: usize,
609 item: Value,
610 out: &mut Out<'_, Completion>,
611 ) -> Result<(), ProviderError> {
612 let Some(done) = self.slots.get_mut(slot) else {
613 return Ok(());
614 };
615 done.open = false;
616 if done.id.is_none() {
617 done.id = item_id(&item).map(str::to_owned);
618 }
619 let at = done.at;
620 let mut item = item;
621 let mut nameless = false;
622 match done.kind {
623 Kind::Call => {
624 nameless = !done.titled && item.str("name").is_none_or(str::is_empty);
625 let arguments = arguments_of(&item).unwrap_or_else(|| streamed_arguments(done));
626 let rest = remainder(done, &arguments, out)?;
627 out.fragment(
628 Some(at),
629 CallFragment {
630 id: item.str("call_id").filter(|_| !done.named),
631 name: item.str("name"),
632 arguments: Some(&rest),
633 },
634 )?;
635 }
636 Kind::Message | Kind::Reasoning => {
637 let text = text_of(&item);
638 if text.is_empty() {
639 if !done.text.is_empty() && done.field != Some("reasoning") {
643 item = stating(item, done.kind, &done.text);
644 }
645 } else if let Some(rest) = text.strip_prefix(done.text.as_str()) {
646 out.push(at, rest)?;
647 } else {
648 out.restate(at, &text)?;
650 }
651 let citations = citations_of(&item);
652 if done.kind == Kind::Message && !citations.is_empty() {
653 out.set_citations(at, citations);
656 }
657 }
658 Kind::Opaque => {}
659 }
660 let reasoning = done.kind == Kind::Reasoning;
661 out.edit(at, |native| *native = item)?;
662 if reasoning {
663 self.held.push(at);
666 return Ok(());
667 }
668 out.finish(at)?;
669 self.release(!nameless, out)
672 }
673
674 fn item_event(
676 &mut self,
677 kind: &str,
678 mut frame: Value,
679 out: &mut Out<'_, Completion>,
680 ) -> Result<(), ProviderError> {
681 match kind {
682 "response.output_item.added" | "response.output_item.done" => {
683 let Some(item) = frame.get("item").filter(|item| item.is_object()) else {
684 return Ok(());
685 };
686 let index = output_index(&frame)?;
687 if kind == "response.output_item.added" {
688 return self.added(index, item, out).map(|_| ());
689 }
690 let known = self.addressed(&frame, Kind::of(item))?;
691 let restates = |slot: &Slot| {
694 slot.kind == Kind::of(item)
695 && (index.is_some()
696 || item_id(item)
697 .zip(slot.id.as_deref())
698 .is_none_or(|(id, known)| id == known))
699 };
700 let known = known.and_then(|at| {
701 let slot = self.slots.get(at)?;
702 let same = item_id(item).is_some() && slot.id.as_deref() == item_id(item);
703 Some((at, slot.open && restates(slot), same))
704 });
705 let slot = match known {
706 Some((slot, true, _)) => slot,
707 Some((_, false, true)) => return Ok(()),
709 _ => self.added(index, item, out)?,
710 };
711 let item = frame
712 .as_object_mut()
713 .and_then(|frame| remove_preserving_order(frame, "item"))
714 .unwrap_or_default();
715 self.done(slot, item, out)
716 }
717 "response.output_text.delta" | "response.refusal.delta" => {
719 self.text(&frame, "message", "content_index", out)
720 }
721 "response.reasoning_summary_text.delta" => {
722 self.text(&frame, "summary", "summary_index", out)
723 }
724 "response.reasoning_text.delta" => self.text(&frame, "reasoning", "content_index", out),
725 "response.function_call_arguments.delta" | "response.custom_tool_call_input.delta" => {
726 self.arguments(&frame, out)
727 }
728 _ => Ok(()),
729 }
730 }
731
732 fn restated(
740 &self,
741 index: usize,
742 item: &Value,
743 kinds: &[Kind],
744 taken: &HashSet<usize>,
745 ) -> Option<usize> {
746 let id = item_id(item);
747 let free = |at: &usize| {
748 !taken.contains(at)
749 && self
750 .slots
751 .get(*at)
752 .is_some_and(|slot| slot.kind == Kind::of(item))
753 };
754 let find = |fits: &dyn Fn(usize, &Slot) -> bool| {
755 self.slots
756 .iter()
757 .enumerate()
758 .find(|(at, slot)| free(at) && fits(*at, slot))
759 .map(|(at, _)| at)
760 };
761 id.and_then(|id| find(&|_, slot| slot.id.as_deref() == Some(id)))
762 .or_else(|| self.indexed.get(&index).copied().filter(free))
763 .or_else(|| {
764 find(&|at, slot| {
765 !self.indexed.values().any(|indexed| *indexed == at)
766 && (id.is_none() || slot.id.is_none())
767 })
768 })
769 .or_else(|| {
770 find(&|at, slot| {
771 (id.is_none() || slot.id.is_none())
772 && self.indexed.iter().any(|(streamed, indexed)| {
773 *indexed == at
774 && kinds.get(*streamed).is_none_or(|other| *other != slot.kind)
775 })
776 })
777 })
778 }
779
780 fn finish(
786 &mut self,
787 mut response: Value,
788 mut out: Out<'_, Completion>,
789 ) -> Result<Flow, ProviderError> {
790 let output = match response.get_mut("output") {
791 Some(Value::Array(output)) => std::mem::take(output),
792 _ => Vec::new(),
793 };
794 if self.slots.is_empty()
795 && !output.iter().any(|item| Kind::of(item) == Kind::Reasoning)
796 && let Some(reasoning) = response.str("reasoning").filter(|text| !text.is_empty())
797 {
798 out.whole(
801 0,
802 Block::Reasoning { redacted: false },
803 Value::Null,
804 reasoning,
805 )?;
806 }
807 let mut taken = HashSet::new();
808 let kinds: Vec<Kind> = output.iter().map(Kind::of).collect();
809 for (index, item) in output
810 .into_iter()
811 .enumerate()
812 .filter(|(_, item)| item.is_object())
813 {
814 let known = self.restated(index, &item, &kinds, &taken).and_then(|at| {
815 let slot = self.slots.get(at)?;
816 Some((at, slot.open, slot.kind, slot.at))
817 });
818 let slot = match known {
819 Some((slot, false, kind, at)) => {
820 taken.insert(slot);
821 let ciphertext = item
822 .get("encrypted_content")
823 .filter(|cipher| cipher.as_str().is_some_and(|cipher| !cipher.is_empty()));
824 if let (Kind::Reasoning, Some(ciphertext)) = (kind, ciphertext) {
825 out.edit(at, |native| {
826 let stated = native
827 .str("encrypted_content")
828 .is_some_and(|cipher| !cipher.is_empty());
829 if let (false, Some(fields)) = (stated, native.as_object_mut()) {
830 fields.insert("encrypted_content".to_owned(), ciphertext.clone());
831 }
832 })?;
833 }
834 continue;
835 }
836 Some((slot, ..)) => slot,
837 None => self.added(
838 Some(index).filter(|index| !self.indexed.contains_key(index)),
839 &item,
840 &mut out,
841 )?,
842 };
843 taken.insert(slot);
844 self.done(slot, item, &mut out)?;
845 }
846 let open = self.slots.iter().any(|slot| slot.open);
849 self.release(!open, &mut out)?;
850 for slot in self.slots.iter_mut().filter(|slot| slot.open) {
852 if slot.kind == Kind::Call {
853 flush(slot, &mut out)?;
854 }
855 }
856 let (reason, error) = finish_reason_of(&response);
857 let end = Finish {
858 usage: response.get("usage").map(usage_of).unwrap_or_default(),
859 reason: Some(reason),
860 response_id: item_id(&response).map(str::to_owned),
861 model: response
862 .str("model")
863 .filter(|model| !model.is_empty())
864 .map(str::to_owned),
865 error,
866 };
867 Ok(out.end(end))
868 }
869}
870
871fn flush(slot: &Slot, out: &mut Out<'_, Completion>) -> Result<(), ProviderError> {
873 let arguments = streamed_arguments(slot);
874 let rest = remainder(slot, &arguments, out)?;
875 let fragment = CallFragment {
876 arguments: Some(&rest),
877 ..CallFragment::default()
878 };
879 out.fragment(Some(slot.at), fragment)
880}
881
882fn send(slot: &mut Slot, delta: &str, out: &mut Out<'_, Completion>) -> Result<(), ProviderError> {
888 if delta.is_empty() {
889 return Ok(());
890 }
891 let fragment = if slot.custom {
892 let quoted = serde_json::to_string(delta)?;
893 let open = quoted.strip_suffix('"').unwrap_or("ed);
894 Cow::Owned(match open.strip_prefix('"') {
895 Some(escaped) if !slot.arguments.is_empty() => escaped.to_owned(),
896 _ => format!("{{\"input\":{open}"),
897 })
898 } else {
899 Cow::Borrowed(delta)
900 };
901 slot.arguments.push_str(delta);
902 let fragment = CallFragment {
903 arguments: Some(&fragment),
904 ..CallFragment::default()
905 };
906 out.fragment(Some(slot.at), fragment)
907}
908
909fn remainder(
914 slot: &Slot,
915 arguments: &str,
916 out: &mut Out<'_, Completion>,
917) -> Result<String, ProviderError> {
918 let streamed = streamed_arguments(slot);
920 let sent = if slot.arguments.is_empty() {
921 ""
922 } else if slot.custom {
923 streamed.strip_suffix("\"}").unwrap_or(&streamed)
924 } else {
925 &streamed
926 };
927 Ok(match arguments.strip_prefix(sent) {
928 Some(rest) => rest.to_owned(),
929 None => {
930 out.restate(slot.at, arguments)?;
931 String::new()
932 }
933 })
934}
935
936fn streamed_arguments(slot: &Slot) -> String {
938 if slot.custom {
939 json!({ "input": slot.arguments }).to_string()
940 } else {
941 slot.arguments.clone()
942 }
943}
944
945fn output_index(frame: &Value) -> Result<Option<usize>, ProviderError> {
947 frame
948 .u64("output_index")
949 .map(|index| {
950 usize::try_from(index).map_err(|_| {
951 ProviderError::Response(format!("output_index {index} is out of range"))
952 })
953 })
954 .transpose()
955}
956
957impl<'id> Decoder<'id, Completion> for ResponsesDecoder {
958 type Event = ResponsesEvent;
959
960 fn classify(&self, frame: WireFrame) -> WireEvent<ResponsesEvent> {
961 classify_responses_payload(&frame.as_str())
962 }
963
964 fn decode(
965 &mut self,
966 event: ResponsesEvent,
967 mut out: Out<'id, Completion>,
968 ) -> Result<Flow, ProviderError> {
969 out.order_by_index();
972 match event {
973 ResponsesEvent::Frame {
974 kind,
975 mut frame,
976 raw,
977 } => match kind.as_str() {
978 "response.completed" | "response.incomplete" => {
982 let mut response = frame
983 .as_object_mut()
984 .and_then(|frame| remove_preserving_order(frame, "response"))
985 .filter(Value::is_object)
986 .unwrap_or_else(|| json!({}));
987 if let (true, Some(fields)) =
988 (kind == "response.incomplete", response.as_object_mut())
989 {
990 fields.insert("status".to_owned(), json!("incomplete"));
991 }
992 self.finish(response, out)
993 }
994 "response.failed" => {
995 Err(ProviderError::from_provider_body(raw.unwrap_or_default()))
996 }
997 "keepalive" => Ok(Flow::More),
998 kind if is_lifecycle_event(kind) => Ok(Flow::More),
999 kind => {
1000 self.item_event(kind, frame, &mut out)?;
1001 Ok(Flow::More)
1002 }
1003 },
1004 ResponsesEvent::Whole(body) => self.finish(body, out),
1006 ResponsesEvent::Failure(raw) => Err(ProviderError::from_provider_body(raw)),
1007 ResponsesEvent::Sentinel => Ok(Flow::More),
1009 }
1010 }
1011}
1012
1013pub(crate) mod document;
1014
1015#[cfg(test)]
1016mod tests;