use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct Observation {
#[serde(default)]
pub sample_id: String,
pub label: Option<String>,
pub score: Option<f64>,
pub evaluator_id: Option<String>,
pub budget: Option<f64>,
pub seed: Option<u64>,
pub model_id: Option<String>,
pub run_id: Option<String>,
pub gold_label: Option<String>,
}
impl Observation {
pub fn validate(&self, line: usize) -> crate::error::Result<()> {
if self.sample_id.trim().is_empty() {
return Err(crate::error::Error::MissingField("sample_id"));
}
if self.score.is_some_and(|s| !s.is_finite()) {
return Err(crate::error::Error::InvalidScore { line });
}
if self.budget.is_some_and(|b| !b.is_finite()) {
return Err(crate::error::Error::InvalidBudget { line });
}
Ok(())
}
}
pub fn parse_jsonl(input: &str) -> crate::error::Result<Vec<Observation>> {
let mut out = Vec::new();
for (i, line) in input.lines().enumerate() {
let line = line.trim();
if line.is_empty() {
continue;
}
let obs: Observation =
serde_json::from_str(line).map_err(|source| crate::error::Error::ParseError {
line: i + 1,
source,
})?;
obs.validate(i + 1)?;
out.push(obs);
}
Ok(out)
}
pub fn parse_csv(input: &[u8]) -> crate::error::Result<Vec<Observation>> {
let mut rdr = csv::Reader::from_reader(input);
let mut out = Vec::new();
for (i, record) in rdr.deserialize::<Observation>().enumerate() {
let obs = record?;
obs.validate(i + 1)?;
out.push(obs);
}
Ok(out)
}