mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-13 08:52:02 +00:00
fix(proxy): preserve team aliases in Responses WebSockets (#51380)
Some checks are pending
CodSpeed Benchmarks / benchmarks (push) Waiting to run
Cost map pins recheck / rerun-cost-map-guard (push) Waiting to run
Publish lint base counts / publish (basedpyright, litellm/scripts/type_check_gate.py) (push) Waiting to run
Publish lint base counts / publish (openapi-docs, litellm/scripts/openapi_docs_gate.py) (push) Waiting to run
Publish lint base counts / publish (ruff-strict, litellm/scripts/ruff_strict_gate.py) (push) Waiting to run
Publish lint base counts / publish (test-quality, litellm/scripts/test_quality_gate.py) (push) Waiting to run
Publish lint base counts / publish (type-discipline, litellm/scripts/type_discipline_gate.py) (push) Waiting to run
Root entry limit / root-entry-limit (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
smoke / dashboard-build (push) Waiting to run
smoke / py3.10 (push) Waiting to run
smoke / py3.11 (push) Waiting to run
smoke / py3.12 (push) Waiting to run
smoke / py3.13 (push) Waiting to run
smoke / py3.14 (push) Waiting to run
smoke / py3.15 (push) Waiting to run
smoke / all-extras-py3.12 (push) Waiting to run
smoke / all-extras-py3.13 (push) Waiting to run
smoke / windows-sdk (push) Waiting to run
smoke / proxy-build-from-pip (push) Waiting to run
smoke / smoke passed (push) Blocked by required conditions
LiteLLM Rust DB / db-queries (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
unit / endpoints (push) Blocked by required conditions
unit / enterprise-package (push) Blocked by required conditions
unit / integrations (push) Blocked by required conditions
unit / llms-anthropic (push) Blocked by required conditions
unit / llms-bedrock (push) Blocked by required conditions
unit / llms-openai-meta (push) Blocked by required conditions
unit / llms-providers (push) Blocked by required conditions
unit / llms-vertex-ai (push) Blocked by required conditions
unit / mcp-elicitation (push) Blocked by required conditions
unit / mcp-server (push) Blocked by required conditions
unit / proxy-auth (push) Blocked by required conditions
unit / proxy-endpoints (push) Blocked by required conditions
unit / proxy-extras (push) Blocked by required conditions
unit / proxy-feature-endpoints (push) Blocked by required conditions
unit / proxy-hooks-client (push) Blocked by required conditions
unit / proxy-infra-db-spend (push) Blocked by required conditions
unit / proxy-infra-root-a-p (push) Blocked by required conditions
unit / proxy-infra-root-q-z (push) Blocked by required conditions
unit / proxy-infra-routing (push) Blocked by required conditions
unit / proxy-subprocess (push) Blocked by required conditions
unit / responses-caching-types (push) Blocked by required conditions
unit / router (push) Blocked by required conditions
unit / rust-bridge-harness (push) Blocked by required conditions
unit / auth-checks (push) Blocked by required conditions
unit / budgets (push) Blocked by required conditions
unit / endpoints-and-responses (push) Blocked by required conditions
unit / enterprise-routing (push) Blocked by required conditions
unit / root (push) Blocked by required conditions
unit / assert-shard-coverage (push) Waiting to run
unit / enterprise-managed-files (push) Blocked by required conditions
unit / core-utils (push) Blocked by required conditions
unit / docs (push) Waiting to run
unit / helm (push) Waiting to run
unit / OpenAI 3.25.0 compatibility (push) Blocked by required conditions
unit / custom-logging (push) Blocked by required conditions
unit / db-and-spend (push) Blocked by required conditions
unit / guardrails-hooks (push) Blocked by required conditions
unit / jwt-and-keys (push) Blocked by required conditions
unit / key-generation (push) Blocked by required conditions
unit / logging-misc (push) Blocked by required conditions
unit / mcp-oauth (push) Blocked by required conditions
unit / proxy-runtime (push) Blocked by required conditions
unit / proxy-server-core (push) Blocked by required conditions
unit / proxy-utils (push) Blocked by required conditions
unit / rust-bridge (push) Waiting to run
unit / enterprise-repositories-secrets (push) Blocked by required conditions
unit / proxy-server (push) Blocked by required conditions
unit / lens-python-310 (push) Blocked by required conditions
unit / caching-local (push) Blocked by required conditions
unit / ui-unit (push) Waiting to run
unit / OpenAI 2.20.0 compatibility (push) Blocked by required conditions
unit / OpenAI 3.0.0 compatibility (push) Blocked by required conditions
unit / redis-py 5.3.1 compatibility (push) Blocked by required conditions
unit / redis-py 6.4.0 compatibility (push) Blocked by required conditions
unit / redis-py 7.4.1 compatibility (push) Blocked by required conditions
unit / redis-py 8.0.1 compatibility (push) Blocked by required conditions
unit / coverage (push) Blocked by required conditions
unit / unit passed (push) Blocked by required conditions
GitHub Actions Security Analysis / zizmor (push) Waiting to run
Some checks are pending
CodSpeed Benchmarks / benchmarks (push) Waiting to run
Cost map pins recheck / rerun-cost-map-guard (push) Waiting to run
Publish lint base counts / publish (basedpyright, litellm/scripts/type_check_gate.py) (push) Waiting to run
Publish lint base counts / publish (openapi-docs, litellm/scripts/openapi_docs_gate.py) (push) Waiting to run
Publish lint base counts / publish (ruff-strict, litellm/scripts/ruff_strict_gate.py) (push) Waiting to run
Publish lint base counts / publish (test-quality, litellm/scripts/test_quality_gate.py) (push) Waiting to run
Publish lint base counts / publish (type-discipline, litellm/scripts/type_discipline_gate.py) (push) Waiting to run
Root entry limit / root-entry-limit (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
smoke / dashboard-build (push) Waiting to run
smoke / py3.10 (push) Waiting to run
smoke / py3.11 (push) Waiting to run
smoke / py3.12 (push) Waiting to run
smoke / py3.13 (push) Waiting to run
smoke / py3.14 (push) Waiting to run
smoke / py3.15 (push) Waiting to run
smoke / all-extras-py3.12 (push) Waiting to run
smoke / all-extras-py3.13 (push) Waiting to run
smoke / windows-sdk (push) Waiting to run
smoke / proxy-build-from-pip (push) Waiting to run
smoke / smoke passed (push) Blocked by required conditions
LiteLLM Rust DB / db-queries (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
unit / endpoints (push) Blocked by required conditions
unit / enterprise-package (push) Blocked by required conditions
unit / integrations (push) Blocked by required conditions
unit / llms-anthropic (push) Blocked by required conditions
unit / llms-bedrock (push) Blocked by required conditions
unit / llms-openai-meta (push) Blocked by required conditions
unit / llms-providers (push) Blocked by required conditions
unit / llms-vertex-ai (push) Blocked by required conditions
unit / mcp-elicitation (push) Blocked by required conditions
unit / mcp-server (push) Blocked by required conditions
unit / proxy-auth (push) Blocked by required conditions
unit / proxy-endpoints (push) Blocked by required conditions
unit / proxy-extras (push) Blocked by required conditions
unit / proxy-feature-endpoints (push) Blocked by required conditions
unit / proxy-hooks-client (push) Blocked by required conditions
unit / proxy-infra-db-spend (push) Blocked by required conditions
unit / proxy-infra-root-a-p (push) Blocked by required conditions
unit / proxy-infra-root-q-z (push) Blocked by required conditions
unit / proxy-infra-routing (push) Blocked by required conditions
unit / proxy-subprocess (push) Blocked by required conditions
unit / responses-caching-types (push) Blocked by required conditions
unit / router (push) Blocked by required conditions
unit / rust-bridge-harness (push) Blocked by required conditions
unit / auth-checks (push) Blocked by required conditions
unit / budgets (push) Blocked by required conditions
unit / endpoints-and-responses (push) Blocked by required conditions
unit / enterprise-routing (push) Blocked by required conditions
unit / root (push) Blocked by required conditions
unit / assert-shard-coverage (push) Waiting to run
unit / enterprise-managed-files (push) Blocked by required conditions
unit / core-utils (push) Blocked by required conditions
unit / docs (push) Waiting to run
unit / helm (push) Waiting to run
unit / OpenAI 3.25.0 compatibility (push) Blocked by required conditions
unit / custom-logging (push) Blocked by required conditions
unit / db-and-spend (push) Blocked by required conditions
unit / guardrails-hooks (push) Blocked by required conditions
unit / jwt-and-keys (push) Blocked by required conditions
unit / key-generation (push) Blocked by required conditions
unit / logging-misc (push) Blocked by required conditions
unit / mcp-oauth (push) Blocked by required conditions
unit / proxy-runtime (push) Blocked by required conditions
unit / proxy-server-core (push) Blocked by required conditions
unit / proxy-utils (push) Blocked by required conditions
unit / rust-bridge (push) Waiting to run
unit / enterprise-repositories-secrets (push) Blocked by required conditions
unit / proxy-server (push) Blocked by required conditions
unit / lens-python-310 (push) Blocked by required conditions
unit / caching-local (push) Blocked by required conditions
unit / ui-unit (push) Waiting to run
unit / OpenAI 2.20.0 compatibility (push) Blocked by required conditions
unit / OpenAI 3.0.0 compatibility (push) Blocked by required conditions
unit / redis-py 5.3.1 compatibility (push) Blocked by required conditions
unit / redis-py 6.4.0 compatibility (push) Blocked by required conditions
unit / redis-py 7.4.1 compatibility (push) Blocked by required conditions
unit / redis-py 8.0.1 compatibility (push) Blocked by required conditions
unit / coverage (push) Blocked by required conditions
unit / unit passed (push) Blocked by required conditions
GitHub Actions Security Analysis / zizmor (push) Waiting to run
* fix(proxy): preserve team aliases in Responses WebSockets Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com> * test(proxy): drop the pre-call kwargs assertion from the Responses WebSocket routing test Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com> Co-authored-by: mateo <277851410+mateo-berri@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
76794359cc
commit
6abacd329b
2 changed files with 106 additions and 2 deletions
|
|
@ -2678,7 +2678,6 @@ async def _responses_websocket_session(
|
|||
user_request_timeout=user_request_timeout,
|
||||
user_max_tokens=user_max_tokens,
|
||||
user_api_base=user_api_base,
|
||||
model=resolved_model,
|
||||
route_type="_aresponses_websocket",
|
||||
skip_guardrails=True,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2546,6 +2546,112 @@ def _key() -> UserAPIKeyAuth:
|
|||
|
||||
|
||||
class TestResponsesWSFirstFrameModelAuth:
|
||||
@pytest.mark.parametrize("query_model", [None, "gpt-6.1-sol"])
|
||||
@pytest.mark.parametrize("nested", [False, True])
|
||||
def test_team_alias_keeps_websocket_turns_on_the_authorized_deployment(
|
||||
self, monkeypatch: pytest.MonkeyPatch, query_model: str | None, nested: bool
|
||||
) -> None:
|
||||
from openai.types.responses import ResponseCompletedEvent
|
||||
|
||||
import litellm.proxy.proxy_server as ps
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth_websocket
|
||||
from litellm.responses.utils import ResponsesAPIRequestUtils
|
||||
from litellm.types.llms.openai import InputTokensDetails, OutputTokensDetails
|
||||
|
||||
model: Final = "gpt-6.1-sol"
|
||||
authorized_model: Final = "restricted/" + model
|
||||
deployment_id: Final = "restricted-deployment"
|
||||
router: Final = litellm.Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "shared/" + model,
|
||||
"litellm_params": {
|
||||
"model": model,
|
||||
"custom_llm_provider": "openai",
|
||||
"api_key": "shared-fixture-key",
|
||||
"order": 0,
|
||||
},
|
||||
"model_info": {"id": "shared-deployment"},
|
||||
},
|
||||
{
|
||||
"model_name": authorized_model,
|
||||
"litellm_params": {
|
||||
"model": model,
|
||||
"custom_llm_provider": "openai",
|
||||
"api_key": "restricted-fixture-key",
|
||||
"order": 1,
|
||||
},
|
||||
"model_info": {"id": deployment_id, "access_groups": ["Restricted"]},
|
||||
},
|
||||
],
|
||||
num_retries=0,
|
||||
)
|
||||
auth: Final = UserAPIKeyAuth(
|
||||
api_key="hashed-fixture-key",
|
||||
team_id="alias-team",
|
||||
models=["all-team-models"],
|
||||
team_models=["Restricted"],
|
||||
team_model_aliases={model: authorized_model},
|
||||
)
|
||||
completed: Final = json.dumps(
|
||||
{
|
||||
"type": "response.completed",
|
||||
"sequence_number": 1,
|
||||
"response": ResponsesAPIResponse(
|
||||
id="resp_alias",
|
||||
object="response",
|
||||
created_at=1,
|
||||
status="completed",
|
||||
model=model,
|
||||
output=[],
|
||||
parallel_tool_calls=True,
|
||||
tool_choice="auto",
|
||||
tools=[],
|
||||
usage=ResponseAPIUsage(
|
||||
input_tokens=1,
|
||||
output_tokens=0,
|
||||
total_tokens=1,
|
||||
input_tokens_details=InputTokensDetails(cached_tokens=0),
|
||||
output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
|
||||
),
|
||||
).model_dump(mode="json"),
|
||||
}
|
||||
)
|
||||
|
||||
def provider_connect(*_args: object, **_kwargs: object) -> MagicMock:
|
||||
replies: Final[asyncio.Queue[str]] = asyncio.Queue()
|
||||
|
||||
async def send(_message: str) -> None:
|
||||
await replies.put(completed)
|
||||
|
||||
async def receive(**_kwargs: object) -> str:
|
||||
return await replies.get()
|
||||
|
||||
peer: Final = MagicMock()
|
||||
peer.send = AsyncMock(side_effect=send)
|
||||
peer.recv = AsyncMock(side_effect=receive)
|
||||
peer.close = AsyncMock()
|
||||
connection: Final = MagicMock()
|
||||
connection.__aenter__ = AsyncMock(return_value=peer)
|
||||
connection.__aexit__ = AsyncMock(return_value=None)
|
||||
return connection
|
||||
|
||||
monkeypatch.setattr(ps, "llm_router", router)
|
||||
monkeypatch.setattr(ps, "master_key", None)
|
||||
monkeypatch.setattr(ps, "general_settings", {})
|
||||
monkeypatch.setitem(app.dependency_overrides, user_api_key_auth_websocket, lambda: auth)
|
||||
monkeypatch.setattr("websockets.connect", provider_connect)
|
||||
payload: Final = {"model": model, "input": "hello"}
|
||||
frame: Final = (
|
||||
{"type": "response.create", "response": payload} if nested else {"type": "response.create", **payload}
|
||||
)
|
||||
path: Final = "/v1/responses" + (f"?model={query_model}" if query_model is not None else "")
|
||||
with TestClient(app).websocket_connect(path) as websocket:
|
||||
websocket.send_json(frame)
|
||||
response: Final = ResponseCompletedEvent.model_validate_json(websocket.receive_text())
|
||||
|
||||
assert ResponsesAPIRequestUtils.get_model_id_from_response_id(response.response.id) == deployment_id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_endpoint_enforces_auth_after_model_from_first_frame(self):
|
||||
from litellm.proxy.response_api_endpoints.endpoints import (
|
||||
|
|
@ -2726,7 +2832,6 @@ class TestResponsesWSFirstFrameModelAuth:
|
|||
assert routed["model"] == "gpt-4o-mini"
|
||||
assert routed["input"] == replayed_input
|
||||
assert routed["previous_response_id"] == "resp_prev"
|
||||
assert processor.common_processing_pre_call_logic.await_args.kwargs["model"] == "gpt-4o-mini"
|
||||
assert processor.common_processing_pre_call_logic.await_args.kwargs["skip_guardrails"] is True
|
||||
assert callable(routed["responses_ws_frame_guard"])
|
||||
assert mock_route_request.await_args.kwargs["route_type"] == "_aresponses_websocket"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue