use serde_json::{Map, Value};
use super::convs::AgentState;
use super::fields;
use super::queue::Held;
use super::spend::Figure;
pub(crate) const KIND: &str = "agent";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Agent {
pub agent: String,
pub root: String,
pub ancestors: Vec<String>,
pub display: String,
pub display_only: bool,
pub tip: String,
pub state: AgentState,
pub refused: bool,
pub failure: Option<String>,
pub marks: Vec<String>,
pub flight: Option<String>,
pub held: Option<Held>,
pub present: bool,
pub offers: Vec<Offer>,
pub seats: Vec<Seat>,
pub strip: Option<Strip>,
pub spend: Figure,
pub context: Option<Fullness>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fullness {
pub model: String,
pub prompt_tokens: u64,
pub window: u64,
pub percent: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Offer {
Nudge,
Stop,
Children,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Seat {
pub name: String,
pub doing: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Strip {
pub class: String,
pub facts: String,
}
pub(crate) fn agent(obj: &Map<String, Value>) -> Result<Agent, String> {
Ok(Agent {
agent: fields::text(obj, "agent")?,
root: fields::text(obj, "root")?,
ancestors: optional_list(obj, "ancestors")?,
display: fields::text(obj, "display")?,
display_only: fields::flag(obj, "display_only")?,
tip: fields::text(obj, "tip")?,
state: AgentState::of(&fields::text(obj, "state")?),
refused: fields::flag(obj, "refused")?,
failure: fields::opt_text(obj, "failure")?,
marks: optional_list(obj, "marks")?,
flight: fields::opt_text(obj, "flight")?,
held: fields::nested(obj, "held", held)?,
present: fields::flag(obj, "present")?,
offers: offers(obj)?,
seats: match obj.get("seats") {
None => Vec::new(),
Some(_) => fields::list(obj, "seats", seat)?,
},
strip: fields::nested(obj, "strip", strip)?,
spend: super::spend::figure(obj.get("spend").ok_or("agent: missing \"spend\"")?)?,
context: fields::nested(obj, "context", fullness)?,
})
}
fn offers(obj: &Map<String, Value>) -> Result<Vec<Offer>, String> {
let gates = [
("nudgeable", Offer::Nudge),
("stoppable", Offer::Stop),
("stop_children", Offer::Children),
];
let mut offered = Vec::new();
for (key, offer) in gates {
if fields::flag(obj, key)? {
offered.push(offer);
}
}
Ok(offered)
}
fn optional_list(obj: &Map<String, Value>, key: &str) -> Result<Vec<String>, String> {
match obj.get(key) {
None => Ok(Vec::new()),
Some(_) => fields::list(obj, key, word),
}
}
fn word(value: &Value) -> Result<String, String> {
value
.as_str()
.map(str::to_owned)
.ok_or_else(|| "a non-string word in a list".to_owned())
}
fn held(obj: &Map<String, Value>) -> Result<Held, String> {
Ok(Held {
tool: fields::text(obj, "tool")?,
tool_use: fields::text(obj, "tool_use_id")?,
reason: fields::text(obj, "reason")?,
})
}
fn seat(value: &Value) -> Result<Seat, String> {
let obj: &Map<String, Value> = value.as_object().ok_or("seat: not an object")?;
Ok(Seat {
name: fields::text(obj, "name")?,
doing: fields::text(obj, "doing")?,
})
}
fn fullness(obj: &Map<String, Value>) -> Result<Fullness, String> {
Ok(Fullness {
model: fields::text(obj, "model")?,
prompt_tokens: fields::count(obj, "prompt_tokens")?,
window: fields::count(obj, "window")?,
percent: fields::count(obj, "percent")?,
})
}
fn strip(obj: &Map<String, Value>) -> Result<Strip, String> {
Ok(Strip {
class: fields::text(obj, "class")?,
facts: fields::text(obj, "facts")?,
})
}
#[cfg(test)]
mod tests;