refactor(s3_vectors): embed search queries through the shared vector store executor - #39474
Merged
mateo-berri merged 2 commits intoSep 3, 2026
Conversation
…store executor S3 Vectors now subclasses BaseQueryEmbeddingVectorStoreConfig, so its query embedding runs through the Router executor with the request metadata instead of a private router lookup. embedding_model stays accepted as an alias of litellm_embedding_model. The router kwarg is gone from the search handler and every provider transform now that nothing but the executor fallback read it.
Contributor
Greptile SummaryThe PR routes S3 Vectors query embeddings through the shared vector-store executor so Router aliases, SDK fallback, and request metadata use the common path.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/llms/base_llm/vector_store/transformation.py | Simplifies the shared executor to route models served by the Router and otherwise fall back to the SDK while preserving request metadata. |
| litellm/llms/s3_vectors/vector_stores/transformation.py | Migrates S3 Vectors query embedding to the shared executor and retains the configured/default embedding-model behavior. |
| litellm/vector_stores/main.py | Constructs and forwards the metadata-aware embedding executor through vector-store search handling. |
| litellm/llms/custom_httpx/llm_http_handler.py | Removes direct Router plumbing from vector-store handlers in favor of the supplied shared embedding executor. |
| tests/test_litellm/llms/s3_vectors/vector_stores/test_s3_vectors_transformation.py | Removes the previously flagged redundant docstrings without weakening assertions and adds SDK-fallback regression coverage. |
| tests/router_unit_tests/test_router_embedding_integration.py | Covers Router selection and request metadata forwarding for shared vector-store embeddings. |
| tests/test_litellm/vector_stores/test_main.py | Verifies executor construction and propagation through the public vector-store search path. |
Reviews (2): Last reviewed commit: "fix(vector-store): embed through the SDK..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…ve the query embedding model The Router executor only routed a query embedding when the vector store carried extra embedding configuration, so a store registered with no embedding model at all always went to the Router and 500'd on the s3_vectors default text-embedding-3-small when no deployment served it. Route on whether the Router serves the model, which is the rule the executor had before, and keep the request metadata on the SDK fallback so the embedding stays attributed either way.
Contributor
Author
Merged
6 tasks
Contributor
Author
|
bugbot run |
Contributor
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 7f7e0d5. Configure here.
mateo-berri
enabled auto-merge
September 3, 2026 05:10
mateo-berri
merged commit Sep 3, 2026
66a3d24
into
litellm_internal_staging
115 of 117 checks passed
sowmy86
added a commit
to sowmy86/litellm
that referenced
this pull request
Sep 3, 2026
…hook fake handler The fake vector store search handler declared `embedding_executor` twice after BerriAI#39474 landed on staging, which is a syntax error under `ruff check --config ruff-tests.toml tests` and fails the required lint job for every PR targeting the branch. The handler is called by keyword, so keeping the first declaration is behavior-preserving. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEHLJtc7bYodrmfXSAcBvh
sowmy86
added a commit
to sowmy86/litellm
that referenced
this pull request
Sep 3, 2026
…hook fake handler The fake vector store search handler declared `embedding_executor` twice after BerriAI#39474 landed on staging, which is a syntax error under `ruff check --config ruff-tests.toml tests` and fails the required lint job for every PR targeting the branch. The handler is called by keyword, so keeping the first declaration is behavior-preserving.
7 tasks
6 tasks
pull Bot
pushed a commit
to TKaxv-7S/litellm
that referenced
this pull request
Sep 3, 2026
…B fake handler Two branches independently added embedding_executor to the same fake search handler in this file, BerriAI#39472 in the middle of the signature and BerriAI#39474 at the end. Neither conflicted with the other, so both edits merged and the function ended up declaring the parameter twice. Python rejects that at compile time, so the whole module fails to import and every test in the file is uncollectable, taking the logging_testing job down on staging. Keep the earlier of the two, which sits where the real handler declares the parameter.
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.
TLDR
Problem this solves:
routerkwarg that only S3 Vectors readHow it solves it:
BaseQueryEmbeddingVectorStoreConfigand embeds through the executorembedding_modelstays accepted as an alias oflitellm_embedding_modelrouterkwarg is gone from the handler and all provider transforms_embedding_router.pystays: the redis, qdrant, and valkey semantic caches still use itUser Flow
Before: a developer on a team key searches an S3 Vectors store whose embedding model is a Router alias; the search works, but its query embedding never shows up in the team's spend logs, or anyone's
qa-team-embeddings(OpenAItext-embedding-3-small) tomodel_listand starts the proxyingest_optionsnamingcustom_llm_provider: "s3_vectors", the bucket, the region, andembedding_model: "qa-team-embeddings"; 200 with avector_store_idlikelit6750-qa-vectors:litellm-index-39e9592a{"query": "What must be drained before flipping DNS?"}; 200 with the matching chunkvector_store_ids: ["<that id>"]and the same question; 200 with "The Osaka queue must be drained before flipping DNS."After: the same search embeds through the Router as the developer's team and key, so the query embedding shows up under the team's spend
qa-team-embeddings(OpenAItext-embedding-3-small) tomodel_listand starts the proxyingest_optionsnamingcustom_llm_provider: "s3_vectors", the bucket, the region, andembedding_model: "qa-team-embeddings"; 200 with avector_store_idlikelit6750-qa-vectors:litellm-index-39e9592a{"query": "What must be drained before flipping DNS?"}; 200 with the matching chunkvector_store_ids: ["<that id>"]and the same question; 200 with "The Osaka queue must be drained before flipping DNS."aembeddingrow forqa-team-embeddingscarrying the team id and the team key's hashRelevant issues
Follow-up to #34788 and #38936
Linear ticket
Resolves LIT-6750
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Shared setup: two proxy processes with two uvicorn workers each on one Postgres,
model_listcarryinggpt-5.4-mini(openai/gpt-5.4-mini) andqa-team-embeddings(openai/text-embedding-3-small),store_model_in_db: true, a real S3 Vectors bucketlit6750-qa-vectorsin eu-central-1, and a one-line documentlit6750-doc.txt("The Kyoto failover runbook: drain the Osaka queue before flipping DNS, then page the on-call owl."). Real OpenAI and AWS calls, no mocks. Search, chat, and rag/query answer from the document on both sides; the difference is in the last two spend-log cases: Before has no embedding row anywhere for the four query embeddings, After has oneaembeddingrow per direct search and per chat completion, all carrying the team id and the team key hash (rag/query bills its search into its ownaqueryrow by design, so it has no separate embedding row on either side)Before (9e8e486)
team + team key on p1 (port 34446)
curl -s http://127.0.0.1:34446/team/new -H 'Authorization: Bearer <master key>' -d '{"team_alias":"lit6750-team-before"}'curl -s http://127.0.0.1:34446/key/generate -H 'Authorization: Bearer <master key>' -d '{"team_id":"c05b3300-af28-4e8d-ab88-9af8de59205a","key_alias":"lit6750-key-before"}'ingest on p1 with the team key, embedding model = bare Router alias qa-team-embeddings
curl -s http://127.0.0.1:34446/v1/rag/ingest -H 'Authorization: Bearer <team key>' -F file=@lit6750-doc.txt -F 'request={"ingest_options":{"embedding":{"model":"qa-team-embeddings"},"vector_store":{"custom_llm_provider":"s3_vectors","vector_bucket_name":"lit6750-qa-vectors","aws_region_name":"eu-central-1","embedding_model":"qa-team-embeddings"}}}'chat completion with vector_store_ids on p2 (port 31642), then on p1 (port 34446), team key
curl -s http://127.0.0.1:31642/v1/chat/completions -H 'Authorization: Bearer <team key>' -d '{"model":"gpt-5.4-mini","vector_store_ids":["lit6750-qa-vectors:litellm-index-39e9592a"],"messages":[{"role":"user","content":"What must be drained before flipping DNS in the Kyoto failover runbook? Answer in one sentence."}]}'curl -s http://127.0.0.1:34446/v1/chat/completions -H 'Authorization: Bearer <team key>' -d '{"model":"gpt-5.4-mini","vector_store_ids":["lit6750-qa-vectors:litellm-index-39e9592a"],"messages":[{"role":"user","content":"What must be drained before flipping DNS in the Kyoto failover runbook? Answer in one sentence."}]}'direct search, POST /v1/vector_stores/{id}/search on p2 with the team key
curl -s http://127.0.0.1:31642/v1/vector_stores/lit6750-qa-vectors:litellm-index-39e9592a/search -H 'Authorization: Bearer <team key>' -d '{"query":"What must be drained before flipping DNS?"}'control: POST /v1/rag/query on p1 with the team key
curl -s http://127.0.0.1:34446/v1/rag/query -H 'Authorization: Bearer <team key>' -d '{"model":"gpt-5.4-mini","retrieval_config":{"vector_store_id":"lit6750-qa-vectors:litellm-index-39e9592a"},"messages":[{"role":"user","content":"What must be drained before flipping DNS in the Kyoto failover runbook? Answer in one sentence."}]}'spend attribution of the query embeddings: GET /spend/logs/ui on p2, filtered to the team
curl -s 'http://127.0.0.1:31642/spend/logs/ui?start_date=2026-09-03%2002:20:45&end_date=2026-09-04%2000:00:00&page_size=100&team_id=c05b3300-af28-4e8d-ab88-9af8de59205a' -H 'Authorization: Bearer <master key>'same window, every embedding row regardless of team (unattributed rows show up here)
curl -s 'http://127.0.0.1:31642/spend/logs/ui?start_date=2026-09-03%2002:20:45&end_date=2026-09-04%2000:00:00&page_size=200' -H 'Authorization: Bearer <master key>'After (7f7e0d5)
team + team key on p1 (port 34446)
curl -s http://127.0.0.1:34446/team/new -H 'Authorization: Bearer <master key>' -d '{"team_alias":"lit6750-team-after2"}'curl -s http://127.0.0.1:34446/key/generate -H 'Authorization: Bearer <master key>' -d '{"team_id":"ce42fe38-d3ef-44e6-b651-f3bbd5a70f29","key_alias":"lit6750-key-after2"}'ingest on p1 with the team key, embedding model = bare Router alias qa-team-embeddings
curl -s http://127.0.0.1:34446/v1/rag/ingest -H 'Authorization: Bearer <team key>' -F file=@lit6750-doc.txt -F 'request={"ingest_options":{"embedding":{"model":"qa-team-embeddings"},"vector_store":{"custom_llm_provider":"s3_vectors","vector_bucket_name":"lit6750-qa-vectors","aws_region_name":"eu-central-1","embedding_model":"qa-team-embeddings"}}}'chat completion with vector_store_ids on p2 (port 31642), then on p1 (port 34446), team key
curl -s http://127.0.0.1:31642/v1/chat/completions -H 'Authorization: Bearer <team key>' -d '{"model":"gpt-5.4-mini","vector_store_ids":["lit6750-qa-vectors:litellm-index-e4e42d8a"],"messages":[{"role":"user","content":"What must be drained before flipping DNS in the Kyoto failover runbook? Answer in one sentence."}]}'curl -s http://127.0.0.1:34446/v1/chat/completions -H 'Authorization: Bearer <team key>' -d '{"model":"gpt-5.4-mini","vector_store_ids":["lit6750-qa-vectors:litellm-index-e4e42d8a"],"messages":[{"role":"user","content":"What must be drained before flipping DNS in the Kyoto failover runbook? Answer in one sentence."}]}'direct search, POST /v1/vector_stores/{id}/search on p2 with the team key
curl -s http://127.0.0.1:31642/v1/vector_stores/lit6750-qa-vectors:litellm-index-e4e42d8a/search -H 'Authorization: Bearer <team key>' -d '{"query":"What must be drained before flipping DNS?"}'control: POST /v1/rag/query on p1 with the team key
curl -s http://127.0.0.1:34446/v1/rag/query -H 'Authorization: Bearer <team key>' -d '{"model":"gpt-5.4-mini","retrieval_config":{"vector_store_id":"lit6750-qa-vectors:litellm-index-e4e42d8a"},"messages":[{"role":"user","content":"What must be drained before flipping DNS in the Kyoto failover runbook? Answer in one sentence."}]}'spend attribution of the query embeddings: GET /spend/logs/ui on p2, filtered to the team
curl -s 'http://127.0.0.1:31642/spend/logs/ui?start_date=2026-09-03%2004:16:51&end_date=2026-09-04%2000:00:00&page_size=100&team_id=ce42fe38-d3ef-44e6-b651-f3bbd5a70f29' -H 'Authorization: Bearer <master key>'same window, every embedding row regardless of team (unattributed rows show up here)
curl -s 'http://127.0.0.1:31642/spend/logs/ui?start_date=2026-09-03%2004:16:51&end_date=2026-09-04%2000:00:00&page_size=200' -H 'Authorization: Bearer <master key>'Regression guard: a store registered with no embedding model, base vs head
The s3_vectors default embedding model is
text-embedding-3-small, and this proxy'smodel_listdoes not serve it under that name, so the search has to fall back to the SDK. For this leg only, port 34446 ran the merge base (9e8e486) and port 31642 ran the head (7f7e0d5), sharing the one Postgres and the one S3 Vectors bucket. Both sides answer the sameingest with NO embedding_model on the vector store registration (search falls back to the s3_vectors default text-embedding-3-small, which model_list does not serve)
curl -s http://127.0.0.1:34446/v1/rag/ingest -H 'Authorization: Bearer <team key>' -F file=@lit6750-doc.txt -F 'request={"ingest_options":{"embedding":{"model":"qa-team-embeddings"},"vector_store":{"custom_llm_provider":"s3_vectors","vector_bucket_name":"lit6750-qa-vectors","aws_region_name":"eu-central-1"}}}'search that store on the base proxy (port 34446)
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:34446/v1/vector_stores/lit6750-qa-vectors:litellm-index-4be4fe28/search -H 'Authorization: Bearer <team key>' -d '{"query":"What must be drained before flipping DNS?"}'search that store on the head proxy (port 31642)
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:31642/v1/vector_stores/lit6750-qa-vectors:litellm-index-4be4fe28/search -H 'Authorization: Bearer <team key>' -d '{"query":"What must be drained before flipping DNS?"}'Type
🐛 Bug Fix
🧹 Refactoring
Caveats (if any)
Low
/model/newaliases were not driven livemodel_listrouterkwarg removaltests/vector_store_testslive-API failures also fail at the merge baseDeploymentNotFound, and a missing Bedrock knowledge baseFinal Attestation
Note
Medium Risk
Changes vector store search embedding routing and spend attribution across providers; behavior shifts for unserved embedding models (SDK fallback instead of errors) and any code that passed
routerinto transform signatures.Overview
Vector store search no longer passes a bare
routerinto provider transforms or the HTTP handler.search()builds aRouterVectorStoreEmbeddingExecutor(with requestlitellm_metadata) and hands that asembedding_executor, so query embeddings can be attributed to the calling team/key (LIT-6750).S3 Vectors now subclasses
BaseQueryEmbeddingVectorStoreConfigand embeds queries through the same executor path as Azure AI/Milvus instead of its own Router lookup viaresolve_embedding_router. It still acceptsembedding_modelas an alias oflitellm_embedding_modeland defaults totext-embedding-3-smallwhen unset.RouterVectorStoreEmbeddingExecutorsimplifies to one rule: use the Router when it serves the model, otherwise call the LiteLLM SDK—with caller metadata attached in both cases. Unserved models no longer error; they fall back to SDK embedding (fixes stores with no named embedding model on proxies that don’t list the default).embed_query/aembed_queryon the base query-embedding config only use the injected executor (no router argument at the transform layer).Reviewed by Cursor Bugbot for commit 7f7e0d5. Bugbot is set up for automated code reviews on this repo. Configure here.