Repository navigation
feat(api): retry indexing saved documents without duplicate chunks - #52
Merged
Merged
Conversation
Use saved document IDs for stable chunks, replace obsolete owned fragments after upsert, and return the saved ID when embedding fails. Add an offline recovery endpoint with local Chroma tests and a captured API transcript. Co-authored-by: OpenAI Codex <noreply@openai.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.
A document can now be indexed again with
POST /api/documents/{document_id}/indexafter its initial embedding fails. API-created documents use stable chunk IDs, so retrying replaces owned chunks and removes obsolete fragments after a successful upsert.Part of #3. Closes #51.
Verification
All commands ran with a cleared inherited environment using
env -i HOME="$HOME" PATH="$PATH". This worktree has no root or backend.env; none was read or changed. Python commands selectedUV_PYTHON=3.13and the tests used fixture Slack credentials andDATABASE_URL=sqlite:///.tmp-tests.db.UV_PYTHON=3.13 MISE_YES=1 mise run installpassed for backend, frontend and runtime. Generated lock-file churn was discarded; dependency declarations and lock files are unchanged.UV_PYTHON=3.13 UV_FROZEN=1 MISE_YES=1 SLACK_BOT_TOKEN=xoxb-fixture-only SLACK_SIGNING_SECRET=fixture-only DATABASE_URL=sqlite:///.tmp-tests.db PYTEST_ADDOPTS=-rP mise run test-backendpassed, 209 tests including 13 new cases, with 928 existing-library and fixture warnings. The new cases use temporary SQLite and real local Chroma with fixed embeddings.MISE_YES=1 mise run test-frontendpassed, 22 tests and 187 assertions.MISE_YES=1 mise run test-runtimepassed, 7 offline tests.UV_PYTHON=3.13 UV_FROZEN=1 MISE_YES=1 PYTHONPATH="$PWD/.tmp/offline:$PWD/backend" SLACK_BOT_TOKEN=xoxb-fixture-only SLACK_SIGNING_SECRET=fixture-only DATABASE_URL="sqlite:///$PWD/.tmp/offline/proof.db" mise run dev-backendreachedApplication startup complete, served the recorded curl requests and was stopped with Ctrl-C, reachingApplication shutdown complete. The localsitecustomize.pyhook mocked Slack authentication, blocked Slack API calls and Socket Mode startup, and initialised the existing vector-store singleton with local Chroma and fixed embeddings. A marker file injected an embedding outage for the first upload, then was removed before retrying.git diff --checkpassed. No migration is needed.fa34ac3, including tests and all three container builds, CodeQL and Socket Security.Proof
Committed test output, fixture retrieval transcript and local curl transcript. Capture methods (a) and (c) succeeded. No capture method was blocked. This slice changes the API and retrieval indexing; there is no dashboard UI change to capture.
The API transcript shows an empty retrieval result, an upload returning HTTP 503 with saved document ID 1, the saved content remaining readable, two successful index retries and exactly one retrieved chunk at raw squared L2 distance 1.0. The list still contains one document. The missing-ID request returns HTTP 404. The fixture test independently checks one vector chunk after two retries. Fixed-vector distances demonstrate recovery and identity; they do not measure production relevance.
New backend test names and suite result
Review notes
POST /api/documentsnow returns HTTP 503 withdetail.messageanddetail.document_idwhen the database save succeeds but indexing fails. Use the index endpoint with that saved ID, rather than posting another copy. Existing successful creation responses are unchanged. Embedding work runs in the shared thread pool.Retrying does not modify database records. It rejects invalid legacy fields before indexing, preserves other document IDs even when titles and sources match, and leaves Slack chunks and legacy chunks without document ownership untouched. Legacy duplicates can remain and require a deliberate collection rebuild. File indexing without a saved document ID retains its existing behaviour.
Database storage, chunk upsert and obsolete-chunk deletion are separate operations. Failed cleanup is reported as HTTP 503 and can leave a mix of old and new chunks until retry succeeds. Upsert is not a cross-store transaction. Changing the embedding model or vector dimensions still requires the existing collection migration or rebuild process.
The management API retains its existing access-control requirements. This endpoint calls the configured embedding provider in a real deployment. No live Slack connection, real token, remote database, model request or paid embedding call was used for verification. Live embedding-provider behaviour and production dataset performance remain unverified; a live Slack workspace is not needed for this slice. The parent issue remains open, and #51 closes only when this PR merges.