fix(#1982): migrate-stats tolerates pre-session_id dbs + skips failed agents
This commit is contained in:
parent
a94fb9955f
commit
c68706e899
1 changed files with 32 additions and 7 deletions
|
|
@ -86,14 +86,23 @@ pub async fn run(
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let mut total_points = 0usize;
|
let mut total_points = 0usize;
|
||||||
let mut hit_agents = 0usize;
|
let mut hit_agents = 0usize;
|
||||||
|
let mut failed = 0usize;
|
||||||
for agent in &agents {
|
for agent in &agents {
|
||||||
let db = Coordinator::agent_harness_dir(agent).join("hyperhive-turn-stats.sqlite");
|
let db = Coordinator::agent_harness_dir(agent).join("hyperhive-turn-stats.sqlite");
|
||||||
if !db.exists() {
|
if !db.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let id_prefix = format!("{swarm}-{hive}-{agent}");
|
let id_prefix = format!("{swarm}-{hive}-{agent}");
|
||||||
let pts = collect_agent_points(&db, &id_prefix)
|
// Skip + warn on a bad db (e.g. an old schema) rather than aborting
|
||||||
.with_context(|| format!("read turn_stats for {agent}"))?;
|
// the whole migration on the first failed agent.
|
||||||
|
let pts = match collect_agent_points(&db, &id_prefix) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{agent}: skipped — {e:#}");
|
||||||
|
failed += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
let n = pts.token.len() + pts.session.len();
|
let n = pts.token.len() + pts.session.len();
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
println!("{agent}: no token rows, skipping");
|
println!("{agent}: no token rows, skipping");
|
||||||
|
|
@ -123,11 +132,16 @@ pub async fn run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!(
|
println!(
|
||||||
"migrate-stats: {hit_agents} agent(s) with stats, {total_points} datapoints {}",
|
"migrate-stats: {hit_agents} agent(s) with stats, {total_points} datapoints {}{}",
|
||||||
if dry_run {
|
if dry_run {
|
||||||
"(dry-run, nothing sent)"
|
"(dry-run, nothing sent)"
|
||||||
} else {
|
} else {
|
||||||
"sent"
|
"sent"
|
||||||
|
},
|
||||||
|
if failed > 0 {
|
||||||
|
format!(" ({failed} agent(s) skipped on error)")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -139,12 +153,23 @@ pub async fn run(
|
||||||
fn collect_agent_points(db: &Path, id_prefix: &str) -> Result<AgentPoints> {
|
fn collect_agent_points(db: &Path, id_prefix: &str) -> Result<AgentPoints> {
|
||||||
let conn = Connection::open_with_flags(db, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)
|
let conn = Connection::open_with_flags(db, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)
|
||||||
.context("open stats db read-only")?;
|
.context("open stats db read-only")?;
|
||||||
// session_id last so the token column indices (3..=6) stay stable.
|
// Older dbs predate the `session_id` migration. Probe for the column and
|
||||||
let mut stmt = conn.prepare(
|
// fall back to a `NULL AS session_id` placeholder so the row shape (col 7)
|
||||||
|
// is identical either way (those rows get a synthetic session.id below).
|
||||||
|
// session_id is selected last so the token column indices stay fixed.
|
||||||
|
let has_session = conn
|
||||||
|
.prepare("SELECT session_id FROM turn_stats LIMIT 1")
|
||||||
|
.is_ok();
|
||||||
|
let sql = if has_session {
|
||||||
"SELECT started_at, ended_at, model, input_tokens, output_tokens, \
|
"SELECT started_at, ended_at, model, input_tokens, output_tokens, \
|
||||||
cache_read_input_tokens, cache_creation_input_tokens, session_id \
|
cache_read_input_tokens, cache_creation_input_tokens, session_id \
|
||||||
FROM turn_stats ORDER BY ended_at ASC",
|
FROM turn_stats ORDER BY ended_at ASC"
|
||||||
)?;
|
} else {
|
||||||
|
"SELECT started_at, ended_at, model, input_tokens, output_tokens, \
|
||||||
|
cache_read_input_tokens, cache_creation_input_tokens, NULL AS session_id \
|
||||||
|
FROM turn_stats ORDER BY ended_at ASC"
|
||||||
|
};
|
||||||
|
let mut stmt = conn.prepare(sql)?;
|
||||||
let rows = stmt.query_map([], |r| {
|
let rows = stmt.query_map([], |r| {
|
||||||
Ok((
|
Ok((
|
||||||
r.get::<_, i64>(0)?,
|
r.get::<_, i64>(0)?,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue