Files
codecraft/cli/src/stats.rs
T
Richard ZinkandClaude Opus 5.5 6dffcb501a Add terminal client, memory, model fallback, version and install improvements
- codecraft: full-screen terminal client (ratatui) – local tools and files,
  models via the CodeCraft server gateway or own providers (standalone with a
  guided setup), slash commands, @files, permission dialogs, resume, update
- Static Linux CLI builds (x86_64/aarch64, cargo-zigbuild) served by the
  server: /cli/install.sh and /cli/download/<target>
- Project memory (remember tool, .codecraft/memory.md) and personal memory
- Model fallback when a host is unreachable or a budget is used up
- Version/commit in UI and health, daily release check, "What's new" dialog
- PWA: install button, maskable icon, manifest id and screenshots
- Case-insensitive detection of connection errors for retries

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 00:13:06 +01:00

37 lines
1.6 KiB
Rust

//! Local usage log (~/.local/share/codecraft/usage.jsonl) for /costs.
use crate::session::LocalSession;
use codecraft_core::types::{Usage, now_ms};
use serde_json::{Value, json};
use std::io::Write;
fn path() -> std::path::PathBuf {
crate::conf::data_dir().join("usage.jsonl")
}
pub fn record(session: &LocalSession, model: &str, u: &Usage, cost: f64) {
let line = json!({"ts": now_ms(), "session": session.id, "model": model, "in": u.input_tokens, "out": u.output_tokens, "cache_read": u.cache_read_tokens, "cache_write": u.cache_write_tokens, "cost": cost});
let _ = std::fs::create_dir_all(crate::conf::data_dir());
if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(path()) {
let _ = writeln!(f, "{line}");
}
}
/// (requests, input, output, cost) per model since `since` (ms).
pub fn summary(since: i64) -> Vec<(String, u64, u64, u64, f64)> {
let text = std::fs::read_to_string(path()).unwrap_or_default();
let mut map: std::collections::BTreeMap<String, (u64, u64, u64, f64)> = Default::default();
for line in text.lines() {
let Ok(v) = serde_json::from_str::<Value>(line) else { continue };
if v["ts"].as_i64().unwrap_or(0) < since {
continue;
}
let e = map.entry(v["model"].as_str().unwrap_or("?").to_string()).or_default();
e.0 += 1;
e.1 += v["in"].as_u64().unwrap_or(0) + v["cache_read"].as_u64().unwrap_or(0) + v["cache_write"].as_u64().unwrap_or(0);
e.2 += v["out"].as_u64().unwrap_or(0);
e.3 += v["cost"].as_f64().unwrap_or(0.0);
}
map.into_iter().map(|(k, (n, i, o, c))| (k, n, i, o, c)).collect()
}