track compacted turns separately in stats
This commit is contained in:
parent
fceab658f4
commit
5951758b35
3 changed files with 23 additions and 11 deletions
|
|
@ -220,7 +220,7 @@ async fn serve(
|
|||
// compaction so it shouldn't reach here, but if it
|
||||
// does we also skip the ack (safer to redeliver than
|
||||
// to lose the message).
|
||||
if matches!(outcome, turn::TurnOutcome::Ok) {
|
||||
if matches!(outcome, turn::TurnOutcome::Ok | turn::TurnOutcome::Compacted) {
|
||||
ack_turn(socket).await;
|
||||
}
|
||||
// Rate-limited: park until the quota resets, then requeue
|
||||
|
|
@ -458,6 +458,7 @@ fn build_row(
|
|||
};
|
||||
let (result_kind, note) = match outcome {
|
||||
turn::TurnOutcome::Ok => ("ok", None),
|
||||
turn::TurnOutcome::Compacted => ("compacted", None),
|
||||
turn::TurnOutcome::PromptTooLong => ("prompt_too_long", None),
|
||||
turn::TurnOutcome::RateLimited => ("rate_limited", None),
|
||||
turn::TurnOutcome::Failed(e) => ("failed", Some(format!("{e:#}"))),
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ async fn serve(
|
|||
// Ack only on a clean turn-end; Failed / RateLimited leave
|
||||
// the popped ids in-flight for the next boot's requeue.
|
||||
// Mirrors hive-ag3nt; see that loop for full rationale.
|
||||
if matches!(outcome, turn::TurnOutcome::Ok) {
|
||||
if matches!(outcome, turn::TurnOutcome::Ok | turn::TurnOutcome::Compacted) {
|
||||
ack_turn(socket).await;
|
||||
}
|
||||
// Rate-limited: park until the quota resets, then requeue
|
||||
|
|
@ -378,6 +378,7 @@ fn build_row(
|
|||
};
|
||||
let (result_kind, note) = match outcome {
|
||||
turn::TurnOutcome::Ok => ("ok", None),
|
||||
turn::TurnOutcome::Compacted => ("compacted", None),
|
||||
turn::TurnOutcome::PromptTooLong => ("prompt_too_long", None),
|
||||
turn::TurnOutcome::RateLimited => ("rate_limited", None),
|
||||
turn::TurnOutcome::Failed(e) => ("failed", Some(format!("{e:#}"))),
|
||||
|
|
|
|||
|
|
@ -168,6 +168,11 @@ pub async fn write_system_prompt(
|
|||
#[derive(Debug)]
|
||||
pub enum TurnOutcome {
|
||||
Ok,
|
||||
/// Turn completed and proactive context-size compaction fired afterwards.
|
||||
/// Treated like `Ok` for ack and failure-notification purposes; recorded
|
||||
/// as `result_kind = "compacted"` in turn stats so the stats page can
|
||||
/// distinguish normal turns from turns that triggered a compaction.
|
||||
Compacted,
|
||||
/// claude saw "Prompt is too long" — the session needs compacting.
|
||||
/// Run `compact_session()` then retry the same wake-up prompt.
|
||||
PromptTooLong,
|
||||
|
|
@ -265,9 +270,12 @@ pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutco
|
|||
// Proactive: a turn just completed on a still-healthy session. If its
|
||||
// context crossed the watermark, checkpoint + compact before a later
|
||||
// turn overflows into the reactive path. Best-effort — never changes
|
||||
// the outcome of the turn that already succeeded.
|
||||
// the outcome of the turn that already succeeded, but records it as
|
||||
// `Compacted` so turn stats can distinguish it from a plain `Ok`.
|
||||
if matches!(outcome, TurnOutcome::Ok) {
|
||||
maybe_checkpoint_and_compact(files, bus).await;
|
||||
if maybe_checkpoint_and_compact(files, bus).await {
|
||||
return TurnOutcome::Compacted;
|
||||
}
|
||||
}
|
||||
outcome
|
||||
}
|
||||
|
|
@ -276,17 +284,18 @@ pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutco
|
|||
/// has crossed the watermark, run one notes-checkpoint turn so the agent
|
||||
/// can persist durable state, then `/compact`. Best-effort: a failed
|
||||
/// checkpoint or compaction is logged + surfaced as a Note but never
|
||||
/// fails the turn that already succeeded.
|
||||
async fn maybe_checkpoint_and_compact(files: &TurnFiles, bus: &Bus) {
|
||||
/// fails the turn that already succeeded. Returns `true` if compaction
|
||||
/// was attempted (watermark crossed), `false` if skipped.
|
||||
async fn maybe_checkpoint_and_compact(files: &TurnFiles, bus: &Bus) -> bool {
|
||||
let watermark = compact_watermark_tokens(bus);
|
||||
if watermark == 0 {
|
||||
return; // proactive compaction disabled
|
||||
return false; // proactive compaction disabled
|
||||
}
|
||||
let Some(used) = bus.last_ctx_usage().map(|u| u.context_tokens()) else {
|
||||
return; // no usage reading yet — nothing to compare against
|
||||
return false; // no usage reading yet — nothing to compare against
|
||||
};
|
||||
if used < watermark {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
bus.emit(LiveEvent::Note {
|
||||
text: format!(
|
||||
|
|
@ -298,7 +307,7 @@ async fn maybe_checkpoint_and_compact(files: &TurnFiles, bus: &Bus) {
|
|||
// session is somehow already too far gone to run even this, fall
|
||||
// through to compaction anyway — the checkpoint is best-effort.
|
||||
match run_turn(CHECKPOINT_PROMPT, files, bus).await {
|
||||
TurnOutcome::Ok => {}
|
||||
TurnOutcome::Ok | TurnOutcome::Compacted => {}
|
||||
TurnOutcome::PromptTooLong => bus.emit(LiveEvent::Note {
|
||||
text: "checkpoint turn overflowed the window — compacting without it".into(),
|
||||
}),
|
||||
|
|
@ -315,6 +324,7 @@ async fn maybe_checkpoint_and_compact(files: &TurnFiles, bus: &Bus) {
|
|||
text: format!("/compact after checkpoint failed: {e:#}"),
|
||||
});
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Pre-turn auto-reset check. If context is large AND the prompt cache has
|
||||
|
|
@ -360,7 +370,7 @@ fn maybe_auto_reset(bus: &Bus) {
|
|||
/// agent and manager loops agree on outcome semantics.
|
||||
pub fn emit_turn_end(bus: &Bus, outcome: &TurnOutcome) {
|
||||
match outcome {
|
||||
TurnOutcome::Ok | TurnOutcome::PromptTooLong => {
|
||||
TurnOutcome::Ok | TurnOutcome::Compacted | TurnOutcome::PromptTooLong => {
|
||||
bus.emit(LiveEvent::TurnEnd {
|
||||
ok: true,
|
||||
note: None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue