Skip to content

Commit 7f5bfa5

Browse files
committed
fix: Preserve legacy embedding on rollback
1 parent c48feea commit 7f5bfa5

5 files changed

Lines changed: 59 additions & 13 deletions

File tree

src/daemon/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,23 @@ fn config_from_acquire(request: &AcquireProjectRequest) -> Result<MemoryConfig>
11931193
crate::model::embedding_config_for_profile(&active.profile_id, config.embedding())?;
11941194
config.set_embedding(persisted);
11951195
} else {
1196+
let store = crate::embedding_generation::ModelSwitchStore::load(
1197+
&config.model_switch_path(),
1198+
config.project_id(),
1199+
)?;
1200+
if let Some(embedding) = store
1201+
.current
1202+
.iter()
1203+
.chain(store.history.iter().rev())
1204+
.find(|job| {
1205+
job.phase == crate::embedding_generation::SwitchPhase::Succeeded
1206+
&& job.target_generation_id == active.generation_id
1207+
&& job.target_profile_id == active.profile_id
1208+
})
1209+
.and_then(|job| job.target_embedding.clone())
1210+
{
1211+
config.set_embedding(embedding);
1212+
}
11961213
anyhow::ensure!(
11971214
active.profile_fingerprint == config.embedding_profile_fingerprint()?,
11981215
"requested custom embedding does not match the persisted active profile"

src/embedding_generation.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ pub(crate) struct ModelSwitchJournal {
280280
pub source_profile_id: String,
281281
#[serde(default)]
282282
pub source_embedding: Option<EmbeddingConfig>,
283+
#[serde(default)]
284+
pub target_embedding: Option<EmbeddingConfig>,
283285
pub target_generation_id: String,
284286
pub target_profile_id: String,
285287
pub phase: SwitchPhase,
@@ -442,6 +444,7 @@ mod tests {
442444
source_generation_id: "legacy".to_string(),
443445
source_profile_id: "profile-a".to_string(),
444446
source_embedding: Some(EmbeddingConfig::default()),
447+
target_embedding: None,
445448
target_generation_id: format!("gen_{id}"),
446449
target_profile_id: "profile-b".to_string(),
447450
phase: SwitchPhase::Succeeded,

src/engine/mod.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,32 @@ impl MemoryEngine {
101101
let state = MemoryState::load(&config.state_path())?;
102102
let document_index = DocumentIndexManifest::load(&config.document_index_path())?;
103103
let graph = GraphStore::load(&config.graph_state_path(), &config.graph_pending_path())?;
104+
let switch_store =
105+
ModelSwitchStore::load(&config.model_switch_path(), config.project_id())?;
104106
let persisted_active =
105107
ActiveEmbedding::load(&config.active_embedding_path(), config.project_id())?;
106-
if let Some(active) = &persisted_active
107-
&& active.profile_id != "legacy-custom"
108-
{
109-
let embedding =
110-
crate::model::embedding_config_for_profile(&active.profile_id, config.embedding())?;
111-
config.set_embedding(embedding);
108+
if let Some(active) = &persisted_active {
109+
if active.profile_id == "legacy-custom" {
110+
if let Some(embedding) = switch_store
111+
.current
112+
.iter()
113+
.chain(switch_store.history.iter().rev())
114+
.find(|job| {
115+
job.phase == crate::embedding_generation::SwitchPhase::Succeeded
116+
&& job.target_generation_id == active.generation_id
117+
&& job.target_profile_id == active.profile_id
118+
})
119+
.and_then(|job| job.target_embedding.clone())
120+
{
121+
config.set_embedding(embedding);
122+
}
123+
} else {
124+
let embedding = crate::model::embedding_config_for_profile(
125+
&active.profile_id,
126+
config.embedding(),
127+
)?;
128+
config.set_embedding(embedding);
129+
}
112130
}
113131
let embedder = LlamaCppEmbedder::load(config.embedding(), config.model_cache())?;
114132
let active_embedding = match persisted_active {
@@ -157,9 +175,6 @@ impl MemoryEngine {
157175
now_ms()?,
158176
)?
159177
};
160-
let switch_store =
161-
ModelSwitchStore::load(&config.model_switch_path(), config.project_id())?;
162-
163178
let mut engine = Self {
164179
config,
165180
collection,

src/engine/model_switch.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ impl MemoryEngine {
100100
});
101101
validate_generation_id(&target_generation_id)?;
102102
let direct_rollback = request.target_generation_id.is_some();
103+
let target_embedding = if direct_rollback && target_generation_id == "legacy" {
104+
self.legacy_snapshot(&request.target_profile_id)
105+
.and_then(|snapshot| snapshot.source_embedding.clone())
106+
} else {
107+
None
108+
};
103109
if direct_rollback {
104110
if target_generation_id == "legacy" {
105111
let snapshot = self
@@ -137,6 +143,7 @@ impl MemoryEngine {
137143
source_generation_id: self.active_embedding.generation_id.clone(),
138144
source_profile_id: self.active_embedding.profile_id.clone(),
139145
source_embedding: Some(self.config.embedding().clone()),
146+
target_embedding,
140147
target_generation_id,
141148
target_profile_id: request.target_profile_id.clone(),
142149
phase: if direct_rollback {
@@ -656,8 +663,12 @@ impl MemoryEngine {
656663
}
657664
if self.switch_target_config.is_none() {
658665
self.switch_target_config = Some(if job.target_generation_id == "legacy" {
659-
self.legacy_snapshot(&job.target_profile_id)
660-
.and_then(|snapshot| snapshot.source_embedding.clone())
666+
job.target_embedding
667+
.clone()
668+
.or_else(|| {
669+
self.legacy_snapshot(&job.target_profile_id)
670+
.and_then(|snapshot| snapshot.source_embedding.clone())
671+
})
661672
.ok_or_else(|| {
662673
anyhow!("retained legacy embedding configuration is unavailable")
663674
})?

src/storage/atomic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@ pub(crate) fn write_json_atomic<T: Serialize>(
3737
pub(crate) fn remove_file_durable(path: &Path) -> Result<()> {
3838
if path.exists() {
3939
fs::remove_file(path).with_context(|| format!("cannot remove {}", path.display()))?;
40-
sync_parent(path)?;
4140
}
41+
sync_parent(path)?;
4242
Ok(())
4343
}
4444

4545
pub(crate) fn remove_dir_all_durable(path: &Path) -> Result<()> {
4646
if path.exists() {
4747
fs::remove_dir_all(path).with_context(|| format!("cannot remove {}", path.display()))?;
48-
sync_parent(path)?;
4948
}
49+
sync_parent(path)?;
5050
Ok(())
5151
}
5252

0 commit comments

Comments
 (0)