[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:
Ishaan Jaff
2025-09-30 15:01:38 -07:00
committed by GitHub
parent 69a464fc97
commit 0476a33d9f
4 changed files with 125 additions and 4 deletions
@@ -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():
"""