mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-05 08:23:17 +00:00
[Bug Fix] Passthrough API Endpoints - Ensure query params are forwarded from origin url to downstream request (#15087)
* test_pass_through_request_query_params_forwarding * fix: pass_through_request * test_azure_openai_assistants_e2e_operations_stream * test_azure_openai_assistants_e2e_operations_stream
This commit is contained in:
@@ -26,4 +26,9 @@ model_list:
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
custom_auth: custom_auth_basic.user_api_key_auth
|
||||
custom_auth: custom_auth_basic.user_api_key_auth
|
||||
pass_through_endpoints:
|
||||
- path: "/azure-config-passthrough"
|
||||
target: os.environ/AZURE_API_BASE
|
||||
headers:
|
||||
Authorization: os.environ/AZURE_API_KEY
|
||||
@@ -688,10 +688,8 @@ async def pass_through_request( # noqa: PLR0915
|
||||
|
||||
# combine url with query params for logging
|
||||
requested_query_params: Optional[dict] = (
|
||||
query_params or request.query_params.__dict__
|
||||
query_params or dict(request.query_params)
|
||||
)
|
||||
if requested_query_params == request.query_params.__dict__:
|
||||
requested_query_params = None
|
||||
|
||||
requested_query_params_str = None
|
||||
if requested_query_params:
|
||||
|
||||
@@ -96,3 +96,37 @@ def test_openai_assistants_e2e_operations_stream():
|
||||
event_handler=EventHandler(),
|
||||
) as stream:
|
||||
stream.until_done()
|
||||
|
||||
|
||||
|
||||
def test_azure_openai_assistants_e2e_operations_stream():
|
||||
client = openai.OpenAI(base_url="http://0.0.0.0:4000/azure-config-passthrough", api_key="sk-1234")
|
||||
assistant = client.beta.assistants.create(
|
||||
name="Math Tutor",
|
||||
instructions="You are a personal math tutor. Write and run code to answer math questions.",
|
||||
tools=[{"type": "code_interpreter"}],
|
||||
model="gpt-4o",
|
||||
)
|
||||
print("assistant created", assistant)
|
||||
|
||||
thread = client.beta.threads.create()
|
||||
print("thread created", thread)
|
||||
|
||||
message = client.beta.threads.messages.create(
|
||||
thread_id=thread.id,
|
||||
role="user",
|
||||
content="I need to solve the equation `3x + 11 = 14`. Can you help me?",
|
||||
)
|
||||
print("message created", message)
|
||||
|
||||
# Then, we use the `stream` SDK helper
|
||||
# with the `EventHandler` class to create the Run
|
||||
# and stream the response.
|
||||
|
||||
with client.beta.threads.runs.stream(
|
||||
thread_id=thread.id,
|
||||
assistant_id=assistant.id,
|
||||
instructions="Please address the user as Jane Doe. The user has a premium account.",
|
||||
event_handler=EventHandler(),
|
||||
) as stream:
|
||||
stream.until_done()
|
||||
@@ -1252,6 +1252,90 @@ async def test_delete_pass_through_endpoint_empty_list():
|
||||
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pass_through_request_query_params_forwarding():
|
||||
"""
|
||||
Test that query parameters from the original request are properly forwarded to the target URL.
|
||||
|
||||
This test verifies the fix for the bug where query parameters like api-version were being lost
|
||||
when forwarding requests to Azure OpenAI and other pass-through endpoints.
|
||||
"""
|
||||
with patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging:
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.pass_through_endpoints.HttpPassThroughEndpointHelpers.non_streaming_http_request_handler"
|
||||
) as mock_http_handler:
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.pass_through_endpoints.ProxyBaseLLMRequestProcessing"
|
||||
) as mock_processing:
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.pass_through_endpoints.pass_through_endpoint_logging.pass_through_async_success_handler"
|
||||
) as mock_success_handler:
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.pass_through_endpoints.get_response_body"
|
||||
) as mock_get_response_body:
|
||||
# Setup mock for pre_call_hook
|
||||
test_body = {"name": "Azure Assistant", "model": "gpt-4o"}
|
||||
mock_proxy_logging.pre_call_hook = AsyncMock(return_value=test_body)
|
||||
|
||||
# Setup mock for http response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.headers = {"content-type": "application/json"}
|
||||
mock_response.aread = AsyncMock(return_value=b'{"id": "asst_123", "object": "assistant"}')
|
||||
mock_response.text = '{"id": "asst_123", "object": "assistant"}'
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
# Mock the HTTP request handler to capture the call
|
||||
mock_http_handler.return_value = mock_response
|
||||
|
||||
# Mock response body parser
|
||||
mock_get_response_body.return_value = {"id": "asst_123", "object": "assistant"}
|
||||
|
||||
# Mock headers for custom headers
|
||||
mock_processing.get_custom_headers.return_value = {}
|
||||
|
||||
# Mock success handler
|
||||
mock_success_handler.return_value = None
|
||||
|
||||
# Create mock request with query parameters (Azure API version)
|
||||
mock_request = MagicMock(spec=Request)
|
||||
mock_request.method = "POST"
|
||||
mock_request.url = "http://localhost:4000/azure-assistant/openai/assistants"
|
||||
mock_request.body = AsyncMock(return_value=json.dumps(test_body).encode())
|
||||
mock_request.headers = Headers({"Content-Type": "application/json"})
|
||||
|
||||
# Create QueryParams with api-version parameter
|
||||
mock_request.query_params = QueryParams([("api-version", "2025-01-01-preview")])
|
||||
|
||||
# Create mock user API key dict
|
||||
mock_user_api_key_dict = MagicMock()
|
||||
mock_user_api_key_dict.api_key = "sk-1234"
|
||||
|
||||
# Call pass_through_request
|
||||
result = await pass_through_request(
|
||||
request=mock_request,
|
||||
target="https://krris-m2f9a9i7-eastus2.openai.azure.com/openai/assistants",
|
||||
custom_headers={"Authorization": "Bearer azure_token"},
|
||||
user_api_key_dict=mock_user_api_key_dict,
|
||||
)
|
||||
|
||||
# Verify the HTTP handler was called
|
||||
mock_http_handler.assert_called_once()
|
||||
|
||||
# Extract the call arguments to verify query parameters were passed
|
||||
call_kwargs = mock_http_handler.call_args[1]
|
||||
|
||||
# The key assertion: query parameters should be preserved and passed to the HTTP handler
|
||||
assert "requested_query_params" in call_kwargs
|
||||
assert call_kwargs["requested_query_params"] == {"api-version": "2025-01-01-preview"}
|
||||
|
||||
# Verify the target URL is correct
|
||||
assert str(call_kwargs["url"]) == "https://krris-m2f9a9i7-eastus2.openai.azure.com/openai/assistants"
|
||||
|
||||
# Verify the request body is preserved
|
||||
assert call_kwargs["_parsed_body"] == test_body
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pass_through_with_httpbin_redirect():
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user