use serde_json::{json, Value};
use crate::canonical::{CanonicalError, Event};
use crate::protocol::json::{parse, text_of};
use crate::protocol::DecodeState;
use super::event;
pub(crate) fn decode_full(
body: &[u8],
state: &mut DecodeState,
) -> Result<Vec<Event>, CanonicalError> {
let response = parse(body)?;
let mut out = event(&created(&response), state);
for (oi, item) in response["output"]
.as_array()
.into_iter()
.flatten()
.enumerate()
{
explode_item(oi as u32, item, state, &mut out);
}
out.extend(event(&completed(&response), state));
Ok(out)
}
fn explode_item(oi: u32, item: &Value, state: &mut DecodeState, out: &mut Vec<Event>) {
let added = json!({ "type": "response.output_item.added", "output_index": oi, "item": item });
out.extend(event(&added, state));
match item["type"].as_str().unwrap_or_default() {
"message" => {
for (ci, part) in item["content"].as_array().into_iter().flatten().enumerate() {
explode_part(oi, ci as u32, part, state, out);
}
}
"function_call" => out.extend(event(
&arg_delta(
oi,
"response.function_call_arguments.delta",
&text_of(item, "arguments"),
),
state,
)),
"reasoning" => {
for s in item["summary"].as_array().into_iter().flatten() {
out.extend(event(
&arg_delta(
oi,
"response.reasoning_summary_text.delta",
&text_of(s, "text"),
),
state,
));
}
}
_ => {}
}
let done = json!({ "type": "response.output_item.done", "output_index": oi, "item": item });
out.extend(event(&done, state));
}
fn explode_part(oi: u32, ci: u32, part: &Value, state: &mut DecodeState, out: &mut Vec<Event>) {
let added = json!({ "type": "response.content_part.added", "output_index": oi, "content_index": ci, "part": part });
out.extend(event(&added, state));
let delta = json!({ "type": "response.output_text.delta", "output_index": oi, "content_index": ci, "delta": part["text"] });
out.extend(event(&delta, state));
}
fn arg_delta(oi: u32, ty: &str, delta: &str) -> Value {
json!({ "type": ty, "output_index": oi, "delta": delta })
}
fn created(response: &Value) -> Value {
json!({ "type": "response.created", "response": response })
}
fn completed(response: &Value) -> Value {
json!({ "type": "response.completed", "response": response })
}