Phase 28-36 body of work
Accumulated since a6f12e2 (Phase 21 Rust port + Phase 27 versioning):
- Phase 36: embed_semaphore on VectorState (permits=1) serializes
seed embed calls — prevents sidecar socket collisions under
concurrent /seed stress load
- Phase 31+: run_stress.ts 6-task diverse stress scaffolding;
run_e2e_rated.ts + orchestrator.ts tightening
- Catalog dedupe cleanup: 16 duplicate manifests removed; canonical
candidates.parquet (10.5MB -> 76KB) + placements.parquet (1.2MB ->
11KB) regenerated post-dedupe; fresh manifests for active datasets
- vectord: harness EvalSet refinements (+181), agent portfolio
rotation + ingest triggers (+158), autotune + rag adjustments
- catalogd/storaged/ingestd/mcp-server: misc tightening
- docs: Phase 28-36 PRD entries + DECISIONS ADR additions;
control-plane pivot banner added to top of docs/PRD.md (pointing
at docs/CONTROL_PLANE_PRD.md which lands in next commit)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
a6f12e2609
commit
5b1fcf6d27
@@ -13,6 +13,7 @@ use aibridge::client::{AiClient, EmbedRequest, GenerateRequest};
|
||||
use catalogd::registry::Registry as CatalogRegistry;
|
||||
use storaged::registry::BucketRegistry;
|
||||
use crate::{agent, autotune, chunker, embedding_cache, harness, hnsw, index_registry, jobs, lance_backend, playbook_memory, promotion, rag, refresh, search, store, supervisor, trial};
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VectorState {
|
||||
@@ -53,6 +54,9 @@ pub struct VectorState {
|
||||
/// and, when `use_playbook_memory` is set on /vectors/hybrid, boosts
|
||||
/// workers that were actually filled in semantically-similar past ops.
|
||||
pub playbook_memory: playbook_memory::PlaybookMemory,
|
||||
/// Serializes embed calls from seed_playbook_memory to avoid
|
||||
/// concurrent socket collisions with the Python sidecar.
|
||||
pub embed_semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
/// What the active-profile singleton records. Narrow — we don't need the
|
||||
@@ -893,7 +897,9 @@ async fn hybrid_search(
|
||||
system: None,
|
||||
temperature: Some(0.2),
|
||||
max_tokens: Some(512),
|
||||
think: None,
|
||||
// Hybrid's answer step — prose output over retrieved records,
|
||||
// no reasoning needed on the hot path.
|
||||
think: Some(false),
|
||||
}).await;
|
||||
|
||||
gen_resp.ok().map(|r| r.text.trim().to_string())
|
||||
@@ -1583,7 +1589,7 @@ async fn activate_profile(
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Json(ActivateReport {
|
||||
let report = ActivateReport {
|
||||
profile_id: profile.id,
|
||||
ollama_name: profile.ollama_name,
|
||||
indexes_warmed: warmed,
|
||||
@@ -1592,7 +1598,9 @@ async fn activate_profile(
|
||||
duration_secs: t0.elapsed().as_secs_f32(),
|
||||
model_preloaded,
|
||||
previous_profile: previous_slot.map(|s| s.profile_id),
|
||||
}))
|
||||
};
|
||||
|
||||
Ok(Json(report))
|
||||
}
|
||||
|
||||
/// Unload this profile's model and clear the active slot. No-op if the
|
||||
@@ -2217,6 +2225,10 @@ async fn seed_playbook_memory(
|
||||
State(state): State<VectorState>,
|
||||
Json(req): Json<SeedPlaybookRequest>,
|
||||
) -> impl IntoResponse {
|
||||
// Serialize embed calls to avoid concurrent socket collisions.
|
||||
let _permit = state.embed_semaphore.acquire().await.map_err(|e|
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("semaphore error: {e}")))?;
|
||||
|
||||
// Embed the entry through the same text shape `rebuild` uses so
|
||||
// similarity math is comparable across seed + real entries.
|
||||
let tmp_entry = playbook_memory::PlaybookEntry {
|
||||
@@ -2549,17 +2561,47 @@ async fn revise_playbook_memory(
|
||||
"operation must match 'fill: Role xN in City, ST' shape".into()));
|
||||
}
|
||||
|
||||
let ts = chrono::Utc::now().to_rfc3339();
|
||||
// Phase 27 — deterministic pid derived ONLY from content-shaped
|
||||
// inputs (parent_id + operation + approach + context + sorted
|
||||
// endorsed_names). Excluding wall-clock ts means two revise calls
|
||||
// with identical payloads produce the same pid, which is the
|
||||
// contract the docstring promises. On retry the caller gets back
|
||||
// the same id and the pre-flight idempotency check below short-
|
||||
// circuits to the existing entry instead of re-appending.
|
||||
let mut names_sorted = req.endorsed_names.clone();
|
||||
names_sorted.sort();
|
||||
use sha2::{Digest, Sha256};
|
||||
let mut h = Sha256::new();
|
||||
h.update(ts.as_bytes());
|
||||
h.update(b"|");
|
||||
h.update(req.parent_id.as_bytes());
|
||||
h.update(b"|");
|
||||
h.update(req.operation.as_bytes());
|
||||
h.update(req.parent_id.as_bytes()); h.update(b"|");
|
||||
h.update(req.operation.as_bytes()); h.update(b"|");
|
||||
h.update(req.approach.as_bytes()); h.update(b"|");
|
||||
h.update(req.context.as_bytes()); h.update(b"|");
|
||||
h.update(names_sorted.join(",").as_bytes());
|
||||
let bytes = h.finalize();
|
||||
let pid = format!("pb-rev-{}", bytes.iter().take(8).map(|b| format!("{b:02x}")).collect::<String>());
|
||||
|
||||
// Idempotency short-circuit — if this exact pid already exists in
|
||||
// memory (from a prior successful revise with the same content),
|
||||
// return it directly rather than re-appending or 400ing on the
|
||||
// superseded-parent rejection. Walks the parent's chain via
|
||||
// history() because the parent may itself have been superseded by
|
||||
// our prior successful call.
|
||||
let chain = state.playbook_memory.history(&req.parent_id).await;
|
||||
if let Some(existing) = chain.iter().find(|e| e.playbook_id == pid) {
|
||||
return Ok(Json(serde_json::json!({
|
||||
"outcome": {
|
||||
"parent_id": req.parent_id,
|
||||
"parent_version": existing.version.saturating_sub(1),
|
||||
"new_playbook_id": existing.playbook_id,
|
||||
"new_version": existing.version,
|
||||
"superseded_at": existing.superseded_at.clone().unwrap_or_default(),
|
||||
"idempotent_return": true,
|
||||
},
|
||||
"entries_after": state.playbook_memory.entry_count().await,
|
||||
})));
|
||||
}
|
||||
|
||||
let ts = chrono::Utc::now().to_rfc3339();
|
||||
let new_entry = playbook_memory::PlaybookEntry {
|
||||
playbook_id: pid.clone(),
|
||||
operation: req.operation,
|
||||
|
||||
Reference in New Issue
Block a user