Skip to content

test(e2e): cover credential-backed /v1/messages request - #33863

Merged
ishaan-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_e2e_credential_messages
Jul 19, 2026
Merged

test(e2e): cover credential-backed /v1/messages request#33863
ishaan-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_e2e_credential_messages

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Pre-Submission checklist

  • 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

Screenshots / Proof of Fix

Captured against a local proxy at commit eb36a94a5ec435fcba86613f9994caa894cfd476 with Postgres + Redis, hitting the real Anthropic API (real $)

Live test run

tests/e2e/llm_translation/test_credential_messages_e2e.py::TestCredentialBackedMessages::test_credential_backed_messages PASSED [100%]
============================== 1 passed in 0.76s ===============================

The same flow by hand (the request bodies reference $ANTHROPIC_API_KEY so the key is never printed)

$ curl -sS -w 'credential HTTP %{http_code}\n' -X POST http://localhost:4000/credentials \
    -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
    --data "{\"credential_name\":\"e2e-cred-demo\",\"credential_values\":{\"api_key\":\"$ANTHROPIC_API_KEY\"},\"credential_info\":{}}"
credential HTTP 200
{"success":true,"message":"Credential created successfully"}

$ curl -sS -w 'model HTTP %{http_code}\n' -X POST http://localhost:4000/model/new \
    -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
    --data '{"model_name":"e2e-cred-messages","litellm_params":{"model":"anthropic/claude-haiku-4-5","litellm_credential_name":"e2e-cred-demo"},"model_info":{}}'
model HTTP 200
{"model_id":"f2d28fe3-9cb5-421d-9b19-73b645a9932d","model_name":"e2e-cred-messages", ...}   # note: no api_key in litellm_params

$ curl -sS -w 'messages HTTP %{http_code}\n' -X POST http://localhost:4000/v1/messages \
    -H 'Authorization: Bearer sk-1234' -H 'anthropic-version: 2023-06-01' -H 'Content-Type: application/json' \
    --data '{"model":"e2e-cred-messages","max_tokens":16,"messages":[{"role":"user","content":"reply with one word"}]}'
messages HTTP 200
{"model":"e2e-cred-messages","id":"msg_011CdAJ7VvAT195cNnvhqDVN","type":"message","role":"assistant","content":[{"type":"text","text":"Hello"}],"stop_reason":"end_turn", ...}

The deployment carries no api_key of its own, so the 200 with assistant text proves the stored credential resolved into the request

Type

✅ Test

Changes

We had no e2e coverage for the credentials feature actually serving traffic: creating a stored credential, attaching a model to it, and having a request resolve that credential at call time. This adds that as a single natural user scenario against /v1/messages

tests/e2e/llm_translation/test_credential_messages_e2e.py walks it end to end: POST /credentials storing the real Anthropic key (read from the environment at runtime, never hardcoded), register a deployment whose only auth is litellm_credential_name pointing at that credential (the model carries no api_key of its own), then drive a real /v1/messages request and assert an assistant message with non-empty text came back. Because the deployment has no key of its own, the call only succeeds if the stored credential resolved into the request, so the assertion actually exercises credential resolution rather than passing vacuously

Supporting harness additions

models.py:      LiteLLMParamsBody.litellm_credential_name: str | None
                CredentialCreateBody { credential_name, credential_values, credential_info }
                CredentialCreateResponse { success }
proxy_client.py: ProxyClient.create_credential(body)   # POST /credentials
                 ProxyClient.delete_credential(name)   # DELETE /credentials/{name}, warn-only teardown

Teardown deletes the model before the credential so the deployment is never left referencing a deleted credential

Coverage registry gains one row, mgmt.credential.new.serves_request, which the new test declares via @pytest.mark.covers

One thing surfaced while writing this: stored credential values are injected verbatim and do not go through the proxy's os.environ/NAME secret resolution, even though litellm_params values on a model config do. So a credential stored as {"api_key": "os.environ/ANTHROPIC_API_KEY"} reaches the provider literally and 401s, whereas the same value in a model's litellm_params resolves. The test stores the real value (the realistic user path), so it does not depend on that behavior; flagging it separately in case the divergence between the two paths is unintended

