mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
feat: seed ad-hoc memory extension instructions (#20606)
## Summary Ad-hoc memory notes are written under `memories/extensions/ad_hoc/`, but the consolidation agent only knows how to interpret an extension when the extension folder has an `instructions.md`. Seed those instructions from the memories write pipeline so an enabled memories startup creates the expected ad-hoc extension layout automatically. This also moves extension-specific write behavior behind a dedicated `memories/write/src/extensions/` module. `ad_hoc` owns the seeded instructions template, while the existing resource-retention cleanup lives in its own `prune` module so future memory extensions can add their own write-side setup without growing a flat helper file. ## Changes - Seed `memories/extensions/ad_hoc/instructions.md` during eligible memory startup without overwriting an existing file. - Store the ad-hoc instructions template under `memories/write/templates/extensions/ad_hoc/`, keeping ownership in `codex-memories-write`. - Split memory extension support into `extensions::ad_hoc` and `extensions::prune`. - Keep the existing old-resource pruning behavior unchanged. ## Verification - `cargo test -p codex-memories-write` - `bazel build //codex-rs/memories/write:write` --------- Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
70fc55b8f3
commit
ff27d01676
7 files changed
+83
-1
No files matched your search
@@ -0,0 +1,28 @@
|
|||||||
|
use crate::memory_extensions_root;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub(super) const INSTRUCTIONS: &str =
|
||||||
|
include_str!("../../templates/extensions/ad_hoc/instructions.md");
|
||||||
|
|
||||||
|
pub(super) async fn seed_instructions(memory_root: &Path) -> std::io::Result<()> {
|
||||||
|
let extension_root = memory_extensions_root(memory_root).join("ad_hoc");
|
||||||
|
let instructions_path = extension_root.join("instructions.md");
|
||||||
|
|
||||||
|
tokio::fs::create_dir_all(&extension_root).await?;
|
||||||
|
match tokio::fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&instructions_path)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(mut file) => {
|
||||||
|
tokio::io::AsyncWriteExt::write_all(&mut file, INSTRUCTIONS.as_bytes()).await
|
||||||
|
}
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "ad_hoc_tests.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::memory_extensions_root;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn seeds_instructions_without_overwriting_existing_file() {
|
||||||
|
let codex_home = TempDir::new().expect("create temp codex home");
|
||||||
|
let memory_root = codex_home.path().join("memories");
|
||||||
|
let instructions_path = memory_extensions_root(&memory_root).join("ad_hoc/instructions.md");
|
||||||
|
|
||||||
|
seed_instructions(&memory_root)
|
||||||
|
.await
|
||||||
|
.expect("seed ad-hoc instructions");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tokio::fs::read_to_string(&instructions_path)
|
||||||
|
.await
|
||||||
|
.expect("read seeded ad-hoc instructions"),
|
||||||
|
INSTRUCTIONS
|
||||||
|
);
|
||||||
|
|
||||||
|
tokio::fs::write(&instructions_path, "custom instructions")
|
||||||
|
.await
|
||||||
|
.expect("write custom instructions");
|
||||||
|
seed_instructions(&memory_root)
|
||||||
|
.await
|
||||||
|
.expect("seed ad-hoc instructions again");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tokio::fs::read_to_string(&instructions_path)
|
||||||
|
.await
|
||||||
|
.expect("read custom ad-hoc instructions"),
|
||||||
|
"custom instructions"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
mod ad_hoc;
|
||||||
|
mod prune;
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub(crate) async fn seed_extension_instructions(memory_root: &Path) -> std::io::Result<()> {
|
||||||
|
ad_hoc::seed_instructions(memory_root).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use prune::prune_old_extension_resources;
|
||||||
+1
-1
@@ -96,5 +96,5 @@ fn resource_timestamp(file_name: &str) -> Option<DateTime<Utc>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "extensions_tests.rs"]
|
#[path = "prune_tests.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
+1
@@ -1,4 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::memory_extensions_root;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
use crate::extensions::seed_extension_instructions;
|
||||||
use crate::guard;
|
use crate::guard;
|
||||||
|
use crate::memory_root;
|
||||||
use crate::metrics::MEMORY_STARTUP;
|
use crate::metrics::MEMORY_STARTUP;
|
||||||
use crate::phase1;
|
use crate::phase1;
|
||||||
use crate::phase2;
|
use crate::phase2;
|
||||||
@@ -47,6 +49,11 @@ pub fn start_memories_startup_task(
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
let root = memory_root(&config.codex_home);
|
||||||
|
if let Err(err) = seed_extension_instructions(&root).await {
|
||||||
|
warn!("failed seeding memory extension instructions: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
// Clean memories to make preserve DB size. This does not consume tokens so can be
|
// Clean memories to make preserve DB size. This does not consume tokens so can be
|
||||||
// done before the quota check.
|
// done before the quota check.
|
||||||
phase1::prune(context.as_ref(), &config).await;
|
phase1::prune(context.as_ref(), &config).await;
|
||||||
|
|||||||
Reference in new issue
Block a user