#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use rig_core::schemars;
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
struct Incident {
service: String,
severity: String,
symptoms: Vec<String>,
#[serde(deserialize_with = "lenient_bool")]
#[schemars(with = "bool")]
deploy_related: bool,
}
fn lenient_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum BoolIsh {
Bool(bool),
Text(String),
}
use serde::Deserialize as _;
match BoolIsh::deserialize(deserializer)? {
BoolIsh::Bool(value) => Ok(value),
BoolIsh::Text(text) => match text.to_lowercase().as_str() {
"true" | "yes" => Ok(true),
"false" | "no" => Ok(false),
other => Err(serde::de::Error::custom(format!(
"not a boolean: {other:?}"
))),
},
}
}
const REPORT: &str = "PagerDuty alert 04:12 UTC: checkout success rate dropped from 99.2% to \
61%. Customers report card payments hanging and then failing. The \
checkout-web frontend shows intermittent 502s from its payment backend \
payments-api. A deploy went out to payments-api about 15 minutes before \
the alert. Impact is severe and ongoing.";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let model_dir = std::env::args()
.nth(1)
.expect("usage: extract <mlx-model-dir>");
let extractor = emelex::Client::from_path(model_dir)?
.extractor::<Incident>()
.preamble(
"Extract the incident facts from the report. Severity must be one of: \
low, medium, high, critical.",
)
.additional_params(serde_json::json!({ "temperature": 0.3 }))
.retries(2)
.build();
let response = extractor.extract_with_usage(REPORT).await?;
println!("extracted: {:#?}", response.data);
println!(
"[usage] {} in ({} cached), {} out",
response.usage.input_tokens,
response.usage.cached_input_tokens,
response.usage.output_tokens
);
Ok(())
}