fix(rag-worker): clear stale doc embeddings before upsert to prevent orphaned vectors - #701
Open
lakshayyy10 wants to merge 1 commit into
Open
Conversation
…orphaned vectors Point IDs are generated with crypto.randomUUID(), so any retry of a failed or partially-completed doc run upserts a full duplicate set of chunks alongside the old ones. Delete the doc's existing points (scoped by projectId + sourceId + docId) before upserting in all three ingestion pipelines, and reuse the same helper in the deletion pipeline. Fixes rowboatlabs#603
Author
|
Ready for review whenever you get a chance — happy to adjust the approach or add a worker-level test if you can point me at the preferred harness for this script. cc @ramnique |
brianlane
added a commit
to brianlane/newCoworker
that referenced
this pull request
Jul 22, 2026
…F, rag-worker) (#838) Bumps the Rowboat pin bb32686b -> f422019e (brianlane/rowboat newcoworker/upgrades-jul-2026), which cherry-picks three unmerged upstream community fixes onto our hardened fork branch: - rowboatlabs/rowboat#776: path traversal in /api/uploads/[fileId] (Rowboat :3000 is published through the tenant tunnel; the route has no auth, so traversal was reachable) - rowboatlabs/rowboat#547: SSRF guard on webhook + custom MCP server URLs (blocks private/internal IP targets like loopback services) - rowboatlabs/rowboat#701: rag-worker clears stale doc embeddings before upsert (KVM8 jobs-worker + qdrant) Upstream main itself has zero apps/rowboat commits since our pin base (all new work is the apps/x desktop app), so this is a cherry-pick bump, not an upstream merge. Also fixes the integration Mongo seed: the agent 'model' field was dropped in April (1de8587), and Rowboat's createAgent has no model fallback - every turn crashed with "Cannot read properties of undefined (reading 'startsWith')", so the kvm suites failed on main with the OLD pin too. Restoring the field (production deploy-client.sh seeds always set it) makes the suites pass again. Validated: agent-tool-seed-parity, test:integration:kvm2 and test: integration:kvm8 (real stacks built from the new SHA) all green. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #603
The three ingestion pipelines (file / text / URL) in
rag-worker.tsgenerate Qdrant point IDs withcrypto.randomUUID(), upsert the points, and then update the doc record in MongoDB. These are separate network calls with no cleanup between attempts.Because docs with status
pendingorerrorare re-polled, any retry of a failed or partially-completed run (e.g. Qdrant upsert succeeded but the MongoupdateByVersionfailed) re-embeds the doc and upserts a full duplicate set of chunks under fresh random IDs. The vectors from the previous attempt are never removed, so duplicates accumulate on every retry — bloating the collection and polluting RAG search results with duplicate/stale chunks.Fix
deleteDocEmbeddings(projectId, sourceId, docId)helper using the same filtered-delete pattern the worker already uses inrunDeletionPipeline.runDeletionPipelineto reuse the helper (removes the duplicated inline filter).Why delete-before-upsert rather than deterministic point IDs
Deterministic IDs (e.g. hashing
docId + chunk index) would make retries overwrite in place, but if a doc is later re-processed and yields fewer chunks, the leftover higher-index points would still linger. The scoped filtered delete handles every case (retry, re-process, content change) and reuses an existing pattern in the codebase.Testing notes
projectId+sourceId+docIdmust-clauses).esbuildparse check passes on the modified file.