mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(proxy): forward every method on the typesafe pass-through route
Backport of #41723 to stable/1.100.x.
Cherry-picked from 34718f0da6 (main).
This commit is contained in:
parent
8bf252b397
commit
ded7f601d9
4 changed files with 149 additions and 7 deletions
|
|
@ -471,7 +471,7 @@ async def mistral_proxy_route(
|
|||
|
||||
@router.api_route(
|
||||
"/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
|
||||
)
|
||||
async def typesafe_proxy_route(
|
||||
|
|
|
|||
|
|
@ -1360,8 +1360,7 @@ def test_models_by_provider():
|
|||
or v["litellm_provider"] == "bedrock_converse"
|
||||
):
|
||||
continue
|
||||
elif v.get("mode") == "search":
|
||||
# Skip search providers as they don't have traditional models
|
||||
elif v.get("mode") in ("search", "evaluation"):
|
||||
continue
|
||||
else:
|
||||
providers.add(v["litellm_provider"])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import contextlib
|
|||
import json
|
||||
import os
|
||||
import traceback
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import Iterator, Mapping
|
||||
from types import MappingProxyType, SimpleNamespace
|
||||
from typing import Final
|
||||
from unittest import mock
|
||||
|
|
@ -12,6 +12,7 @@ from urllib.parse import parse_qs
|
|||
|
||||
import httpx
|
||||
import pytest
|
||||
import respx
|
||||
from fastapi import HTTPException, Request, Response
|
||||
from fastapi.testclient import TestClient
|
||||
from starlette.datastructures import FormData
|
||||
|
|
@ -42,6 +43,7 @@ from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
|
|||
)
|
||||
from litellm.proxy._types import LitellmUserRoles, SpecialHeaders, UserAPIKeyAuth
|
||||
from litellm.proxy.auth.handle_jwt import JWTHandler
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
from litellm.types.passthrough_endpoints.vertex_ai import VertexPassThroughCredentials
|
||||
|
||||
|
||||
|
|
@ -4640,6 +4642,42 @@ class TestTypeSafePassthroughRoute:
|
|||
request.json = AsyncMock(return_value=body)
|
||||
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
|
||||
async def test_forwards_target_auth_headers_provider_and_query(self, monkeypatch):
|
||||
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
|
|
@ -15911,16 +15911,28 @@ export interface paths {
|
|||
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||
*/
|
||||
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
|
||||
* @description [Docs](https://docs.litellm.ai/docs/pass_through/typesafe)
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
};
|
||||
"/update/default_team_settings": {
|
||||
|
|
@ -58373,6 +58385,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: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -58404,6 +58447,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: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue