use crate::cognition::PillarResult;
use crate::corpus::default_corpus_dir;
use axiom_engine::session_recorder::{ExchangeRecord, read_session};
use serde_json::json;
use sha2::{Digest, Sha256};
use std::path::{Path, PathBuf};
fn corpus_dir() -> PathBuf {
default_corpus_dir()
}
#[derive(Clone, Copy, Debug, Default)]
struct SavingsMetrics {
bytes_in: u64,
bytes_forwarded: u64,
}
impl SavingsMetrics {
fn delta(self, before: Self) -> Self {
Self {
bytes_in: self.bytes_in.saturating_sub(before.bytes_in),
bytes_forwarded: self.bytes_forwarded.saturating_sub(before.bytes_forwarded),
}
}
}
fn corpus_files(dir: &Path) -> Vec<PathBuf> {
let mut files = std::fs::read_dir(dir)
.map(|entries| {
entries
.filter_map(|entry| entry.ok())
.map(|entry| entry.path())
.filter(|path| {
path.extension()
.and_then(|value| value.to_str())
.map(|value| value.eq_ignore_ascii_case("jsonl"))
.unwrap_or(false)
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
files.sort();
files
}
fn corpus_hash(files: &[PathBuf]) -> String {
let mut hasher = Sha256::new();
for path in files {
hasher.update(
path.file_name()
.and_then(|value| value.to_str())
.unwrap_or_default(),
);
if let Ok(bytes) = std::fs::read(path) {
hasher.update(Sha256::digest(bytes));
}
}
format!("{:x}", hasher.finalize())
}
fn parse_metrics(text: &str) -> SavingsMetrics {
let mut metrics = SavingsMetrics::default();
for line in text.lines() {
let mut parts = line.split_whitespace();
match (parts.next(), parts.next()) {
(Some("axiom_savings_bytes_in_total"), Some(value)) => {
metrics.bytes_in = value.parse::<f64>().map(|value| value as u64).unwrap_or(0);
}
(Some("axiom_savings_bytes_forwarded_total"), Some(value)) => {
metrics.bytes_forwarded =
value.parse::<f64>().map(|value| value as u64).unwrap_or(0);
}
_ => {}
}
}
metrics
}
fn request_bytes(record: &ExchangeRecord) -> u64 {
serde_json::to_vec(&record.request)
.map(|bytes| bytes.len() as u64)
.unwrap_or(0)
}
fn read_metrics(
client: &reqwest::blocking::Client,
base_url: &str,
) -> Result<SavingsMetrics, String> {
let url = format!("{}/metrics", base_url.trim_end_matches('/'));
let response = client
.get(&url)
.send()
.map_err(|error| format!("GET {url} failed: {error}"))?;
if !response.status().is_success() {
return Err(format!("GET {url} returned {}", response.status()));
}
let text = response
.text()
.map_err(|error| format!("read metrics body failed: {error}"))?;
Ok(parse_metrics(&text))
}
fn replay_authorization_with(explicit: Option<&str>, openai_key: Option<&str>) -> String {
if let Some(value) = explicit.filter(|value| !value.trim().is_empty()) {
return value.to_string();
}
if let Some(value) = openai_key.filter(|value| !value.trim().is_empty()) {
return format!("Bearer {value}");
}
"Bearer axiombench-live-replay".to_string()
}
fn replay_authorization() -> String {
replay_authorization_with(
std::env::var("AXIOMBENCH_AUTHORIZATION").ok().as_deref(),
std::env::var("OPENAI_API_KEY").ok().as_deref(),
)
}
fn replay_record(
client: &reqwest::blocking::Client,
base_url: &str,
record: &ExchangeRecord,
authorization: &str,
compression_enabled: bool,
) -> Result<(u16, SavingsMetrics), String> {
let before = read_metrics(client, base_url)?;
let url = format!("{}/v1/responses", base_url.trim_end_matches('/'));
let mut request = client
.post(&url)
.header("content-type", "application/json")
.header("authorization", authorization)
.header(
"x-axiom-session-id",
format!("axiombench-{}", record.session_id),
);
if !compression_enabled {
request = request.header("x-axiom-responses-compress", "0");
}
let response = request
.json(&record.request)
.send()
.map_err(|error| format!("POST {url} failed: {error}"))?;
let status = response.status().as_u16();
let _ = response.bytes();
let after = read_metrics(client, base_url)?;
Ok((status, after.delta(before)))
}
pub fn run_cost_with(base_url: &str, dir: &Path) -> PillarResult {
let files = corpus_files(dir);
let sessions = files.len();
if sessions == 0 {
return PillarResult {
name: "cost".into(),
headline: "skipped - no corpus (add bench/corpus/*.jsonl and run with --live)".into(),
detail: json!({ "skipped": true, "reason": "no corpus", "corpus_dir": dir.display().to_string() }),
};
}
let client = match reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(90))
.build()
{
Ok(client) => client,
Err(error) => {
return PillarResult {
name: "cost".into(),
headline: format!("skipped - HTTP client unavailable ({error})"),
detail: json!({ "skipped": true, "reason": "client", "error": error.to_string() }),
};
}
};
if let Err(error) = read_metrics(&client, base_url) {
return PillarResult {
name: "cost".into(),
headline: format!("skipped - proxy metrics unavailable at {base_url}"),
detail: json!({ "skipped": true, "reason": "proxy unavailable", "base_url": base_url, "error": error }),
};
}
let mut replayed = 0usize;
let mut errored = 0usize;
let mut skipped_records = 0usize;
let mut bytes_in = 0u64;
let mut bytes_forwarded = 0u64;
let mut details = Vec::new();
let authorization = replay_authorization();
for path in &files {
let records = match read_session(path) {
Ok(records) => records,
Err(error) => {
errored += 1;
details.push(
json!({ "file": path.display().to_string(), "error": error.to_string() }),
);
continue;
}
};
for record in records {
if record.endpoint != "/v1/responses" {
skipped_records += 1;
continue;
}
let off = replay_record(&client, base_url, &record, &authorization, false);
let on = replay_record(&client, base_url, &record, &authorization, true);
match (off, on) {
(Ok((off_status, _)), Ok((on_status, delta)))
if (200..300).contains(&off_status) && (200..300).contains(&on_status) =>
{
replayed += 1;
let original_bytes = request_bytes(&record);
let saved = delta.bytes_in.saturating_sub(delta.bytes_forwarded);
bytes_in = bytes_in.saturating_add(original_bytes);
bytes_forwarded =
bytes_forwarded.saturating_add(original_bytes.saturating_sub(saved));
}
(Ok((off_status, off_delta)), Ok((on_status, on_delta))) => {
errored += 1;
details.push(json!({
"file": path.display().to_string(),
"session_id": record.session_id,
"off_status": off_status,
"on_status": on_status,
"off_bytes_in_delta": off_delta.bytes_in,
"off_bytes_forwarded_delta": off_delta.bytes_forwarded,
"on_bytes_in_delta": on_delta.bytes_in,
"on_bytes_forwarded_delta": on_delta.bytes_forwarded,
}));
}
(Err(error), _) => {
errored += 1;
details.push(json!({
"file": path.display().to_string(),
"session_id": record.session_id,
"arm": "compression_off",
"error": error,
}));
}
(_, Err(error)) => {
errored += 1;
details.push(json!({
"file": path.display().to_string(),
"session_id": record.session_id,
"arm": "compression_on",
"error": error,
}));
}
}
}
}
let saved = bytes_in.saturating_sub(bytes_forwarded);
let reduction = if bytes_in > 0 {
saved as f64 / bytes_in as f64
} else {
0.0
};
let hash = corpus_hash(&files);
let headline = if replayed == 0 {
format!(
"no successful live cost replays ({errored} errored, {skipped_records} skipped) on {sessions} corpus session(s)"
)
} else {
format!(
"{:.1}% byte reduction over {replayed} replayed record(s) from {sessions} session(s)",
reduction * 100.0
)
};
PillarResult {
name: "cost".into(),
headline,
detail: json!({
"skipped": false,
"base_url": base_url,
"corpus_dir": dir.display().to_string(),
"corpus_hash": hash,
"precondition": "run against an idle proxy; /metrics savings counters are process-global",
"sessions": sessions,
"replayed": replayed,
"paired_replays": replayed,
"errored": errored,
"skipped_records": skipped_records,
"bytes_in": bytes_in,
"bytes_forwarded": bytes_forwarded,
"byte_reduction": reduction,
"errors": details,
}),
}
}
pub fn run_cost(base_url: &str) -> PillarResult {
run_cost_with(base_url, &corpus_dir())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cost_skips_cleanly_without_corpus() {
let temp = tempfile::tempdir().unwrap();
let r = run_cost_with("http://127.0.0.1:3000", temp.path());
assert_eq!(r.name, "cost");
assert_eq!(r.detail["skipped"], true);
assert_eq!(r.detail["reason"], "no corpus");
}
#[test]
fn parses_savings_metrics() {
let metrics = parse_metrics(
"# TYPE axiom_savings_bytes_in_total counter\n\
axiom_savings_bytes_in_total 1200\n\
axiom_savings_bytes_forwarded_total 300\n",
);
assert_eq!(metrics.bytes_in, 1200);
assert_eq!(metrics.bytes_forwarded, 300);
}
#[test]
fn replay_authorization_prefers_explicit_then_openai_key() {
assert_eq!(
replay_authorization_with(Some("Bearer custom"), Some("sk-test")),
"Bearer custom"
);
assert_eq!(
replay_authorization_with(None, Some("sk-test")),
"Bearer sk-test"
);
assert_eq!(
replay_authorization_with(None, None),
"Bearer axiombench-live-replay"
);
}
}