Skip to content

fix(proxy): keep requested model group when merging vector store file credentials - #36106

Open
devin-ai-integration[bot] wants to merge 1 commit into
litellm_internal_stagingfrom
litellm_fix_vector_store_alias_identity_36103
Open

fix(proxy): keep requested model group when merging vector store file credentials#36106
devin-ai-integration[bot] wants to merge 1 commit into
litellm_internal_stagingfrom
litellm_fix_vector_store_alias_identity_36103

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Vector store file routes lost the requested model group
  • Router then resolved by shared litellm_params.model
  • Wrong group could serve the request
  • Spend and logs attributed to the provider model

How it solves it:

  • Keep the caller's group in data["model"]
  • Credentials still merged, only model is restored
  • Regression test with two groups sharing a model

Relevant issues

Fixes #36103

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

Config with two model groups pointing at the same litellm_params.model, which is what makes the alias loss observable:

model_list:
  - model_name: vip-embeddings
    litellm_params:
      model: openai/text-embedding-3-small
      api_key: sk-vip-key
      api_base: http://127.0.0.1:8090
    model_info:
      id: vip-dep
  - model_name: public-embeddings
    litellm_params:
      model: openai/text-embedding-3-small
      api_key: sk-public-key
      api_base: http://127.0.0.1:8090
    model_info:
      id: public-dep
general_settings:
  master_key: sk-1234

The upstream is a local OpenAI-shaped server returning a vector store file list, since this box has no egress to api.openai.com, so the calls do not hit a real provider. Everything on the LiteLLM side (auth, routing, credential merge, deployment selection, spend logging) is the real code path

Before, at b66d4e6:

STORE_MODEL_IN_DB=False python litellm/proxy/proxy_cli.py --config config_36103.yaml --port 4000

for i in $(seq 1 10); do curl -s -D - -o /dev/null "http://localhost:4000/v1/vector_stores/vs_123/files?model=public-embeddings" -H "Authorization: Bearer sk-1234" | grep -i x-litellm-model-id; done
x-litellm-model-id: da537f9f9f8a40a596083c46a3fd9a88f21f564f10132a5aae49306aa9522fcb
... (x10)

curl -s localhost:4000/model/info -H "Authorization: Bearer sk-1234" | python3 -c "import json,sys; [print(m['model_name'], m['model_info'].get('id')) for m in json.load(sys.stdin)['data']]"
vip-embeddings vip-dep
public-embeddings public-dep
openai/text-embedding-3-small da537f9f9f8a40a596083c46a3fd9a88f21f564f10132a5aae49306aa9522fcb

psql "$DATABASE_URL" -A -F' | ' -c 'select model, model_group, call_type from "LiteLLM_SpendLogs" order by "startTime" desc limit 5;'
model | model_group | call_type
openai/text-embedding-3-small | openai/text-embedding-3-small | avector_store_file_list
openai/text-embedding-3-small | openai/text-embedding-3-small | avector_store_file_list
openai/text-embedding-3-small | openai/text-embedding-3-small | avector_store_file_list
openai/text-embedding-3-small | openai/text-embedding-3-small | avector_store_file_list
openai/text-embedding-3-small | openai/text-embedding-3-small | avector_store_file_list

The request asked for public-embeddings and the proxy served, tracked and logged it as openai/text-embedding-3-small, a group the caller never asked for and that no config entry declares

After, at 9cbc2c3, same config and same commands:

for i in $(seq 1 10); do curl -s -D - -o /dev/null "http://localhost:4000/v1/vector_stores/vs_123/files?model=public-embeddings" -H "Authorization: Bearer sk-1234" | grep -i x-litellm-model-id; done
x-litellm-model-id: 717646fd657b544bb5371593debb2dcd192d6e2ff8b3fa4d56d9c4b4e019e77b
... (x10)

