use std::fmt::Write as _;
use serde_json::{json, Value};
use crate::canonical::{CanonicalError, FinishReason, Usage};
use crate::ingress::state::IngressState;
pub(super) fn frame(state: &mut IngressState, name: &str, data: &Value) -> Vec<u8> {
format!("{}event: {name}\ndata: {data}\n\n", comments(state)).into_bytes()
}
fn comments(state: &mut IngressState) -> String {
let mut out = String::new();
for name in state.pending.drain(..) {
let _ = writeln!(out, ": brazen adaptation={name}");
}
out
}
pub(super) fn wire_id(state: &IngressState) -> String {
state
.id
.clone()
.unwrap_or_else(|| format!("msg_brazen-{}", state.created))
}
pub(super) fn message_object(state: &IngressState, usage: Value) -> Value {
json!({
"type": "message",
"id": wire_id(state),
"role": "assistant",
"model": state.wire_model(),
"content": [],
"stop_reason": null,
"stop_sequence": null,
"usage": usage,
})
}
pub(super) fn usage_json(u: &Usage) -> Value {
let mut v = json!({
"input_tokens": u.input_tokens.unwrap_or(0),
"output_tokens": u.output_tokens.unwrap_or(0),
});
if let Some(c) = u.cache_read_tokens {
v["cache_read_input_tokens"] = json!(c);
}
if let Some(c) = u.cache_write_tokens {
v["cache_creation_input_tokens"] = json!(c);
}
v
}
pub(super) fn stop_reason(reason: &FinishReason) -> (String, Option<Value>) {
match reason {
FinishReason::Stop => ("end_turn".into(), None),
FinishReason::Length => ("max_tokens".into(), None),
FinishReason::StopSequence => ("stop_sequence".into(), None),
FinishReason::ToolUse => ("tool_use".into(), None),
FinishReason::Pause => ("pause_turn".into(), None),
FinishReason::Refusal {
category,
explanation,
} => (
"refusal".into(),
Some(json!({"category": category, "explanation": explanation})),
),
FinishReason::Other(s) => (s.clone(), None),
}
}
pub(super) fn error(e: &CanonicalError) -> (u16, Value) {
let status = e.kind.http_status();
let envelope = json!({
"type": "error",
"error": {"type": error_type(status), "message": e.message},
});
(status, envelope)
}
fn error_type(status: u16) -> &'static str {
match status {
401 | 403 => "authentication_error",
400 => "invalid_request_error",
402 => "billing_error",
404 => "not_found_error",
413 => "request_too_large",
429 => "rate_limit_error",
529 => "overloaded_error",
504 => "timeout_error",
500..=599 => "api_error",
_ => "invalid_request_error",
}
}