mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-28 01:32:17 +00:00
Merge pull request #41723 from BerriAI/litellm_/circleci-specific-sha-0cf414
fix(proxy): forward every method on the typesafe pass-through route
This commit is contained in:
commit
34718f0da6
5 changed files with 278 additions and 6 deletions
|
|
@ -20374,6 +20374,50 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/typesafe/{endpoint}": {
|
"/typesafe/{endpoint}": {
|
||||||
|
"delete": {
|
||||||
|
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
||||||
|
"operationId": "typesafe_proxy_route_typesafe__endpoint__delete",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "endpoint",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"title": "Endpoint",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Successful Response"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Validation Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"APIKeyHeader": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": "Typesafe Proxy Route",
|
||||||
|
"tags": [
|
||||||
|
"llm_passthrough"
|
||||||
|
]
|
||||||
|
},
|
||||||
"get": {
|
"get": {
|
||||||
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
||||||
"operationId": "typesafe_proxy_route_typesafe__endpoint__get",
|
"operationId": "typesafe_proxy_route_typesafe__endpoint__get",
|
||||||
|
|
@ -20418,6 +20462,50 @@
|
||||||
"llm_passthrough"
|
"llm_passthrough"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"patch": {
|
||||||
|
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
||||||
|
"operationId": "typesafe_proxy_route_typesafe__endpoint__patch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "endpoint",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"title": "Endpoint",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Successful Response"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Validation Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"APIKeyHeader": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": "Typesafe Proxy Route",
|
||||||
|
"tags": [
|
||||||
|
"llm_passthrough"
|
||||||
|
]
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
||||||
"operationId": "typesafe_proxy_route_typesafe__endpoint__post",
|
"operationId": "typesafe_proxy_route_typesafe__endpoint__post",
|
||||||
|
|
@ -20461,6 +20549,50 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"llm_passthrough"
|
"llm_passthrough"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"description": "[Docs](https://docs.litellm.ai/docs/pass_through/typesafe)",
|
||||||
|
"operationId": "typesafe_proxy_route_typesafe__endpoint__put",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "endpoint",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"title": "Endpoint",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Successful Response"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Validation Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"APIKeyHeader": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": "Typesafe Proxy Route",
|
||||||
|
"tags": [
|
||||||
|
"llm_passthrough"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/vertex_ai/discovery/{endpoint}": {
|
"/vertex_ai/discovery/{endpoint}": {
|
||||||
|
|
|
||||||
|
|
@ -527,7 +527,7 @@ async def mistral_proxy_route(
|
||||||
|
|
||||||
@router.api_route(
|
@router.api_route(
|
||||||
"/typesafe/{endpoint:path}",
|
"/typesafe/{endpoint:path}",
|
||||||
methods=["GET", "POST"], # mutable-ok: FastAPI route metadata requires a list
|
methods=["GET", "POST", "PUT", "DELETE", "PATCH"], # mutable-ok: FastAPI route metadata requires a list
|
||||||
tags=["TypeSafe AI Pass-through", "pass-through"], # mutable-ok: FastAPI route metadata requires a list
|
tags=["TypeSafe AI Pass-through", "pass-through"], # mutable-ok: FastAPI route metadata requires a list
|
||||||
)
|
)
|
||||||
async def typesafe_proxy_route(
|
async def typesafe_proxy_route(
|
||||||
|
|
|
||||||
|
|
@ -1360,8 +1360,7 @@ def test_models_by_provider():
|
||||||
or v["litellm_provider"] == "bedrock_converse"
|
or v["litellm_provider"] == "bedrock_converse"
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
elif v.get("mode") == "search":
|
elif v.get("mode") in ("search", "evaluation"):
|
||||||
# Skip search providers as they don't have traditional models
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
providers.add(v["litellm_provider"])
|
providers.add(v["litellm_provider"])
|
||||||
|
|
|
||||||
|
|
@ -6149,6 +6149,42 @@ class TestTypeSafePassthroughRoute:
|
||||||
request.json = AsyncMock(return_value=body)
|
request.json = AsyncMock(return_value=body)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client(self, monkeypatch: pytest.MonkeyPatch) -> Iterator[TestClient]:
|
||||||
|
from litellm.proxy.proxy_server import app
|
||||||
|
|
||||||
|
monkeypatch.setenv("TYPESAFE_API_KEY", "typesafe-test-key")
|
||||||
|
monkeypatch.setenv("TYPESAFE_API_BASE", "https://typesafe.example/base")
|
||||||
|
monkeypatch.delenv("SERVER_ROOT_PATH", raising=False)
|
||||||
|
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
|
||||||
|
litellm.in_memory_llm_clients_cache.flush_cache()
|
||||||
|
monkeypatch.setitem(app.dependency_overrides, user_api_key_auth, lambda: UserAPIKeyAuth(api_key="sk-virtual"))
|
||||||
|
yield TestClient(app)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"method, body",
|
||||||
|
[
|
||||||
|
("GET", None),
|
||||||
|
("POST", {"state": "x"}),
|
||||||
|
("PUT", {"state": "x"}),
|
||||||
|
("DELETE", None),
|
||||||
|
("PATCH", {"state": "x"}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_forwards_every_method_and_body_upstream(
|
||||||
|
self, client: TestClient, method: str, body: dict[str, str] | None
|
||||||
|
) -> None:
|
||||||
|
with respx.mock(assert_all_called=True) as upstream:
|
||||||
|
route = upstream.request(method, "https://typesafe.example/base/v1/systemone").mock(
|
||||||
|
return_value=httpx.Response(200, json={"id": "upstream_123"})
|
||||||
|
)
|
||||||
|
response = client.request(method, "/typesafe/v1/systemone", json=body)
|
||||||
|
|
||||||
|
assert (response.status_code, response.json()) == (200, {"id": "upstream_123"})
|
||||||
|
sent: Final = route.calls.last.request
|
||||||
|
assert sent.headers["authorization"] == "Bearer typesafe-test-key"
|
||||||
|
assert json.loads(sent.content or b"{}") == (body or {})
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_forwards_target_auth_headers_provider_and_query(self, monkeypatch):
|
async def test_forwards_target_auth_headers_provider_and_query(self, monkeypatch):
|
||||||
monkeypatch.setenv("TYPESAFE_API_KEY", "typesafe-test-key")
|
monkeypatch.setenv("TYPESAFE_API_KEY", "typesafe-test-key")
|
||||||
|
|
|
||||||
111
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
111
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -16488,16 +16488,28 @@ export interface paths {
|
||||||
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||||
*/
|
*/
|
||||||
get: operations["typesafe_proxy_route_typesafe__endpoint__get"];
|
get: operations["typesafe_proxy_route_typesafe__endpoint__get"];
|
||||||
put?: never;
|
/**
|
||||||
|
* Typesafe Proxy Route
|
||||||
|
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||||
|
*/
|
||||||
|
put: operations["typesafe_proxy_route_typesafe__endpoint__put"];
|
||||||
/**
|
/**
|
||||||
* Typesafe Proxy Route
|
* Typesafe Proxy Route
|
||||||
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||||
*/
|
*/
|
||||||
post: operations["typesafe_proxy_route_typesafe__endpoint__post"];
|
post: operations["typesafe_proxy_route_typesafe__endpoint__post"];
|
||||||
delete?: never;
|
/**
|
||||||
|
* Typesafe Proxy Route
|
||||||
|
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||||
|
*/
|
||||||
|
delete: operations["typesafe_proxy_route_typesafe__endpoint__delete"];
|
||||||
options?: never;
|
options?: never;
|
||||||
head?: never;
|
head?: never;
|
||||||
patch?: never;
|
/**
|
||||||
|
* Typesafe Proxy Route
|
||||||
|
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||||
|
*/
|
||||||
|
patch: operations["typesafe_proxy_route_typesafe__endpoint__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
"/update/default_team_settings": {
|
"/update/default_team_settings": {
|
||||||
|
|
@ -61695,6 +61707,37 @@ export interface operations {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
typesafe_proxy_route_typesafe__endpoint__put: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
endpoint: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
typesafe_proxy_route_typesafe__endpoint__post: {
|
typesafe_proxy_route_typesafe__endpoint__post: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -61726,6 +61769,68 @@ export interface operations {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
typesafe_proxy_route_typesafe__endpoint__delete: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
endpoint: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
typesafe_proxy_route_typesafe__endpoint__patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
endpoint: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
update_default_team_settings_update_default_team_settings_patch: {
|
update_default_team_settings_update_default_team_settings_patch: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue