loopd: compact after a full turn and retry SessionFull once
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
+24
-11
@@ -178,25 +178,38 @@ pub fn handle(stream: UnixStream, ctx: Arc<Context>) {
|
||||
}
|
||||
};
|
||||
|
||||
let mut emit = |event: &bxcore::TurnEvent| {
|
||||
let _ = write_frame(
|
||||
&mut stream,
|
||||
&Envelope {
|
||||
v: PROTOCOL_VERSION,
|
||||
id: request.id,
|
||||
r#final: false,
|
||||
msg: Message::TurnEvent(event.clone()),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
let outcome = run_turn(
|
||||
&mut session,
|
||||
&runtime,
|
||||
&turn.content,
|
||||
turn.speaker.as_deref(),
|
||||
&mut |event| {
|
||||
let _ = write_frame(
|
||||
&mut stream,
|
||||
&Envelope {
|
||||
v: PROTOCOL_VERSION,
|
||||
id: request.id,
|
||||
r#final: false,
|
||||
msg: Message::TurnEvent(event.clone()),
|
||||
},
|
||||
);
|
||||
},
|
||||
&mut emit,
|
||||
);
|
||||
let outcome = crate::compact::finish_turn(
|
||||
&mut session,
|
||||
&runtime,
|
||||
&*ctx.memory,
|
||||
&turn.content,
|
||||
turn.speaker.as_deref(),
|
||||
&mut emit,
|
||||
outcome,
|
||||
);
|
||||
|
||||
// 5. Release the session before the final frame, so the next turn does not find it busy.
|
||||
// Compaction ran inside the hold: a second turn on this session waits until the new
|
||||
// epoch is on disk (or compact failed and the epoch is unchanged).
|
||||
drop(held);
|
||||
|
||||
// A finished turn's records are for `memoryd` to index; best effort, since `memoryd` catches
|
||||
|
||||
@@ -11,6 +11,8 @@ use crate::memory::MemorySource;
|
||||
use crate::session::Session;
|
||||
use crate::tools::Registry;
|
||||
use crate::turn::is_context_full;
|
||||
use crate::turn::{Runtime, TurnError, TurnOutcome, run_turn};
|
||||
use bxcore::TurnEvent;
|
||||
|
||||
/// Why a summary call or a next-epoch write failed. Every variant's `Display` ends with the
|
||||
/// runbook pointer `session-compact-failed`.
|
||||
@@ -133,3 +135,52 @@ pub fn commit(
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// After `run_turn`: compact when the last `Usage` is due, or on `SessionFull` (then retry the
|
||||
/// user message once). Compact failure on a successful turn keeps the success; on `SessionFull`
|
||||
/// keeps `SessionFull`. Other errors skip compact.
|
||||
pub fn finish_turn(
|
||||
session: &mut Session,
|
||||
rt: &Runtime<'_>,
|
||||
memory: &dyn MemorySource,
|
||||
text: &str,
|
||||
speaker: Option<&str>,
|
||||
on_event: &mut dyn FnMut(&TurnEvent),
|
||||
outcome: Result<TurnOutcome, TurnError>,
|
||||
) -> Result<TurnOutcome, TurnError> {
|
||||
match outcome {
|
||||
Err(TurnError::SessionFull) => match compact_session(session, rt, memory) {
|
||||
Ok(()) => run_turn(session, rt, text, speaker, on_event),
|
||||
Err(e) => {
|
||||
log_compact(session, &e);
|
||||
Err(TurnError::SessionFull)
|
||||
}
|
||||
},
|
||||
Ok(done) => {
|
||||
if let Some(usage) = session.last_usage()
|
||||
&& due(&usage, rt.cfg.compact.threshold, rt.cfg.expect.n_ctx)
|
||||
&& let Err(e) = compact_session(session, rt, memory)
|
||||
{
|
||||
log_compact(session, &e);
|
||||
}
|
||||
Ok(done)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_session(
|
||||
session: &mut Session,
|
||||
rt: &Runtime<'_>,
|
||||
memory: &dyn MemorySource,
|
||||
) -> Result<(), CompactError> {
|
||||
let summary = summarise(rt.cfg, rt.client, session)?;
|
||||
commit(session, rt.cfg, rt.registry, memory, summary)
|
||||
}
|
||||
|
||||
fn log_compact(session: &Session, e: &CompactError) {
|
||||
eprintln!(
|
||||
"loopd: could not compact session {}: {e}",
|
||||
session.id().as_str()
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user