Deleted 31 completed task files: - M0.x: 8 tasks (cargo, domain types, recordsource, tokenizer, adapters, gate) - M1.x: 8 tasks (llm-chat, standing-query, prompt template, parser, loop, log, e2e, gate) - M3.x: 4 tasks (l2-synthesis, rerank, mem-query, gate) - M3.5.x: 8 tasks (http-server, ingest, query, federation, skills, projects, rate-limiting, gate) - M3.6.1: DocCorpusSource (heading-boundary chunking) - M4.1-2: skill-draft, derived-filter Updated INDEX.md: - Removed M0 & M1 phase sections (archived in git history) - Updated progress table: 65 active tasks (42✅ + 2🟡 + 21⬜) - Updated status: M0/M1 complete, M3/M3.5 gates passing, M4.1-2 done - Noted M3.5.10 JWT auth implementation complete (awaiting image rollout) - Cleaned up broken links to deleted task files Total test count: 239 passing, 2 ignored (up from 196 at M3.4) Ready for M4.3 gate composition, M5 post-training, M7 source connectors.
7.7 KiB
7.7 KiB
Implementation Notes: Query Optimization Engine
Status
✅ Design Phase COMPLETE
- Query Optimizer (query_optimizer.rs) — READY
- Hybrid Query Worker (hybrid_query_worker.rs) — STUB (needs API integration)
- Design Documentation (QUERY_OPTIMIZATION_ENGINE.md) — COMPLETE
⚠️ API Integration Notes (for Phase 2)
VectorStore API Corrections
Current Methods (Confirmed)
The actual VectorStore has level-based search methods:
// NOT available:
vector_store.search(&embedding, project, limit, None)
vector_store.search_with_ids(&embedding, project, limit, chunk_ids)
// ACTUALLY available:
vector_store.search_l1(project, &embedding, limit)
vector_store.search_l2(project, &embedding, limit)
vector_store.search_l3(project, &embedding, limit)
Updated Semantic Retrieval
async fn retrieve_semantic(
&self,
project: &str,
query_ctx: &QueryContext,
limit: i64,
) -> Result<Vec<(String, f32)>> {
let embedding = query_ctx
.embedding
.as_ref()
.ok_or_else(|| anyhow::anyhow!("no embedding"))?;
// Use L1 (most specific level)
let results = self.vector_store.search_l1(project, embedding, limit).await?;
// Convert to (id, score) tuples
let scored: Vec<(String, f32)> = results
.into_iter()
.map(|r| (r.id, r.score))
.collect();
Ok(scored)
}
OpenSearchClient API Corrections
Issue: Method Visibility
The lexical_search method in opensearch_client.rs is private:
// NOT public (private):
async fn lexical_search(...)
// NEEDED:
pub async fn lexical_search(...)
Fix
Make the method public:
// In crates/mem-cli/src/opensearch_client.rs
pub async fn lexical_search(
&self,
query: &str,
limit: usize,
jwt_token: &str,
) -> Result<Vec<(String, f32, String, String, Vec<String>)>>
Simplified Phase 2 Implementation
For Phase 2, instead of modifying http_server.rs extensively, create a wrapper:
/// In crates/mem-cli/src/http_server.rs
async fn query_handler(...) -> HttpResponse {
let (claims, token) = match validate_auth(&req, &state).await {
Ok(c) => c,
Err(e) => return e,
};
// ... existing checks ...
// Try hybrid if available
#[cfg(feature = "hybrid_search")]
{
match state.hybrid_query_worker.query(
&project,
&question,
limit,
&token,
).await {
Ok(response) => return HttpResponse::Ok().json(response),
Err(e) => {
tracing::warn!("Hybrid query failed: {}, falling back", e);
}
}
}
// Fallback: existing semantic search
match state.query_worker.query(&project, &question, Some(limit)).await {
Ok(results) => HttpResponse::Ok().json(json!({
"query": question,
"project": project,
"results": results,
"method": "semantic_only" // Indicate fallback
})),
Err(e) => {
tracing::error!("Query failed: {}", e);
HttpResponse::InternalServerError().json(json!({"error": "query_failed"}))
}
}
}
Build Status
Current Issues (Non-Blocking Design)
-
hybrid_query_worker.rs uses placeholder VectorStore API
- Fix: Use
search_l1()instead ofsearch() - Status: TRIVIAL (rename methods)
- Fix: Use
-
OpenSearchClient::lexical_search is private
- Fix: Remove
async fn, change topub async fn - Status: TRIVIAL (add
pub)
- Fix: Remove
-
Embedding type mismatch in line 89
- Fix: Use correct return type from embeddings crate
- Status: TRIVIAL (type annotation)
Estimated Fix Time
15-20 minutes to update API calls and make methods public.
No Design Changes Needed
All architectural decisions are sound:
- ✅ QueryOptimizer (6-stage pipeline) — No API dependency
- ✅ RRF Fusion algorithm — No API dependency
- ✅ Search strategy routing — No API dependency
- ⚠️ HybridQueryWorker — Needs VectorStore + OpenSearchClient API fixes
Phase 2 Checklist (1 Week)
Day 1-2: Fix Compilation
- Update
hybrid_query_worker.rsto use actual VectorStore API - Make
OpenSearchClient::lexical_searchpublic - Fix type mismatches in embedding handling
cargo checkpasses without errors
Day 3-4: Integration
- Update
/memory/queryhandler to attempt hybrid search - Add fallback strategy (hybrid → semantic → error)
- Forward JWT token through query pipeline
- Update response format to include metrics + score breakdown
Day 5: Testing
- Write 10+ integration tests
- Test fallback scenarios (OpenSearch unavailable)
- Measure latency (hybrid vs semantic)
- Verify score breakdown accuracy
Day 6-7: Buffer + Deployment
- Performance profiling
- Documentation updates
- Deploy to staging
- Manual E2E testing
Reference: Actual API Signatures
VectorStore (from mem-store)
pub async fn search_l1(
&self,
project: &str,
embedding: &[f32],
limit: i64,
) -> Result<Vec<SearchResult>>;
pub struct SearchResult {
pub id: String,
pub score: f32,
pub item: ChunkWithMetadata,
}
pub struct ChunkWithMetadata {
pub id: String,
pub content: String,
pub source: String,
pub level: Option<String>,
pub breadcrumb: Option<Vec<String>>,
}
EmbeddingsClient (from mem-llm)
pub async fn embed(&self, text: &str) -> Result<Vec<f32>>;
// Returns: 384-dim (all-MiniLM) or 1536-dim (OpenAI)
OpenSearchClient (current, needs pub)
pub async fn lexical_search(
&self,
query: &str,
limit: usize,
jwt_token: &str,
) -> Result<Vec<(String, f32, String, String, Vec<String>)>>;
// (id, score, chunk, source, breadcrumb)
Code Diff Preview (Phase 2)
Fix 1: Make OpenSearch method public
- async fn lexical_search(
+ pub async fn lexical_search(
Fix 2: Update HybridQueryWorker to use real API
- let results = self.vector_store.search(&embedding, project, limit, None).await?;
+ let results = self.vector_store.search_l1(project, embedding, limit).await?;
- let scored: Vec<(String, f32)> = results
- .into_iter()
- .map(|(id, score, _)| (id, score))
- .collect();
+ let scored: Vec<(String, f32)> = results
+ .into_iter()
+ .map(|r| (r.id, r.score))
+ .collect();
Fix 3: Update http_server.rs to use HybridQueryWorker
- match state.query_worker.query(&project, &question, Some(limit)).await {
+ // Try hybrid first
+ match state.hybrid_query_worker.query(
+ &project,
+ &question,
+ limit,
+ &jwt_token,
+ ).await {
Ok(response) => return HttpResponse::Ok().json(response),
+ Err(_) => {
+ // Fallback to semantic
}
+ }
+
+ match state.query_worker.query(...).await {
Design Validation
✅ All design decisions validated:
| Component | Status | Notes |
|---|---|---|
| Query Optimizer (6-stage) | ✅ READY | No API dependency |
| RRF Algorithm | ✅ READY | No API dependency |
| Query Routing | ✅ READY | Pure logic |
| Cascading Strategy | ✅ READY | Uses existing APIs |
| Hybrid Strategy | ⚠️ STUB | Needs VectorStore API fix |
| Fallback Pattern | ✅ READY | Uses existing query_worker |
None of these require architectural changes.
Next: Week 2 Task
"Implement Phase 2: Hybrid Integration"
Time estimate: 3-4 days (15-20 min for compilation fixes + 2-3 days for integration + testing)
Deliverables:
- ✅
/memory/querynow attempts hybrid search - ✅ Score breakdown + metrics in response
- ✅ Fallback to semantic if OpenSearch unavailable
- ✅ 10+ integration tests
- ✅ Latency measurements (hybrid vs semantic)