mirror of
https://github.com/openai/codex.git
synced 2026-11-08 08:37:30 +00: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:
1 parent
4027f1f1a4
commit
a0dab25c68
6 files changed
+134
-1
No files matched your search
@@ -3033,6 +3033,12 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
|
|||||||
Op::Compact => {
|
Op::Compact => {
|
||||||
handlers::compact(&sess, sub.id.clone()).await;
|
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 } => {
|
Op::ThreadRollback { num_turns } => {
|
||||||
handlers::thread_rollback(&sess, sub.id.clone(), num_turns).await;
|
handlers::thread_rollback(&sess, sub.id.clone(), num_turns).await;
|
||||||
}
|
}
|
||||||
@@ -3582,6 +3588,68 @@ mod handlers {
|
|||||||
.await;
|
.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) {
|
pub async fn thread_rollback(sess: &Arc<Session>, sub_id: String, num_turns: u32) {
|
||||||
if num_turns == 0 {
|
if num_turns == 0 {
|
||||||
sess.send_event_raw(Event {
|
sess.send_event_raw(Event {
|
||||||
|
|||||||
@@ -290,6 +290,12 @@ pub enum Op {
|
|||||||
/// to generate a summary which will be returned as an AgentMessage event.
|
/// to generate a summary which will be returned as an AgentMessage event.
|
||||||
Compact,
|
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.
|
/// Set a user-facing thread name in the persisted rollout metadata.
|
||||||
/// This is a local-only operation handled by codex-core; it does not
|
/// This is a local-only operation handled by codex-core; it does not
|
||||||
/// involve the model.
|
/// involve the model.
|
||||||
|
|||||||
@@ -18,6 +18,32 @@ const MEMORY_CONSOLIDATION_JOB_KEY: &str = "global";
|
|||||||
const DEFAULT_RETRY_REMAINING: i64 = 3;
|
const DEFAULT_RETRY_REMAINING: i64 = 3;
|
||||||
|
|
||||||
impl StateRuntime {
|
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(
|
pub async fn claim_stage1_jobs_for_startup(
|
||||||
&self,
|
&self,
|
||||||
current_thread_id: ThreadId,
|
current_thread_id: ThreadId,
|
||||||
|
|||||||
@@ -3398,6 +3398,12 @@ impl ChatWidget {
|
|||||||
SlashCommand::Clean => {
|
SlashCommand::Clean => {
|
||||||
self.clean_background_terminals();
|
self.clean_background_terminals();
|
||||||
}
|
}
|
||||||
|
SlashCommand::MemoryDrop => {
|
||||||
|
self.submit_op(Op::DropMemories);
|
||||||
|
}
|
||||||
|
SlashCommand::MemoryUpdate => {
|
||||||
|
self.submit_op(Op::UpdateMemories);
|
||||||
|
}
|
||||||
SlashCommand::Mcp => {
|
SlashCommand::Mcp => {
|
||||||
self.add_mcp_output();
|
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]
|
#[tokio::test]
|
||||||
async fn slash_resume_opens_picker() {
|
async fn slash_resume_opens_picker() {
|
||||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ pub enum SlashCommand {
|
|||||||
Clean,
|
Clean,
|
||||||
Personality,
|
Personality,
|
||||||
TestApproval,
|
TestApproval,
|
||||||
|
// Debugging commands.
|
||||||
|
#[strum(serialize = "debug-m-drop")]
|
||||||
|
MemoryDrop,
|
||||||
|
#[strum(serialize = "debug-m-update")]
|
||||||
|
MemoryUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlashCommand {
|
impl SlashCommand {
|
||||||
@@ -70,6 +75,8 @@ impl SlashCommand {
|
|||||||
SlashCommand::Statusline => "configure which items appear in the status line",
|
SlashCommand::Statusline => "configure which items appear in the status line",
|
||||||
SlashCommand::Ps => "list background terminals",
|
SlashCommand::Ps => "list background terminals",
|
||||||
SlashCommand::Clean => "stop all 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::Model => "choose what model and reasoning effort to use",
|
||||||
SlashCommand::Personality => "choose a communication style for Codex",
|
SlashCommand::Personality => "choose a communication style for Codex",
|
||||||
SlashCommand::Plan => "switch to Plan mode",
|
SlashCommand::Plan => "switch to Plan mode",
|
||||||
@@ -118,7 +125,9 @@ impl SlashCommand {
|
|||||||
| SlashCommand::Experimental
|
| SlashCommand::Experimental
|
||||||
| SlashCommand::Review
|
| SlashCommand::Review
|
||||||
| SlashCommand::Plan
|
| SlashCommand::Plan
|
||||||
| SlashCommand::Logout => false,
|
| SlashCommand::Logout
|
||||||
|
| SlashCommand::MemoryDrop
|
||||||
|
| SlashCommand::MemoryUpdate => false,
|
||||||
SlashCommand::Diff
|
SlashCommand::Diff
|
||||||
| SlashCommand::Rename
|
| SlashCommand::Rename
|
||||||
| SlashCommand::Mention
|
| SlashCommand::Mention
|
||||||
|
|||||||
Reference in new issue
Block a user