mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
## Why Dual-write prepares v2 memories while v1 is selected. Once v2 is selected, running the v1 pipeline is no longer needed. ## What changed Run both memory pipelines only when `memories.dual_write` is enabled and `memories.version` selects v1. Run only v2 when it is selected, even if dual-write remains enabled. Update the configuration documentation and schema to describe this behavior. ## Testing Extend the startup test to cover both selected versions, checking model request counts, versioned extraction and consolidation state, and the absence of v1 rollout summaries when v2 is selected. GitOrigin-RevId: 1eb85bd67d7b32883b4ff086514a2acfb6b18f3a
96 lines
3.3 KiB
Rust
96 lines
3.3 KiB
Rust
use crate::ensure_layout;
|
|
use crate::extensions::seed_extension_instructions;
|
|
use crate::guard;
|
|
use crate::metrics::MEMORY_STARTUP;
|
|
use crate::phase1;
|
|
use crate::phase2;
|
|
use crate::runtime::MemoryStartupContext;
|
|
use codex_core::CodexThread;
|
|
use codex_core::ThreadManager;
|
|
use codex_core::config::Config;
|
|
use codex_features::Feature;
|
|
use codex_login::AuthManager;
|
|
use codex_protocol::MemoryVersion;
|
|
use codex_protocol::ThreadId;
|
|
use codex_protocol::models::PermissionProfile;
|
|
use codex_protocol::protocol::SessionSource;
|
|
use std::sync::Arc;
|
|
use tracing::warn;
|
|
|
|
/// Starts the asynchronous startup memory pipeline for an eligible root session.
|
|
///
|
|
/// The pipeline is skipped for ephemeral sessions, disabled feature flags, and
|
|
/// subagent sessions.
|
|
pub fn start_memories_startup_task(
|
|
thread_manager: Arc<ThreadManager>,
|
|
auth_manager: Arc<AuthManager>,
|
|
thread_id: ThreadId,
|
|
thread: Arc<CodexThread>,
|
|
config: Arc<Config>,
|
|
parent_permission_profile: PermissionProfile,
|
|
source: &SessionSource,
|
|
) {
|
|
if config.ephemeral
|
|
|| !config.features.enabled(Feature::MemoryTool)
|
|
|| source.is_non_root_agent()
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dual-write warms v2 while v1 is selected; it is no longer needed after cutover.
|
|
let versions = if config.memories.dual_write && config.memories.version == MemoryVersion::V1 {
|
|
vec![MemoryVersion::V1, MemoryVersion::V2]
|
|
} else {
|
|
vec![config.memories.version]
|
|
};
|
|
for version in versions {
|
|
let mut pipeline_config = config.as_ref().clone();
|
|
pipeline_config.memories.version = version;
|
|
let config = Arc::new(pipeline_config);
|
|
let auth_manager = Arc::clone(&auth_manager);
|
|
let parent_permission_profile = parent_permission_profile.clone();
|
|
let context = Arc::new(MemoryStartupContext::new(
|
|
Arc::clone(&thread_manager),
|
|
Arc::clone(&auth_manager),
|
|
thread_id,
|
|
Arc::clone(&thread),
|
|
config.as_ref(),
|
|
source.clone(),
|
|
));
|
|
tokio::spawn(async move {
|
|
if context.memory_store().await.is_none() {
|
|
warn!("state db unavailable for memories startup pipeline; skipping");
|
|
return;
|
|
}
|
|
let root = config
|
|
.codex_home
|
|
.join(config.memories.version.directory_name());
|
|
if let Err(err) = ensure_layout(&root).await {
|
|
warn!("failed preparing memories root: {err}");
|
|
return;
|
|
}
|
|
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
|
|
// done before the quota check.
|
|
phase1::prune(context.as_ref(), &config).await;
|
|
|
|
if !guard::rate_limits_ok(&auth_manager, &config).await {
|
|
context.counter(
|
|
MEMORY_STARTUP,
|
|
/*inc*/ 1,
|
|
&[("status", "skipped_rate_limit")],
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Run phase 1.
|
|
phase1::run(Arc::clone(&context), Arc::clone(&config)).await;
|
|
// Run phase 2.
|
|
phase2::run(context, config, parent_permission_profile).await;
|
|
});
|
|
}
|
|
}
|