use anyllm_translate::mapping::responses_streaming_map::ResponsesStreamEvent;
use anyllm_translate::openai::responses::{ResponsesResponse, ResponsesUsage};
use axum::response::sse::Event;
use runtime::StopReason;
use serde_json::{json, Value};
use tokenizer::ParsedToolCall;
use crate::handler::Generated;
pub(crate) fn reasoning_item(text: &str) -> Value {
json!({
"type": "reasoning",
"id": "rs_0",
"summary": [{"type": "summary_text", "text": text}],
})
}
pub(crate) fn message_item(text: &str, status: &str, id: &str) -> Value {
json!({
"type": "message",
"id": id,
"role": "assistant",
"status": status,
"content": [{"type": "output_text", "text": text, "annotations": []}],
})
}
pub(crate) fn function_call_item(call: &ParsedToolCall, status: &str) -> Value {
json!({
"type": "function_call",
"id": format!("fc_{}", call.id),
"call_id": call.id,
"name": call.name,
"arguments": call.arguments_json,
"status": status,
})
}
pub(crate) fn response_status(reason: StopReason) -> &'static str {
match reason {
StopReason::MaxTokens => "incomplete",
_ => "completed",
}
}
pub(crate) fn build_response(id: String, model: String, generated: Generated) -> ResponsesResponse {
let mut output = Vec::new();
if !generated.reasoning.is_empty() {
output.push(reasoning_item(&generated.reasoning));
}
if !generated.text.is_empty() {
output.push(message_item(&generated.text, "completed", "msg_0"));
}
for call in &generated.calls {
output.push(function_call_item(call, "completed"));
}
let status = response_status(generated.decode.reason);
let usage = ResponsesUsage {
input_tokens: generated.decode.prompt_tokens as u32,
output_tokens: generated.decode.new_tokens as u32,
total_tokens: (generated.decode.prompt_tokens + generated.decode.new_tokens) as u32,
input_token_details: None,
};
ResponsesResponse {
id,
response_type: "response".to_string(),
model,
output,
usage: Some(usage),
status: status.to_string(),
extra: serde_json::Map::new(),
}
}
fn sse_event(event_type: &'static str, fields: Vec<(&'static str, Value)>) -> Event {
let event = ResponsesStreamEvent {
event_type: event_type.to_string(),
data: fields
.into_iter()
.map(|(k, v)| (k.to_string(), v))
.collect(),
};
Event::default()
.event(event_type)
.data(serde_json::to_string(&event).unwrap_or_default())
}
pub(crate) fn created_event(id: &str, model: &str) -> Event {
sse_event(
"response.created",
vec![(
"response",
json!({"id": id, "object": "response", "status": "in_progress", "model": model}),
)],
)
}
pub(crate) fn completed_event(
id: &str,
model: &str,
decode: &runtime::RawDecodeResult,
output: &[Value],
) -> Event {
sse_event(
"response.completed",
vec![(
"response",
json!({
"id": id, "object": "response", "model": model,
"status": response_status(decode.reason),
"output": output,
"usage": {
"input_tokens": decode.prompt_tokens,
"output_tokens": decode.new_tokens,
"total_tokens": decode.prompt_tokens + decode.new_tokens,
},
}),
)],
)
}
pub(crate) fn failed_event(message: &str) -> Event {
sse_event(
"response.failed",
vec![(
"response",
json!({"status": "failed", "status_details": {"error": {"message": message}}}),
)],
)
}
pub(crate) fn open_message_event(item_id: &str, output_index: u32) -> Event {
sse_event(
"response.output_item.added",
vec![
("output_index", json!(output_index)),
(
"item",
json!({"id": item_id, "type": "message", "role": "assistant", "status": "in_progress", "content": []}),
),
],
)
}
pub(crate) fn content_part_added_event(item_id: &str, output_index: u32) -> Event {
sse_event(
"response.content_part.added",
vec![
("item_id", json!(item_id)),
("output_index", json!(output_index)),
("content_index", json!(0)),
(
"part",
json!({"type": "output_text", "text": "", "annotations": []}),
),
],
)
}
pub(crate) fn text_delta_event(item_id: &str, output_index: u32, delta: &str) -> Event {
sse_event(
"response.output_text.delta",
vec![
("item_id", json!(item_id)),
("output_index", json!(output_index)),
("content_index", json!(0)),
("delta", json!(delta)),
],
)
}
pub(crate) fn close_message_events(
send: &impl Fn(Event),
item_id: &str,
output_index: u32,
text: &str,
) {
send(sse_event(
"response.output_text.done",
vec![
("item_id", json!(item_id)),
("output_index", json!(output_index)),
("content_index", json!(0)),
("text", json!(text)),
],
));
send(sse_event(
"response.content_part.done",
vec![
("item_id", json!(item_id)),
("output_index", json!(output_index)),
("content_index", json!(0)),
(
"part",
json!({"type": "output_text", "text": text, "annotations": []}),
),
],
));
send(sse_event(
"response.output_item.done",
vec![
("output_index", json!(output_index)),
("item", message_item(text, "completed", item_id)),
],
));
}
pub(crate) fn function_call_events(
send: &impl Fn(Event),
call: &ParsedToolCall,
output_index: u32,
) {
let item_id = format!("fc_{}", call.id);
send(sse_event(
"response.output_item.added",
vec![
("output_index", json!(output_index)),
(
"item",
json!({"id": item_id, "type": "function_call", "call_id": call.id, "name": call.name, "arguments": "", "status": "in_progress"}),
),
],
));
send(sse_event(
"response.function_call_arguments.delta",
vec![
("item_id", json!(item_id)),
("output_index", json!(output_index)),
("delta", json!(call.arguments_json)),
],
));
send(sse_event(
"response.function_call_arguments.done",
vec![
("item_id", json!(item_id)),
("output_index", json!(output_index)),
("arguments", json!(call.arguments_json)),
],
));
send(sse_event(
"response.output_item.done",
vec![
("output_index", json!(output_index)),
("item", function_call_item(call, "completed")),
],
));
}