feat: add use_row_insert to add_texts; single insert for one-document calls by default - #17
Merged
Merged
Conversation
EnVector has two insert paths and the row one is the cheaper choice when a call carries only a few documents, which is what `add_texts` is for in a RAG pipeline: documents arriving a handful at a time into an index that already holds data. Until now the flag only reached the SDK by slipping through `**kwargs` — undiscoverable, untyped and undocumented. It is now a named argument, defaulting to the bulk path as before. EnVector applies the row path only to calls carrying fewer rows than the index dimension and silently inserts in bulk otherwise, so asking for it above that raises a UserWarning instead of leaving the caller believing it happened. The flag also survives the re-insert inside the `ids=` upsert arm. Which path is faster at which size is measured separately and documented in docs/insert-modes.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds docs/insert-modes.md with the boundary, a README pointer, and a correction to the add_texts docstring. Measured against an index already holding 8192 documents, six dimensions from 256 to 1536, calls of 1 to 32 documents, three repetitions each, every call's merge allowed to finish before the next: - The bulk path is flat in the number of documents. Across ten sizes each dimension stayed inside a 0.4s band. - The row path is never ahead on the thing callers notice. At one document it is within ~10% of bulk; at two it is 1.5x slower; at eight, 5x. Documents become searchable a fraction of a second after either call returns, so the same holds there. - It also writes about twice as much to storage per call, at every size and dimension. - What it does buy is a faster merge after a very small call: 2x at one document at dim 256, shrinking with dimension until at 1536 there is no size where it helps. That matters only when something waits for the merge, which is await_completion=True or an update/upsert soon after the insert. So the recommendation is to leave it off, with a per-dimension table for the case where the merge is on the critical path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The timings in insert-modes.md were taken with the client on the same machine as the server, which hides the one place the two paths differ by a lot. The bulk path uploads a block sized by the index dimension and not by the number of documents: 31.5 MB at dim 1024, to add one document or a thousand. The row path uploads 61.5 KB per document, a factor of 500 at one document. Adding transfer time to the measured timings moves the boundary with the link and barely with the dimension: bulk everywhere at 1 Gbps or on the same host, row up to 1-2 documents at 100 Mbps, 2-3 at 50, 9-12 at 10. Also answers the question that prompted this, which the page did not address: adding a few documents at a time does not leave the index fragmented. After 45 small calls on an index of 8192 documents, every dimension held three shards of 4096, 4096 and 402 - the small inserts are folded into the last partly-filled shard. A shard's fixed cost is paid once per 4096 documents, not per call, and the merged result is byte-identical on both paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Requested by the enVector team: their concern is what an insert sends over the network, more than how long it takes. The bulk path uploads one block sized by the index dimension no matter how many documents the call carries (31.5 MB at dim 1024, to add one document or a thousand); the row path uploads about 60 KB per document. So calls of fewer documents than the dimension now take the row path, and calls at or above it take bulk. The decision is made on the whole call, not per 4096-row encryption chunk as the SDK does it, so a large call whose last chunk happens to be short never sends that tail on the row path (~2 s per row server-side; a 900-row tail would be half an hour). WriteSettings.use_row_insert turns the default off for a store whose client sits next to the server and cares about latency; add_texts(use_row_insert=...) overrides per call either way. Asking for the row path explicitly at or above dim still warns; the default falling back to bulk there is the rule, so it does not. The cost is latency and it is documented, not hidden: about 2 s of server time per document at dim 1024, so 8 documents take 19 s where bulk took 4 s. Documents are searchable right after the call returns on both paths. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…page Findings from an independent review of the branch, and the user-facing text brought back to what the reader has to do. - docs/insert-modes.md is gone. It opened by calling bulk the default and row opt-in, the opposite of what the last commit made true, and the rest of it was a measurement report on the row path's cost — a working note, not user documentation. What a user needs is the rule and the switch, and those are in the README line, the add_texts docstring and WriteSettings. The numbers stay in the internal notes. - README and docstrings no longer carry measurements (31.5 MB, 2 s per row): they say the row path sends less and takes longer per document, and how to turn it off. - add_texts' docstring states that id-less rows in an ids= call are inserted by Index.upsert, which offers no choice of path, so use_row_insert governs only its re-insert of ids that matched no live row. It used to claim False forces bulk for the whole call. - The explicit-True warning is emitted before the insert runs, so it no longer says the rows "were inserted"; they "go on the bulk path instead". - Tests: the default is checked strictly above dim, not only at it, and use_row_insert is checked through add_documents, the API the README shows. Left as found: the review's first finding is that the default itself makes a call of hundreds of documents below dim take minutes rather than seconds. That is the traffic-first trade the enVector team asked for and it is stated in the PR; it is a decision for the reviewers, not a defect to patch here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
euphoria0-0
approved these changes
Sep 21, 2026
The line now states the behaviour the way the review put it — single insert below the index dimension, batch insert from there up — and keeps only the switch to always use batch insert. The SDK's own default is batch insert and needs use_row_insert=True to take single insert below dim; this integration passes that True itself, which is why the README describes the result rather than the flag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The previous commits compared the call's document count with the index dimension here and passed the SDK a decided bool, so that a large call's short last encryption chunk would never take the row path. That was this integration's own rule layered on the SDK's, and the ask was to behave the same as the SDK. Now the flag goes through as given: None means WriteSettings.use_row_insert (on), an explicit bool is passed as is, and the SDK applies its per-chunk rule from there. The warning for an explicit True at or above dim goes with it, since the wrapper no longer looks at dim. Whether the SDK's per-chunk decision is intended for a call that spans more than one chunk is a question for the SDK, raised separately. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…therwise add_texts picks the insert path per call when nothing is specified: a call with one document uses EnVector's single insert, a call with two or more uses batch insert. WriteSettings.use_row_insert (now Optional, default None) sets a store-wide value instead, and the add_texts argument overrides both. Docs and docstrings state the argument, what each value does, and the default rule; nothing else. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.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.
add_texts에use_row_insert인자 추가(English below.)
무엇이 달라지나
add_texts/add_documents에use_row_insert인자가 생긴다.True면 EnVector 의 singleinsert,
False면 batch insert 로 넣는다. 지금까지는**kwargs로만 전달됐다.None): 호출에 문서가 1개면 single insert, 2개 이상이면 batch insert.WriteSettings.use_row_insert로 store 전체의 기본을 정할 수 있고, 호출 인자가 그보다 우선한다.ids=를 준 호출에서 살아 있는 row 를 못 찾아 다시 insert 하는 경우에도 같은 규칙을 따른다.검증
python -m pytest tests -m "not integration"— 95 passed. 새 테스트: 기본 규칙(1개 single, 2개이상 batch), store 설정과 호출 override,
add_documents경유 전달,ids=재삽입 경로.pre-commit은 이 기계에서 못 돌렸다 (black hook 이 python3.11 을 요구, venv 는 3.10). CI 통과.Add
use_row_inserttoadd_textsWhat changes
add_texts/add_documentsgain ause_row_insertargument:Trueinserts with EnVector'ssingle insert,
Falsewith batch insert. Until now it only reached the SDK through**kwargs.None): a call with one document uses single insert, a call with two or more usesbatch insert.
WriteSettings.use_row_insertsets a store-wide default; the call argument overrides it.ids=call (for ids that matched no live row) follows the same rule.Limitationsline, the add-documents example and the docstrings.Verification
python -m pytest tests -m "not integration"— 95 passed. New tests: the default rule (onedocument single, two or more batch), the store setting and the per-call override, forwarding
through
add_documents, theids=re-insert arm.pre-commitcould not run on this machine (its black hook pins python3.11, this venv is 3.10);CI passes.
🤖 Generated with Claude Code