curl -s localhost:4000/model/info -H "Authorization: Bearer sk-1234" | python3 -c "import json,sys; [print(m['model_name'], m['model_info'].get('id')) for m in json.load(sys.stdin)['data']]"
vip-embeddings vip-dep
public-embeddings public-dep
public-embeddings 717646fd657b544bb5371593debb2dcd192d6e2ff8b3fa4d56d9c4b4e019e77b

psql "$DATABASE_URL" -A -F' | ' -c 'select model, model_group, call_type from "LiteLLM_SpendLogs" order by "startTime" desc limit 5;'
model | model_group | call_type
openai/text-embedding-3-small | public-embeddings | avector_store_file_list
openai/text-embedding-3-small | public-embeddings | avector_store_file_list
openai/text-embedding-3-small | public-embeddings | avector_store_file_list
openai/text-embedding-3-small | public-embeddings | avector_store_file_list
openai/text-embedding-3-small | public-embeddings | avector_store_file_list

The deployment is now resolved under the group the caller requested, and spend rows carry model_group: public-embeddings

Type

🐛 Bug Fix

Changes

get_deployment_credentials_with_provider returns the deployment's litellm_params.model, and the vector store file endpoints merged that dict straight into the request body, so data["model"] stopped being the requested group before the Router ever saw it. The Router usually papers over this with the _get_deployment_by_litellm_model fallback, but that fallback matches on the underlying model, so any second group sharing it becomes an equally valid candidate: access group filtering, team scoping, per-group fallbacks, rate limits and tags are all evaluated against the wrong name, and the clientside-credential upsert registers a synthetic deployment whose model_group is the provider model

The fix is one helper in the vector store file endpoints that merges credentials and then puts the requested group name back:

def _merge_credentials_keeping_requested_model(data, credentials, requested_model, file_id=None) -> None:
    prepare_data_with_credentials(data=data, credentials=credentials, file_id=file_id)
    if requested_model is not None:
        data["model"] = requested_model

It is used at the four call sites that merge deployment credentials: the managed file id path, the encoded file id path, the model routing hint path, and the single team deployment fallback. Credentials themselves are untouched, so provider-only fields such as the Bedrock batch keys added in #24548 still flow through, and the batch and file endpoints that call the provider SDK directly rather than routing keep their current behavior

Three existing tests asserted data["model"] came back as the provider model out of hand written credential mocks. That expectation is the bug, so they now assert the requested group, and a new test builds a real Router with two groups sharing openai/text-embedding-3-small and checks that resolution stays on the requested one across repeated selections, which fails before this change since simple shuffle splits across both

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

… credentials

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR preserves the requested model group after merging vector-store deployment credentials, preventing shared provider models from changing routing and spend attribution.

  • Adds a centralized credential-merge helper that restores the requested model.
  • Applies it across managed-file, model-hint, and team-deployment fallback paths.
  • Updates existing expectations and adds a real-Router regression test for groups sharing an underlying provider model.

Confidence Score: 5/5

The PR appears safe to merge with no actionable correctness or security issues identified.

The changed paths consistently restore the selected routing group after merging deployment credentials, and the regression coverage verifies the shared-provider-model scenario that previously caused incorrect routing and attribution.

Important Files Changed

Filename Overview
litellm/proxy/vector_store_files_endpoints/endpoints.py Centralizes credential merging while retaining the routing group across the four affected vector-store file paths; no actionable defect was established.
tests/test_litellm/proxy/vector_store_endpoints/test_vector_store_endpoints.py Updates corrected model-group expectations and adds regression coverage using two groups backed by the same provider model.
tests/test_litellm/proxy/vector_store_endpoints/test_vector_store_tenant_guard.py Updates the managed-deployment assertion to verify that the routing group survives credential merging.

Reviews (1): Last reviewed commit: "fix(proxy): keep requested model group w..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unconditional model injection in get_deployment_credentials_with_provider loses alias identity when model groups share a litellm_params.model

1 participant