use salvor_core::{PendingCall, RunState, RunStatus};
use serde_json::{Value, json};
#[must_use]
pub fn status(status: &RunStatus) -> Value {
match status {
RunStatus::NotStarted => json!({ "state": "not_started" }),
RunStatus::Running => json!({ "state": "running" }),
RunStatus::AwaitingModel => json!({ "state": "awaiting_model" }),
RunStatus::AwaitingTool => json!({ "state": "awaiting_tool" }),
RunStatus::Suspended {
reason,
input_schema,
} => json!({
"state": "suspended",
"reason": reason,
"input_schema": input_schema,
}),
RunStatus::BudgetExceeded { budget, observed } => json!({
"state": "budget_exceeded",
"budget": budget,
"observed": observed,
}),
RunStatus::NeedsReconciliation => json!({ "state": "needs_reconciliation" }),
RunStatus::Completed { output } => json!({ "state": "completed", "output": output }),
RunStatus::Failed { error } => json!({ "state": "failed", "error": error }),
RunStatus::Abandoned {
reason,
unresolved_write,
} => {
let mut obj = json!({ "state": "abandoned" });
let map = obj.as_object_mut().expect("status object");
if let Some(reason) = reason {
map.insert("reason".to_owned(), json!(reason));
}
if let Some(write) = unresolved_write {
map.insert(
"unresolved_write".to_owned(),
json!({ "seq": write.seq.get(), "tool": write.tool }),
);
}
obj
}
}
}
#[must_use]
pub fn pending(pending: Option<&PendingCall>) -> Value {
match pending {
None => Value::Null,
Some(PendingCall::Model { seq, request_hash }) => json!({
"kind": "model",
"seq": seq.get(),
"request_hash": request_hash,
}),
Some(PendingCall::Tool {
seq,
tool,
input,
effect,
idempotency_key,
}) => json!({
"kind": "tool",
"seq": seq.get(),
"tool": tool,
"input": input,
"effect": effect,
"idempotency_key": idempotency_key,
}),
}
}
#[must_use]
pub fn run_state(state: &RunState) -> Value {
json!({
"status": status(&state.status),
"usage": {
"input_tokens": state.usage.input_tokens,
"output_tokens": state.usage.output_tokens,
},
"next_seq": state.next_seq.get(),
"pending": pending(state.pending_call.as_ref()),
})
}