use serde_json::{Map, Value};
use super::{ChatDecoder, ChatRecord};
use crate::wire::document::Reassemble;
use crate::wire::{Decoder, WireEvent, WireFrame};
#[derive(Debug, Default)]
pub struct ChatResponse {
classifier: ChatDecoder,
document: Map<String, Value>,
}
const APPENDED: &[&str] = &["content", "thinking"];
const COLLECTED: &[&str] = &["tool_calls", "images", "logprobs"];
fn fold(into: &mut Map<String, Value>, fields: Map<String, Value>, appended: &[&str]) {
for (key, value) in fields {
match (into.get_mut(&key), value) {
(Some(Value::String(text)), Value::String(more))
if appended.contains(&key.as_str()) =>
{
text.push_str(&more);
}
(Some(Value::Array(items)), Value::Array(more))
if COLLECTED.contains(&key.as_str()) =>
{
items.extend(more);
}
(Some(_), Value::Null) => {}
(_, value) => {
into.insert(key, value);
}
}
}
}
impl crate::wire::document::Serves<crate::operation::Completion> for ChatResponse {}
impl Reassemble<WireFrame> for ChatResponse {
fn absorb(&mut self, frame: &WireFrame) {
let WireEvent::Known(ChatRecord(mut fields)) = self.classifier.classify(frame.clone())
else {
return;
};
if let Some(Value::Object(message)) = fields.shift_remove("message") {
match self.document.get_mut("message") {
Some(Value::Object(into)) => fold(into, message, APPENDED),
_ => {
self.document
.insert("message".to_owned(), Value::Object(message));
}
}
}
fold(&mut self.document, fields, &[]);
}
fn finish(self) -> Value {
if self.document.is_empty() {
Value::Null
} else {
Value::Object(self.document)
}
}
}