feat(#1466): add sessions table + turn_stats.session_id capture for per-session stats
This commit is contained in:
parent
0c1c06ad76
commit
17ddba0341
5 changed files with 102 additions and 3 deletions
|
|
@ -46,10 +46,21 @@ CREATE TABLE IF NOT EXISTS turn_stats (
|
|||
open_threads_count INTEGER,
|
||||
open_reminders_count INTEGER,
|
||||
result_kind TEXT NOT NULL,
|
||||
note TEXT
|
||||
note TEXT,
|
||||
session_id INTEGER
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_turn_stats_started
|
||||
ON turn_stats (started_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_turn_stats_session
|
||||
ON turn_stats (session_id);
|
||||
-- One row per fresh claude session (minted when --continue is suppressed).
|
||||
-- turn_stats.session_id FKs here so per-session stats (first-turn tokens,
|
||||
-- per-session totals, turn count, duration) are one GROUP BY away.
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
started_at INTEGER NOT NULL,
|
||||
model TEXT NOT NULL
|
||||
);
|
||||
";
|
||||
|
||||
/// Additive column migrations. Each runs unconditionally and ignores
|
||||
|
|
@ -61,6 +72,9 @@ const MIGRATIONS: &[&str] = &[
|
|||
"ALTER TABLE turn_stats ADD COLUMN last_output_tokens INTEGER NOT NULL DEFAULT 0",
|
||||
"ALTER TABLE turn_stats ADD COLUMN last_cache_read_input_tokens INTEGER NOT NULL DEFAULT 0",
|
||||
"ALTER TABLE turn_stats ADD COLUMN last_cache_creation_input_tokens INTEGER NOT NULL DEFAULT 0",
|
||||
// Nullable FK to sessions.id — no default; pre-migration rows stay NULL
|
||||
// (the surface treats NULL as "no session", inert until capture lands).
|
||||
"ALTER TABLE turn_stats ADD COLUMN session_id INTEGER",
|
||||
];
|
||||
|
||||
/// One row to be inserted. `Option`-wrapped fields default to NULL
|
||||
|
|
@ -92,6 +106,10 @@ pub struct TurnStatRow {
|
|||
/// `"ok" | "failed" | "prompt_too_long"`.
|
||||
pub result_kind: &'static str,
|
||||
pub note: Option<String>,
|
||||
/// FK to `sessions.id` for the fresh claude session this turn belongs
|
||||
/// to. `None` on pre-capture rows (and when the stats db couldn't mint
|
||||
/// a session) so the read side degrades to empty.
|
||||
pub session_id: Option<i64>,
|
||||
}
|
||||
|
||||
/// Thin sqlite wrapper. Cloning is cheap (Arc-shared connection).
|
||||
|
|
@ -163,7 +181,7 @@ impl TurnStats {
|
|||
last_cache_read_input_tokens, last_cache_creation_input_tokens,
|
||||
tool_call_count, tool_call_breakdown_json,
|
||||
open_threads_count, open_reminders_count,
|
||||
result_kind, note
|
||||
result_kind, note, session_id
|
||||
) VALUES (
|
||||
?1, ?2, ?3, ?4, ?5,
|
||||
?6, ?7,
|
||||
|
|
@ -172,7 +190,7 @@ impl TurnStats {
|
|||
?12, ?13,
|
||||
?14, ?15,
|
||||
?16, ?17,
|
||||
?18, ?19
|
||||
?18, ?19, ?20
|
||||
)",
|
||||
params![
|
||||
row.started_at,
|
||||
|
|
@ -196,6 +214,7 @@ impl TurnStats {
|
|||
.map(|n| i64::try_from(n).unwrap_or(i64::MAX)),
|
||||
row.result_kind,
|
||||
row.note,
|
||||
row.session_id,
|
||||
],
|
||||
);
|
||||
if let Err(e) = res {
|
||||
|
|
@ -203,6 +222,29 @@ impl TurnStats {
|
|||
}
|
||||
}
|
||||
|
||||
/// Mint a new session row at fresh-session start, returning its `id`
|
||||
/// for stamping onto this session's `turn_stats` rows. Best-effort —
|
||||
/// returns `None` (and logs) on any sqlite error, so a hiccup degrades
|
||||
/// to NULL `session_id` rows rather than crashing the turn loop.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the internal lock is poisoned.
|
||||
#[must_use]
|
||||
pub fn start_session(&self, started_at: i64, model: &str) -> Option<i64> {
|
||||
let conn = self.inner.lock().unwrap();
|
||||
match conn.execute(
|
||||
"INSERT INTO sessions (started_at, model) VALUES (?1, ?2)",
|
||||
params![started_at, model],
|
||||
) {
|
||||
Ok(_) => Some(conn.last_insert_rowid()),
|
||||
Err(e) => {
|
||||
tracing::warn!(error = ?e, "turn_stats: start_session insert failed");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Token counts from the most recently inserted row, if any.
|
||||
/// Returns `(ctx, cost)` — both backfill `Bus` on startup so the
|
||||
/// per-agent web UI's ctx + cost badges paint with real numbers on
|
||||
|
|
|
|||
Loading…
Reference in a new issue