Deploy Poimen Memory K8s cluster with ArgoCD tracking (M2.2, M3.5-M3.7)
ci / markdown (push) Waiting to run
ci / markdown (push) Waiting to run
This commit is contained in:
@@ -17,10 +17,12 @@ time and produces the same rows.
|
||||
## Facts (inlined — no spec read needed)
|
||||
|
||||
```rust
|
||||
async fn upsert_node(&self, node: &MemoryNode, embedding: &[f32]) -> Result<()>;
|
||||
async fn upsert_node(&self, node: &MemoryNode) -> Result<()>;
|
||||
async fn upsert_vector(&self, sha: &Sha256Hash, kind: VectorKind, embedding: &[f32]) -> Result<()>;
|
||||
async fn insert_edges(&self, child: &Sha256Hash, parents: &[Sha256Hash]) -> Result<()>;
|
||||
async fn search(&self, q: &[f32], levels: &[Level], project: &ProjectId, k: usize)
|
||||
-> Result<Vec<ScoredNode>>;
|
||||
async fn search(&self, q: &[f32], kind: VectorKind, levels: &[Level],
|
||||
project: Scope, k: usize) -> Result<Vec<ScoredNode>>;
|
||||
async fn lookup_signature(&self, sig_sha: &str) -> Result<Option<SignatureHit>>;
|
||||
async fn parents_of(&self, sha: &Sha256Hash) -> Result<Vec<MemoryNode>>;
|
||||
async fn clear_project(&self, project: &ProjectId) -> Result<()>;
|
||||
```
|
||||
@@ -32,6 +34,23 @@ rows. Same for edges on the composite key.
|
||||
`search` orders by `embedding <=> $1` — cosine distance, matching the
|
||||
`vector_cosine_ops` index. Any other operator silently drops to a seq scan.
|
||||
|
||||
**`kind` must be a literal predicate in the SQL, not a bind parameter, and not a
|
||||
filter applied to results.** The indexes are partial (`WHERE kind = 'text'`), and
|
||||
the planner only uses a partial index when the query's predicate provably matches
|
||||
it. A `WHERE kind = $2` defeats that and silently degrades to a scan over every
|
||||
vector of both kinds — the same failure mode as the wrong opclass, and just as
|
||||
invisible.
|
||||
|
||||
**`Scope` is not a `ProjectId`.** Tool-failure lookups federate across projects
|
||||
because an `ERESOLVE` lesson is not project-specific, while ordinary standing-query
|
||||
memories stay scoped. `Scope::Project(id)` filters; `Scope::AllProjects` does not
|
||||
and lets project relevance act as a rank boost later instead of a hard filter.
|
||||
|
||||
`lookup_signature` is the exact-match tier: a primary-key hit on
|
||||
`failure_signature`, no vector involved. It is the cheapest and highest-precision
|
||||
answer the store can give, so it belongs in the repository rather than being
|
||||
assembled from a `search` call by a caller who does not know it exists.
|
||||
|
||||
Edges are inserted **after** both endpoints exist, or the foreign key rejects
|
||||
them. Rebuild therefore has two passes: all nodes, then all edges. This is not an
|
||||
optimisation; a single-pass insert fails on the first forward reference.
|
||||
@@ -44,12 +63,23 @@ rebuild — batch across nodes, not per node.
|
||||
1. `PgRepo::connect(url)` with a pool; run migrations on connect.
|
||||
2. Implement the five methods above.
|
||||
3. `upsert_many(nodes)` batching embedding calls at 32 and inserting with a
|
||||
multi-row statement.
|
||||
4. Two-pass write: nodes, then edges.
|
||||
multi-row statement. Batch across *both* vector kinds — a node with a symptom
|
||||
projection contributes two texts to the same batch, not two batches.
|
||||
4. Three-pass write: nodes, then vectors and signatures, then edges. Vectors and
|
||||
signatures carry foreign keys to nodes, so they cannot precede them, and edges
|
||||
still need both endpoints present.
|
||||
5. Separate query builders per `kind` so the literal predicate is guaranteed at
|
||||
compile time rather than by convention.
|
||||
5. `clear_project` deletes nodes for one project; edges cascade.
|
||||
6. Return `ScoredNode { node, distance }` — keep the raw distance, do not convert
|
||||
to a similarity score here. The reranker (M3.2) wants the ordering, and a
|
||||
lossy conversion hides ties.
|
||||
6. Return `ScoredNode { node, distance, matched_kind }` — keep the raw distance,
|
||||
do not convert to a similarity score here. The reranker (M3.2) wants the
|
||||
ordering, and a lossy conversion hides ties. `matched_kind` tells the caller
|
||||
whether the hit came from the memory text or its symptom projection, which is
|
||||
the difference between "this is about your topic" and "this explains your
|
||||
error".
|
||||
7. Exclude superseded nodes by default: `LEFT JOIN memory_supersede` on
|
||||
`old_sha`, filter where the join is null. An `include_superseded` flag exists
|
||||
for audit, off everywhere else.
|
||||
|
||||
## Acceptance
|
||||
|
||||
|
||||
Reference in New Issue
Block a user