-
-
Notifications
You must be signed in to change notification settings - Fork 11.6k
test(e2e): add models_mgmt suite covering add/update/delete persistence and route permissions #32272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
mubashir1osmani
wants to merge
8
commits into
litellm_internal_staging
from
litellm_e2e_models_mgmt_suite
Closed
test(e2e): add models_mgmt suite covering add/update/delete persistence and route permissions #32272
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
25b6985
fix(e2e): route model management to the control plane and restore Gat…
mubashir1osmani 9a6c9b4
test(e2e): make the fake transport payload depend on response_type
mubashir1osmani 82efd5c
test(e2e): add models_mgmt suite covering add/update/delete persisten…
mubashir1osmani e0a009f
Merge branch 'litellm_internal_staging' into litellm_e2e_models_mgmt_…
mubashir1osmani be48835
test(e2e): mirror the full LiteLLM_Params surface in DeploymentParams
mubashir1osmani 37503ff
Merge branch 'litellm_e2e_models_mgmt_suite' of https://github.com/Be…
mubashir1osmani 35bc89e
fix(e2e): drop ModelMode alias duplicated by the staging merge
mubashir1osmani 2623ff4
test(e2e): poll the /model/info read-back after /model/new
mubashir1osmani File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| """Models-management suite client fixture; lifecycle/skip/marker live in the parent conftest.""" | ||
|
|
||
| import pytest | ||
|
|
||
| from models_mgmt_client import ModelsMgmtClient, build_client | ||
|
|
||
|
|
||
| def pytest_configure(config: pytest.Config) -> None: | ||
| config.addinivalue_line( | ||
| "markers", | ||
| "covers: registry cell a test covers, e.g. mgmt.model.add.persists", | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def client() -> ModelsMgmtClient: | ||
| return build_client() |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| """Client for the model-management e2e suite: the shared Gateway plus the | ||
| /model/update write, a strict /model/delete, a /model/info lookup by name, and | ||
| the raw-status calls the tests judge by HTTP outcome (a forbidden /model/new, a | ||
| chat against a deleted model). | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| from e2e_gateway import Gateway, build_gateway | ||
| from e2e_http import NoBody, StreamingResponse, unwrap | ||
| from models import ( | ||
| ChatBody, | ||
| ChatMessage, | ||
| KeyGenerateBody, | ||
| LiteLLMParamsBody, | ||
| ModelDeleteBody, | ||
| ModelInfoBody, | ||
| ModelInfoEntry, | ||
| ModelNewBody, | ||
| ModelUpdateBody, | ||
| ModelUpdateParams, | ||
| ) | ||
|
|
||
| ROUTE_NOT_ALLOWED_MARKER = "not allowed to call this route" | ||
| UNKNOWN_MODEL_MARKER = "Invalid model name passed in model=" | ||
| NO_DEPLOYMENTS_MARKER = "There are no healthy deployments" | ||
|
|
||
|
|
||
| def is_deleted_model_rejection(outcome: StreamingResponse, model: str) -> bool: | ||
| """True if the gateway refused the call because `model` is gone: a 400 naming | ||
| the model, either the proxy's unknown-model shape (the data plane never knew | ||
| the group) or the router's no-healthy-deployments shape (the group name | ||
| outlives its last deployment in the router until restart).""" | ||
| if outcome.status_code != 400 or model not in outcome.body: | ||
| return False | ||
| return UNKNOWN_MODEL_MARKER in outcome.body or NO_DEPLOYMENTS_MARKER in outcome.body | ||
|
|
||
|
|
||
| @dataclass(frozen=True, slots=True) | ||
| class ModelsMgmtClient: | ||
| gateway: Gateway | ||
|
|
||
| def llm_only_key(self) -> str: | ||
| return self.gateway.generate_key( | ||
| KeyGenerateBody(models=[], allowed_routes=["llm_api_routes"]) | ||
| ) | ||
|
|
||
| def find_deployment(self, model_name: str) -> ModelInfoEntry | None: | ||
| return next( | ||
| (entry for entry in self.gateway.model_info() if entry.model_name == model_name), | ||
| None, | ||
| ) | ||
|
|
||
| def update_model_tpm(self, model_id: str, tpm: int) -> None: | ||
| _ = unwrap( | ||
| self.gateway.transport.post( | ||
| "/model/update", | ||
| headers=self.gateway.transport.master, | ||
| json=ModelUpdateBody( | ||
| litellm_params=ModelUpdateParams(tpm=tpm), | ||
| model_info=ModelInfoBody(id=model_id), | ||
| ), | ||
| response_type=NoBody, | ||
| ) | ||
| ) | ||
|
|
||
| def delete_model(self, model_id: str) -> None: | ||
| """Strict delete for the act phase of a test: a failed delete is a hard | ||
| failure, unlike the warn-only Gateway.delete_model used at teardown.""" | ||
| _ = unwrap( | ||
| self.gateway.transport.post( | ||
| "/model/delete", | ||
| headers=self.gateway.transport.master, | ||
| json=ModelDeleteBody(id=model_id), | ||
| response_type=NoBody, | ||
| ) | ||
| ) | ||
|
|
||
| def create_model_status( | ||
| self, key: str, model_name: str, litellm_params: LiteLLMParamsBody | ||
| ) -> StreamingResponse: | ||
| return self.gateway.transport.send( | ||
| "/model/new", | ||
| headers=self.gateway.transport.bearer(key), | ||
| json=ModelNewBody( | ||
| model_name=model_name, | ||
| litellm_params=litellm_params, | ||
| model_info=ModelInfoBody(id=model_name), | ||
| ), | ||
| ) | ||
|
|
||
| def chat_status(self, key: str, model: str, content: str) -> StreamingResponse: | ||
| return self.gateway.transport.send( | ||
| "/chat/completions", | ||
| headers=self.gateway.transport.bearer(key), | ||
| json=ChatBody( | ||
| model=model, | ||
| messages=[ChatMessage(role="user", content=content)], | ||
| max_tokens=16, | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| def build_client() -> ModelsMgmtClient: | ||
| return ModelsMgmtClient(gateway=build_gateway()) |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.