QA runbook

  • tests/e2e/llm_translation/test_credential_messages_e2e.py::TestCredentialBackedMessages::test_credential_backed_messages - a model whose only auth is a stored credential serves a real Anthropic /v1/messages request
    • Create a credential holding the real Anthropic key (referenced from env so it isn't printed): curl -X POST http://localhost:4000/credentials -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" --data "{\"credential_name\":\"e2e-cred-demo\",\"credential_values\":{\"api_key\":\"$ANTHROPIC_API_KEY\"},\"credential_info\":{}}"
    • Register a deployment with no api_key, only the credential name: curl -X POST http://localhost:4000/model/new -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -d '{"model_name": "e2e-cred-messages", "litellm_params": {"model": "anthropic/claude-haiku-4-5", "litellm_credential_name": "e2e-cred-demo"}, "model_info": {}}'
    • Send a /v1/messages request for e2e-cred-messages and expect a 200 with an assistant message: curl -X POST http://localhost:4000/v1/messages -H "Authorization: Bearer sk-1234" -H "anthropic-version: 2023-06-01" -H "Content-Type: application/json" -d '{"model": "e2e-cred-messages", "max_tokens": 16, "messages": [{"role": "user", "content": "reply with one word"}]}'
    • Sanity check: this test makes sense to add and is not hand-wavey; the deployment carries no api_key of its own, so a 200 with assistant text proves the stored credential resolved into the request rather than some ambient key

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

Link to Devin session: https://app.devin.ai/sessions/2b483d3d20c3428f852162b995ecfcd2
Requested by: @ishaan-berri

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
@ishaan-berri ishaan-berri self-assigned this Jul 18, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@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 Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a new end-to-end test that verifies stored credentials correctly resolve into a live /v1/messages request — a scenario that previously had no e2e coverage. Supporting harness additions include CredentialCreateBody/CredentialCreateResponse models, create_credential/delete_credential client methods, litellm_credential_name on LiteLLMParamsBody, and a new coverage-registry row.

  • The test itself is well-structured: a model is registered with no api_key of its own (only litellm_credential_name), so a successful assistant response proves credential resolution rather than passing on ambient auth. Teardown is LIFO, deleting the model before the credential as intended.
  • CredentialCreateBody.credential_values and credential_info are typed dict[str, str], which is narrower than the actual API (CreateCredentialItem uses plain dict); the preferred wide type in this repo is dict[str, object].
  • The new coverage-registry source field points to the production endpoint file (credential_endpoints/endpoints.py:42) rather than the test file, unlike every other entry in the registry.

Confidence Score: 4/5

Safe to merge; changes are confined to the e2e test harness and add no production code paths

The test logic, teardown ordering, and client wiring are all correct. The only concerns are the overly narrow dict[str, str] types on CredentialCreateBody and a minor registry-metadata inconsistency — neither affects correctness of the new test or production behaviour.

tests/e2e/models.py — the dict[str, str] type constraint on credential_values and credential_info could silently reject valid future harness usage

Important Files Changed

Filename Overview
tests/e2e/llm_translation/test_credential_messages_e2e.py New e2e test covering credential-backed /v1/messages; teardown order, assertion logic, and lifecycle usage are all correct
tests/e2e/models.py Adds CredentialCreateBody/CredentialCreateResponse and litellm_credential_name to LiteLLMParamsBody; credential_values and credential_info are typed dict[str, str] which is narrower than the actual API's plain dict
tests/e2e/proxy_client.py Adds create_credential (unwrap on failure) and delete_credential (warn-only on failure) methods; consistent with existing harness patterns
tests/e2e/coverage_registry/mgmt.yaml New mgmt.credential.new.serves_request registry row; source field references production endpoint file rather than the test file, unlike all other entries

Reviews (1): Last reviewed commit: "test(e2e): cover credential-backed /v1/m..." | Re-trigger Greptile

Comment thread tests/e2e/models.py
Comment on lines +560 to +563
class CredentialCreateBody(BaseModel):
credential_name: str
credential_values: dict[str, str]
credential_info: dict[str, str] = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 credential_values and credential_info typed too narrowly

Both fields are typed as dict[str, str], but the actual API model (CreateCredentialItem) uses plain dict — values can be any JSON type. Pydantic will reject a harness call the moment a credential value is non-string (e.g. a boolean or numeric timeout), even though the server would accept it fine. The repo-preferred wide type is dict[str, object].

- {id: mgmt.config_override.hashicorp_vault.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "config_override_endpoints.py", rationale: "Vault integration (smoke)"}
- {id: mgmt.workflow.list.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "workflow_management_endpoints.py", rationale: "Workflow tracking (smoke)"}
- {id: mgmt.credential_migration.check.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "key_management_endpoints.py:4252", rationale: "Encryption migration (smoke)"}
- {id: mgmt.credential.new.serves_request, module: mgmt, tier: P1, surface: api, assertions: [serves_request], source: "credential_endpoints/endpoints.py:42", rationale: "Stored credential resolves into a deployment and serves a live /messages request"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 source field references production code, not the test file

Every other entry in this file sets source to a test-harness or test file (e.g. key_management_endpoints.py:4252, workflow_management_endpoints.py). This new entry sets source: "credential_endpoints/endpoints.py:42", which is the production endpoint. The test that covers this entry lives in llm_translation/test_credential_messages_e2e.py; pointing there would keep the registry consistent and make it easy to jump from the registry to the actual test.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
@codspeed-hq

codspeed-hq Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_e2e_credential_messages (eb36a94) with litellm_internal_staging (e238e89)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (3f9b71c) during the generation of this report, so e238e89 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@ishaan-berri
ishaan-berri merged commit b83c60b into litellm_internal_staging Jul 19, 2026
77 checks passed
@ishaan-berri
ishaan-berri deleted the litellm_e2e_credential_messages branch July 19, 2026 01:30
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.

2 participants