mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: mem slash commands (#11569)
Add 2 slash commands for memories: * `/m_drop` delete all the memories * `/m_update` update the memories with phase 1 and 2
This commit is contained in:
committed by
GitHub
Unverified
parent
4027f1f1a4
commit
a0dab25c68
@@ -3033,6 +3033,12 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
|
||||
Op::Compact => {
|
||||
handlers::compact(&sess, sub.id.clone()).await;
|
||||
}
|
||||
Op::DropMemories => {
|
||||
handlers::drop_memories(&sess, &config, sub.id.clone()).await;
|
||||
}
|
||||
Op::UpdateMemories => {
|
||||
handlers::update_memories(&sess, &config, sub.id.clone()).await;
|
||||
}
|
||||
Op::ThreadRollback { num_turns } => {
|
||||
handlers::thread_rollback(&sess, sub.id.clone(), num_turns).await;
|
||||
}
|
||||
@@ -3582,6 +3588,68 @@ mod handlers {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn drop_memories(sess: &Arc<Session>, config: &Arc<Config>, sub_id: String) {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
if let Some(state_db) = sess.services.state_db.as_deref() {
|
||||
if let Err(err) = state_db.clear_memory_data().await {
|
||||
errors.push(format!("failed clearing memory rows from state db: {err}"));
|
||||
}
|
||||
} else {
|
||||
errors.push("state db unavailable; memory rows were not cleared".to_string());
|
||||
}
|
||||
|
||||
let memory_root = crate::memories::memory_root(&config.codex_home);
|
||||
if let Err(err) = tokio::fs::remove_dir_all(&memory_root).await
|
||||
&& err.kind() != std::io::ErrorKind::NotFound
|
||||
{
|
||||
errors.push(format!(
|
||||
"failed removing memory directory {}: {err}",
|
||||
memory_root.display()
|
||||
));
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
sess.send_event_raw(Event {
|
||||
id: sub_id,
|
||||
msg: EventMsg::Warning(WarningEvent {
|
||||
message: format!(
|
||||
"Dropped memories at {} and cleared memory rows from state db.",
|
||||
memory_root.display()
|
||||
),
|
||||
}),
|
||||
})
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
sess.send_event_raw(Event {
|
||||
id: sub_id,
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: format!("Memory drop completed with errors: {}", errors.join("; ")),
|
||||
codex_error_info: Some(CodexErrorInfo::Other),
|
||||
}),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_memories(sess: &Arc<Session>, config: &Arc<Config>, sub_id: String) {
|
||||
let session_source = {
|
||||
let state = sess.state.lock().await;
|
||||
state.session_configuration.session_source.clone()
|
||||
};
|
||||
|
||||
crate::memories::start_memories_startup_task(sess, Arc::clone(config), &session_source);
|
||||
|
||||
sess.send_event_raw(Event {
|
||||
id: sub_id.clone(),
|
||||
msg: EventMsg::Warning(WarningEvent {
|
||||
message: "Memory update triggered.".to_string(),
|
||||
}),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn thread_rollback(sess: &Arc<Session>, sub_id: String, num_turns: u32) {
|
||||
if num_turns == 0 {
|
||||
sess.send_event_raw(Event {
|
||||
|
||||
@@ -290,6 +290,12 @@ pub enum Op {
|
||||
/// to generate a summary which will be returned as an AgentMessage event.
|
||||
Compact,
|
||||
|
||||
/// Drop all persisted memory artifacts and memory-tracking DB rows.
|
||||
DropMemories,
|
||||
|
||||
/// Trigger a single pass of the startup memory pipeline.
|
||||
UpdateMemories,
|
||||
|
||||
/// Set a user-facing thread name in the persisted rollout metadata.
|
||||
/// This is a local-only operation handled by codex-core; it does not
|
||||
/// involve the model.
|
||||
|
||||
@@ -18,6 +18,32 @@ const MEMORY_CONSOLIDATION_JOB_KEY: &str = "global";
|
||||
const DEFAULT_RETRY_REMAINING: i64 = 3;
|
||||
|
||||
impl StateRuntime {
|
||||
pub async fn clear_memory_data(&self) -> anyhow::Result<()> {
|
||||
let mut tx = self.pool.begin().await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
DELETE FROM stage1_outputs
|
||||
"#,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
DELETE FROM jobs
|
||||
WHERE kind = ? OR kind = ?
|
||||
"#,
|
||||
)
|
||||
.bind(JOB_KIND_MEMORY_STAGE1)
|
||||
.bind(JOB_KIND_MEMORY_CONSOLIDATE_GLOBAL)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn claim_stage1_jobs_for_startup(
|
||||
&self,
|
||||
current_thread_id: ThreadId,
|
||||
|
||||
@@ -3398,6 +3398,12 @@ impl ChatWidget {
|
||||
SlashCommand::Clean => {
|
||||
self.clean_background_terminals();
|
||||
}
|
||||
SlashCommand::MemoryDrop => {
|
||||
self.submit_op(Op::DropMemories);
|
||||
}
|
||||
SlashCommand::MemoryUpdate => {
|
||||
self.submit_op(Op::UpdateMemories);
|
||||
}
|
||||
SlashCommand::Mcp => {
|
||||
self.add_mcp_output();
|
||||
}
|
||||
|
||||
@@ -3380,6 +3380,24 @@ async fn slash_clean_submits_background_terminal_cleanup() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_memory_drop_submits_drop_memories_op() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.dispatch_command(SlashCommand::MemoryDrop);
|
||||
|
||||
assert_matches!(op_rx.try_recv(), Ok(Op::DropMemories));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_memory_update_submits_update_memories_op() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.dispatch_command(SlashCommand::MemoryUpdate);
|
||||
|
||||
assert_matches!(op_rx.try_recv(), Ok(Op::UpdateMemories));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_resume_opens_picker() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
@@ -46,6 +46,11 @@ pub enum SlashCommand {
|
||||
Clean,
|
||||
Personality,
|
||||
TestApproval,
|
||||
// Debugging commands.
|
||||
#[strum(serialize = "debug-m-drop")]
|
||||
MemoryDrop,
|
||||
#[strum(serialize = "debug-m-update")]
|
||||
MemoryUpdate,
|
||||
}
|
||||
|
||||
impl SlashCommand {
|
||||
@@ -70,6 +75,8 @@ impl SlashCommand {
|
||||
SlashCommand::Statusline => "configure which items appear in the status line",
|
||||
SlashCommand::Ps => "list background terminals",
|
||||
SlashCommand::Clean => "stop all background terminals",
|
||||
SlashCommand::MemoryDrop => "DO NOT USE",
|
||||
SlashCommand::MemoryUpdate => "DO NOT USE",
|
||||
SlashCommand::Model => "choose what model and reasoning effort to use",
|
||||
SlashCommand::Personality => "choose a communication style for Codex",
|
||||
SlashCommand::Plan => "switch to Plan mode",
|
||||
@@ -118,7 +125,9 @@ impl SlashCommand {
|
||||
| SlashCommand::Experimental
|
||||
| SlashCommand::Review
|
||||
| SlashCommand::Plan
|
||||
| SlashCommand::Logout => false,
|
||||
| SlashCommand::Logout
|
||||
| SlashCommand::MemoryDrop
|
||||
| SlashCommand::MemoryUpdate => false,
|
||||
SlashCommand::Diff
|
||||
| SlashCommand::Rename
|
||||
| SlashCommand::Mention
|
||||
|
||||
Reference in New Issue
Block a user