use serde_json::{json, Value};
use super::acc::{finish_block, merge_usage, open_block, OpenBlock};
use super::frames;
use crate::canonical::{ContentKind, Delta, Event};
use crate::ingress::state::IngressState;
pub(crate) use super::acc::AnthAcc;
pub(crate) fn encode_response(event: &Event, state: &mut IngressState) -> Vec<u8> {
match event {
Event::MessageStart { id, model, .. } => {
state.id.clone_from(id);
state.model.clone_from(model);
emit(state, "message_start", |st| {
json!({"type": "message_start",
"message": frames::message_object(st, frames::usage_json(&st.anth.usage))})
})
}
Event::ContentStart { index, kind } => start(*index, kind, state),
Event::ContentDelta { index, delta } => fragment(*index, delta, state),
Event::ContentStop { index } => stop(*index, state),
Event::Usage(u) => {
merge_usage(&mut state.anth.usage, u);
Vec::new() }
Event::Finish { reason } => {
let (r, details) = frames::stop_reason(reason);
state.anth.stop_reason = Some(r);
state.anth.stop_details = details;
emit(state, "message_delta", message_delta)
}
Event::Error(e) => {
let (status, envelope) = frames::error(e);
state.status = Some(status);
let out = if state.stream {
frames::frame(state, "error", &envelope)
} else {
Vec::new()
};
state.error = Some(envelope);
out
}
Event::End => {
if state.stream {
if state.error.is_some() {
Vec::new()
} else {
emit(state, "message_stop", |_| json!({"type": "message_stop"}))
}
} else {
body(state)
}
}
Event::Raw(_) | Event::Other => Vec::new(),
}
}
fn emit(state: &mut IngressState, name: &str, build: impl Fn(&IngressState) -> Value) -> Vec<u8> {
if !state.stream {
return Vec::new();
}
let data = build(state);
frames::frame(state, name, &data)
}
fn start(index: u32, kind: &ContentKind, state: &mut IngressState) -> Vec<u8> {
let (block, cb) = open_block(kind);
state.anth.open.insert(index, block);
match cb {
Some(cb) => emit(
state,
"content_block_start",
|_| json!({"type": "content_block_start", "index": index, "content_block": cb}),
),
None => Vec::new(),
}
}
fn fragment(index: u32, delta: &Delta, state: &mut IngressState) -> Vec<u8> {
let wire = match (state.anth.open.get_mut(&index), delta) {
(Some(OpenBlock::Text(t)), Delta::TextDelta(d)) => {
t.push_str(d);
Some(json!({"type": "text_delta", "text": d}))
}
(Some(OpenBlock::Tool { args, .. }), Delta::JsonDelta(d)) => {
args.push_str(d);
Some(json!({"type": "input_json_delta", "partial_json": d}))
}
(Some(OpenBlock::Thinking { text, .. }), Delta::ThinkingDelta(d)) => {
text.push_str(d);
Some(json!({"type": "thinking_delta", "thinking": d}))
}
(Some(OpenBlock::Thinking { signature, .. }), Delta::SignatureDelta(d)) => {
signature.push_str(d);
Some(json!({"type": "signature_delta", "signature": d}))
}
_ => None,
};
match wire {
Some(d) => emit(
state,
"content_block_delta",
|_| json!({"type": "content_block_delta", "index": index, "delta": d}),
),
None => Vec::new(),
}
}
fn stop(index: u32, state: &mut IngressState) -> Vec<u8> {
let Some(block) = state.anth.open.remove(&index) else {
return Vec::new();
};
match finish_block(block) {
Some(v) => {
state.anth.content.push(v);
emit(
state,
"content_block_stop",
|_| json!({"type": "content_block_stop", "index": index}),
)
}
None => Vec::new(),
}
}
fn message_delta(state: &IngressState) -> Value {
let mut delta = json!({"stop_reason": state.anth.stop_reason, "stop_sequence": null});
if let Some(sd) = &state.anth.stop_details {
delta["stop_details"] = sd.clone();
}
json!({"type": "message_delta", "delta": delta,
"usage": frames::usage_json(&state.anth.usage)})
}
fn body(state: &IngressState) -> Vec<u8> {
let mut v = match &state.error {
Some(e) => e.clone(),
None => success_body(state),
};
if !state.adaptations.is_empty() {
v["brazen"] = json!({"adaptations": state.adaptations});
}
v.to_string().into_bytes()
}
fn success_body(state: &IngressState) -> Value {
let mut v = json!({
"type": "message",
"id": frames::wire_id(state),
"role": "assistant",
"model": state.wire_model(),
"content": state.anth.content,
"stop_reason": state.anth.stop_reason,
"stop_sequence": null,
"usage": frames::usage_json(&state.anth.usage),
});
if let Some(sd) = &state.anth.stop_details {
v["stop_details"] = sd.clone();
}
v
}