Compare commits

...

1 Commits

Author SHA1 Message Date
9qeklajc
cb1abc7d26 draft to fix azure issue 2026-02-09 22:15:15 +01:00

View File

@@ -1,9 +1,14 @@
from typing import TYPE_CHECKING, Mapping
from fastapi import Request
from fastapi.responses import Response, StreamingResponse
from .base import BaseUpstreamProvider
if TYPE_CHECKING:
from ..core.db import UpstreamProviderRow
from ..auth import ApiKey
from ..core.db import AsyncSession, UpstreamProviderRow
from ..payment.models import Model
class AzureUpstreamProvider(BaseUpstreamProvider):
@@ -58,19 +63,101 @@ class AzureUpstreamProvider(BaseUpstreamProvider):
"platform_url": cls.platform_url,
}
def prepare_headers(self, request_headers: dict) -> dict:
"""Prepare headers for Azure OpenAI, adding api-key."""
headers = super().prepare_headers(request_headers)
if self.api_key:
headers["api-key"] = self.api_key
headers.pop("Authorization", None)
headers.pop("authorization", None)
return headers
def prepare_params(
self, path: str, query_params: Mapping[str, str] | None
) -> Mapping[str, str]:
"""Prepare query parameters for Azure OpenAI, adding API version.
Args:
path: Request path
query_params: Original query parameters from the client
Returns:
Query parameters dict with Azure API version added for chat completions
"""
"""Prepare query parameters for Azure OpenAI, adding API version."""
params = dict(query_params or {})
if path.endswith("chat/completions"):
params["api-version"] = self.api_version
# Ensure we use a valid Azure API version format
# Strip any hidden characters like Byte Order Marks (BOM) or whitespace
version = self.api_version.strip().replace("\ufeff", "")
if version == "v1":
version = "2024-02-15-preview"
params["api-version"] = version
return params
async def forward_request(
self,
request: Request,
path: str,
headers: dict,
request_body: bytes | None,
key: "ApiKey",
max_cost_for_model: int,
session: "AsyncSession",
model_obj: "Model",
) -> Response | StreamingResponse:
"""Forward request to Azure OpenAI."""
# Fix: If base_url contains /openai/v1, remove it
actual_base_url = self.base_url
if "/openai/v1" in actual_base_url:
actual_base_url = actual_base_url.split("/openai/v1")[0]
# Use canonical_slug as it often stores the deployment name in Azure setups
# otherwise fallback to transform_model_name
deployment_id = getattr(
model_obj, "canonical_slug", None
) or self.transform_model_name(model_obj.id)
# Ensure deployment_id doesn't contain a provider prefix (e.g., 'openai/' or 'azure/')
if "/" in deployment_id:
deployment_id = deployment_id.split("/")[-1]
# Azure format: openai/deployments/{deployment-id}/chat/completions
clean_path = path.lstrip("/")
azure_path = f"openai/deployments/{deployment_id}/{clean_path}"
# Temporary backup and restore base_url to use cleaned version
original_base = self.base_url
self.base_url = actual_base_url
# The query params are handled by super().forward_request via prepare_params
# We don't need to manually append them to full_url for the print if we want to be accurate
params = self.prepare_params(path, {})
full_url = (
f"{actual_base_url}/{azure_path}?api-version={params.get('api-version')}"
)
print(f"\n[DEBUG] Azure Forwarding URL: {full_url}")
print(f"[DEBUG] Deployment ID: {deployment_id}")
try:
response = await super().forward_request(
request,
azure_path,
headers,
request_body,
key,
max_cost_for_model,
session,
model_obj,
)
# Check if it's an error response to print details
if hasattr(response, "status_code") and response.status_code != 200:
print(f"[DEBUG] Azure Error Status: {response.status_code}")
if hasattr(response, "body"):
print(
f"[DEBUG] Azure Error Body: {response.body.decode() if isinstance(response.body, bytes) else response.body}"
)
return response
except Exception as e:
print(f"[DEBUG] Azure Exception: {str(e)}")
raise
finally:
self.base_url = original_base
def transform_model_name(self, model_id: str) -> str:
"""Extract deployment name from model ID."""
if "/" in model_id:
return model_id.split("/")[-1]
return model_id