use serde_json::{Map, Value};
use super::diff::Diff;
use super::{diff, fields};
pub(crate) const KIND: &str = "science";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attempt {
pub diff: Diff,
pub base: Option<String>,
pub conversation: Option<String>,
pub goal: Option<String>,
pub governing: Option<String>,
pub response: Option<String>,
pub pins: Vec<String>,
pub usage: Usage,
pub wall_secs: u64,
pub steps: u64,
pub verdicts: Vec<Verdict>,
pub compacted: Option<u64>,
pub outcome: Ending,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Usage {
pub input: u64,
pub output: u64,
pub cache_read: u64,
pub cache_write: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Verdict {
pub sender: String,
pub body: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ending {
pub state: String,
pub commit: Option<String>,
pub by: Option<String>,
}
pub(crate) fn row(value: &Value) -> Result<Attempt, String> {
let obj: &Map<String, Value> = value.as_object().ok_or("science row: not an object")?;
Ok(Attempt {
diff: diff::diff(obj.get("diff").ok_or("science row: missing diff")?)?,
base: fields::opt_text(obj, "base")?,
conversation: fields::opt_text(obj, "conversation")?,
goal: fields::opt_text(obj, "goal")?,
governing: fields::opt_text(obj, "governing")?,
response: fields::opt_text(obj, "response")?,
pins: fields::strings(obj, "pins")?,
usage: usage(obj)?,
wall_secs: fields::count(obj, "wall_secs")?,
steps: fields::count(obj, "steps")?,
verdicts: fields::list(obj, "verdicts", verdict)?,
compacted: fields::opt_count(obj, "compacted")?,
outcome: ending(obj)?,
})
}
fn usage(obj: &Map<String, Value>) -> Result<Usage, String> {
let held = obj
.get("usage")
.and_then(Value::as_object)
.ok_or("missing or non-object field \"usage\"")?;
Ok(Usage {
input: fields::count(held, "input_tokens")?,
output: fields::count(held, "output_tokens")?,
cache_read: fields::count(held, "cache_read_tokens")?,
cache_write: fields::count(held, "cache_write_tokens")?,
})
}
fn verdict(value: &Value) -> Result<Verdict, String> {
let obj: &Map<String, Value> = value.as_object().ok_or("verdict: not an object")?;
Ok(Verdict {
sender: fields::text(obj, "sender")?,
body: fields::text(obj, "body")?,
})
}
fn ending(obj: &Map<String, Value>) -> Result<Ending, String> {
let held = obj
.get("outcome")
.and_then(Value::as_object)
.ok_or("missing or non-object field \"outcome\"")?;
Ok(Ending {
state: fields::text(held, "state")?,
commit: fields::opt_text(held, "commit")?,
by: fields::opt_text(held, "by")?,
})
}
#[cfg(test)]
mod tests;