-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunction_app.py
More file actions
126 lines (100 loc) · 4.03 KB
/
function_app.py
File metadata and controls
126 lines (100 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import json
import os
import azure.functions as func
import httpx
from azure.identity import DefaultAzureCredential
app = func.FunctionApp()
BASE = os.environ["AZURE_OPENAI_BASE"].rstrip("/")
DEPLOYMENT = os.environ["AZURE_OPENAI_DEPLOYMENT"]
API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION", "2024-05-01-preview")
V1_API_VERSION = os.getenv("AZURE_OPENAI_V1_API_VERSION", "preview")
MODEL_MAP = json.loads(os.getenv("AZURE_OPENAI_MODEL_MAP", "{}"))
DEFAULT_MODEL_ALIAS = os.getenv("AZURE_OPENAI_DEFAULT_MODEL_ALIAS")
SCOPE = "https://cognitiveservices.azure.com/.default"
def _resolve_deployment(payload: dict) -> tuple[str, str | None]:
"""
Resolve incoming OpenAI `model` alias to an Azure deployment name.
Backward compatibility:
- If no model map is configured, use AZURE_OPENAI_DEPLOYMENT.
- If model is omitted, use the default deployment.
"""
requested_model = payload.get("model")
if not MODEL_MAP:
return DEPLOYMENT, requested_model
if requested_model in MODEL_MAP:
return MODEL_MAP[requested_model], requested_model
if requested_model and requested_model not in MODEL_MAP:
raise ValueError(f"Unsupported model '{requested_model}'")
if DEFAULT_MODEL_ALIAS and DEFAULT_MODEL_ALIAS in MODEL_MAP:
return MODEL_MAP[DEFAULT_MODEL_ALIAS], DEFAULT_MODEL_ALIAS
# Final fallback preserves old single-deployment behavior.
return DEPLOYMENT, requested_model
@app.route(route="v1/chat/completions", methods=["POST"], auth_level=func.AuthLevel.FUNCTION)
def chat_completions(req: func.HttpRequest) -> func.HttpResponse:
try:
payload = req.get_json()
except ValueError:
return func.HttpResponse(
json.dumps({"error": "Invalid JSON"}),
status_code=400,
mimetype="application/json",
)
try:
deployment, requested_model = _resolve_deployment(payload)
except ValueError as e:
return func.HttpResponse(
json.dumps({"error": str(e)}),
status_code=400,
mimetype="application/json",
)
# Keep model in payload stable for clients that expect echoing.
if requested_model:
payload["model"] = requested_model
token = DefaultAzureCredential().get_token(SCOPE).token
backend_url = f"{BASE}/openai/deployments/{deployment}/chat/completions"
params = {"api-version": API_VERSION}
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
with httpx.Client(timeout=60.0) as client:
r = client.post(backend_url, params=params, headers=headers, json=payload)
return func.HttpResponse(
r.content,
status_code=r.status_code,
mimetype=r.headers.get("content-type", "application/json"),
)
@app.route(route="v1/responses", methods=["POST"], auth_level=func.AuthLevel.FUNCTION)
def responses(req: func.HttpRequest) -> func.HttpResponse:
try:
payload = req.get_json()
except ValueError:
return func.HttpResponse(
json.dumps({"error": "Invalid JSON"}),
status_code=400,
mimetype="application/json",
)
try:
deployment, requested_model = _resolve_deployment(payload)
except ValueError as e:
return func.HttpResponse(
json.dumps({"error": str(e)}),
status_code=400,
mimetype="application/json",
)
# Azure OpenAI responses API expects the deployment in `model`.
payload["model"] = deployment
token = DefaultAzureCredential().get_token(SCOPE).token
backend_url = f"{BASE}/openai/v1/responses"
params = {"api-version": V1_API_VERSION}
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
with httpx.Client(timeout=60.0) as client:
r = client.post(backend_url, params=params, headers=headers, json=payload)
return func.HttpResponse(
r.content,
status_code=r.status_code,
mimetype=r.headers.get("content-type", "application/json"),
)