v5.6.0 — Atlas embeddings endpoint, streamed media, and retrieval correctness fixes - #31
Merged
Merged
Conversation
AdrianCurtin
added a commit
that referenced
this pull request
Jul 26, 2026
…rectness fixes (#31) * v5.6.0 - Atlas embeddings endpoint, streamed media, and retrieval correctness fixes * Voyage AI corections * Update dependencies
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.
Adds MongoDB's Atlas Embedding and Reranking API as a Voyage endpoint, video embedding, and memory-safe streaming for local media — and corrects three retrieval defects found while validating that work against the live API.
The correctness fixes are the part worth reviewing closely. All three were invisible to a fully green mocked suite, which is also why this PR adds a live contract-test harness.
Embeddings
ai.mongodb.com.endpoint:names it explicitly, and a named endpoint that contradicts an explicitbase_urlis refused rather than silently reconciled.voyage-3.5,voyage-3.5-lite,voyage-code-2,voyage-multimodal-3.5. Models Atlas does not serve are refused at construction with a replacement named, and stay valid against Voyage's own API.voyage-3.5(breaking — see below).voyage-3is retired from Atlas, so the old default made an Atlas key fail at construction whenever no model was named.voyage-4-nanoturned out to be served by neither hosted endpoint — it is open-weight and meant to be self-hosted. Lumping it in with the Atlas-retired models told an Atlas user to "target Voyage's own API" for a model Voyage does not serve either. It now lives inSELF_HOSTED_ONLY_MODELS, is refused against both hosted hosts with a pointer to a self-hostedbase_url:orLocalHTTP, and remains usable through either.embed_videoonvoyage-multimodal-3.5, plusembed_videoandsupports_modality?on the base provider protocol so video is a declared capability rather than one adapter's method.to_jsoncosts roughly 2.4x the file size resident — raw bytes, the base64 copy, and the serialized document — enough to exhaust a small dyno on one moderate video.MediaFilereads only a 16-byte header;StreamingBodybase64-encodes into the socket in fixed chunks, with an exactContent-Lengthand a retry-safe rewind. Passing a URL keeps the SDK out of the transfer entirely.voyage-4-largeandvoyage-4-literaised on every call. Per-model supported widths replace the coarse Matryoshka gate.ftypbox no longer implies MP4 on its own, and Apple's audio-onlyM4Abrand is excluded — it previously let an audio file pass as video. Media is capped at the documented 20 MB, enforced by the adapter independently of the global limit.Vector search
kunder ACL.$vectorSearchapplied itslimitbefore the ACL$match,protectedFieldsredaction, pointer-field filtering, and any callerfilter. A scoped caller who could read 2 of the top 10 asked for 10 and received 2, while readable matches sat further down the ranking. The search now requests a wider candidate window, enforces, then trims tok. The window is raised only when something can actually drop rows, and HNSW width stays anchored tokso the wider window does not widen the ANN search.kand then multiplied again by the plain search's own window derivation:k: 10asked Atlas for 1,000 rows instead of 100, an explicitcandidate_limitdiverged between the two fusion paths, and anykabove 100 produced a branchkbeyond the plain search's ceiling and failed outright. Fusion depth and candidate window are now separate values, bounded byMAX_Kand Atlas's 10,000 ceiling respectively.$rankFusiontrimmed tokin a$limitthat runs after its ACL$match, reintroducing the same underfill. It now limits to the candidate window and trims once enforcement has run.parse.vector_search.searchandparse.vector_search.hybridcarry counts named for where they are measured, so an underfill is observable rather than silent. There is no cheap way to learn how many rows$vectorSearchemitted before the server-side$match, so no field claims to be that number.Model declarations
model:is enforced. It was recorded on:vectorproperties and never checked. Because models in a family usually share a width (voyage-3andvoyage-3.5are both 1024), swapping the registered provider's model silently mixed incomparable vectors into one index — no error, just degraded recall, repairable only by re-embedding.model:anddimensions:are now verified before any request, fail closed when a provider cannot report them, and are exposed as an explicit audit for boot or CI.similarity:is checked against the functions Atlas accepts. A property wider than an Atlas index allows must declaresearchable: false, which now makes it genuinely storage-only: excluded from field resolution, refused when named directly, and rejected byagent_searchableat class load.Breaking
The Voyage default model moves from
voyage-3tovoyage-3.5. Vectors from the two are not comparable. Code relying on the default must pinmodel: "voyage-3"explicitly to keep existing embeddings valid, or re-embed. A:vectorproperty that declaresmodel:is unaffected — the new binding audit catches the mismatch before any request rather than letting the two mix silently.Other behavior changes to note
:vectorproperty above the Atlas index cap withoutsearchable: falsenow fails at declaration, and a storage-only field fails at resolution rather than at Atlas. Intended, but not purely additive.#model_nameor#dimensionsagainst a property that declares them is now refused rather than skipped.voyage-4-nanocan no longer be constructed against a hosted endpoint. It never worked there; the failure has moved from an opaque provider 400 to a constructor error naming the fix.Testing
rake test:unitpasses.rake test:contractpasses against both an Atlas model API key and a Voyage key.rake test:contractis new: live provider contract tests pinning request routing, native dimensions, the Matryoshka ladder, accepted media, size limits, model availability, and response shape. It is excluded from bothrake testandrake test:unit, so no ordinary run becomes billable because a key happens to be exported.Refusal probes issue raw requests instead of going through the SDK — a local guard asserted against itself proves nothing about the contract it encodes. Infrastructure conditions are classified rather than guessed at: a bad credential fails (the operator must fix it), while rate limiting and 5xx skip as "contract not determined", since failing there would cry wolf on a low-quota key and passing would hide real drift.
The suite earned its keep immediately. The live runs are what established that the v4 family defaults to 1024, that WebM and QuickTime are rejected, that
voyage-3/voyage-3-litematch their declared widths, and thatvoyage-4-nanois absent from both hosted endpoints. Every one of those was wrong or unverified in the SDK while the mocked suite was fully green.For low-quota keys,
VOYAGE_CONTRACT_RPMpaces the suite per model and disables the provider's own retries, which otherwise spend the rate budget invisibly during backoff.Still outstanding: on a 3-requests-per-minute key, the Matryoshka-ladder, video, and WebM-rejection probes exhaust the budget and skip. All three facts are separately confirmed against Atlas; only the Voyage-side assertion needs more quota. Wiring the contract suite into a scheduled job still needs a CI secret and a schedule, and should be two jobs — Atlas credentials in one, Voyage in the other — kept out of PR CI.