GitHub Action commited on
Commit
af0d253
·
1 Parent(s): ca4cde9

Auto deploy from GitHub 2026-05-11 13:39:00

Browse files
Files changed (1) hide show
  1. src/openai_fallback_proxy.py +51 -2
src/openai_fallback_proxy.py CHANGED
@@ -28,6 +28,10 @@ def is_retryable(status_code: int) -> bool:
28
  return status_code in {408, 409, 425, 429, 500, 502, 503, 504}
29
 
30
 
 
 
 
 
31
  def build_headers(api_key: str, extra: Optional[Dict[str, str]] = None) -> Dict[str, str]:
32
  headers = {"Content-Type": "application/json"}
33
  if api_key:
@@ -45,6 +49,16 @@ def create_upstream_response(
45
  extra_headers: Optional[Dict[str, str]] = None,
46
  ) -> requests.Response:
47
  request_payload = dict(payload)
 
 
 
 
 
 
 
 
 
 
48
  request_payload["model"] = model_override
49
  return requests.post(
50
  f"{upstream_base}/chat/completions",
@@ -96,6 +110,14 @@ class Handler(BaseHTTPRequestHandler):
96
  else:
97
  self.wfile.write(response.content)
98
 
 
 
 
 
 
 
 
 
99
  def do_GET(self) -> None:
100
  if self.path == "/health":
101
  self._send_json(
@@ -108,7 +130,22 @@ class Handler(BaseHTTPRequestHandler):
108
  )
109
  return
110
 
111
- if self.path == "/v1/models":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  self._send_json(
113
  200,
114
  {
@@ -124,6 +161,18 @@ class Handler(BaseHTTPRequestHandler):
124
  )
125
  return
126
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  self._send_json(404, {"error": {"message": "Not found"}})
128
 
129
  def do_POST(self) -> None:
@@ -151,7 +200,7 @@ class Handler(BaseHTTPRequestHandler):
151
  if primary_response.status_code < 400:
152
  self._relay_response(primary_response, stream)
153
  return
154
- if not FALLBACK_API_KEY or not is_retryable(primary_response.status_code):
155
  self._relay_response(primary_response, False)
156
  return
157
  except requests.RequestException as error:
 
28
  return status_code in {408, 409, 425, 429, 500, 502, 503, 504}
29
 
30
 
31
+ def should_fallback(status_code: int) -> bool:
32
+ return status_code == 400 or is_retryable(status_code)
33
+
34
+
35
  def build_headers(api_key: str, extra: Optional[Dict[str, str]] = None) -> Dict[str, str]:
36
  headers = {"Content-Type": "application/json"}
37
  if api_key:
 
49
  extra_headers: Optional[Dict[str, str]] = None,
50
  ) -> requests.Response:
51
  request_payload = dict(payload)
52
+ if isinstance(request_payload.get("messages"), list):
53
+ normalized_messages = []
54
+ for message in request_payload["messages"]:
55
+ if isinstance(message, dict) and message.get("role") == "developer":
56
+ normalized = dict(message)
57
+ normalized["role"] = "system"
58
+ normalized_messages.append(normalized)
59
+ else:
60
+ normalized_messages.append(message)
61
+ request_payload["messages"] = normalized_messages
62
  request_payload["model"] = model_override
63
  return requests.post(
64
  f"{upstream_base}/chat/completions",
 
110
  else:
111
  self.wfile.write(response.content)
112
 
113
+ def _send_plain(self, status_code: int, body: str) -> None:
114
+ payload = body.encode("utf-8")
115
+ self.send_response(status_code)
116
+ self.send_header("Content-Type", "text/plain; charset=utf-8")
117
+ self.send_header("Content-Length", str(len(payload)))
118
+ self.end_headers()
119
+ self.wfile.write(payload)
120
+
121
  def do_GET(self) -> None:
122
  if self.path == "/health":
123
  self._send_json(
 
130
  )
131
  return
132
 
133
+ if self.path in {"/version", "/v1/props", "/props"}:
134
+ self._send_json(
135
+ 200,
136
+ {
137
+ "version": "fallback-proxy",
138
+ "primary_model": PRIMARY_MODEL,
139
+ "fallback_model": FALLBACK_MODEL,
140
+ },
141
+ )
142
+ return
143
+
144
+ if self.path in {"/api/tags"}:
145
+ self._send_json(200, {"models": [{"name": PRIMARY_MODEL or FALLBACK_MODEL}]})
146
+ return
147
+
148
+ if self.path in {"/v1/models", "/api/v1/models"}:
149
  self._send_json(
150
  200,
151
  {
 
161
  )
162
  return
163
 
164
+ if self.path.startswith("/v1/models/"):
165
+ model_id = self.path.split("/v1/models/", 1)[1]
166
+ self._send_json(
167
+ 200,
168
+ {
169
+ "id": model_id or PRIMARY_MODEL or FALLBACK_MODEL,
170
+ "object": "model",
171
+ "owned_by": "hermes-local-proxy",
172
+ },
173
+ )
174
+ return
175
+
176
  self._send_json(404, {"error": {"message": "Not found"}})
177
 
178
  def do_POST(self) -> None:
 
200
  if primary_response.status_code < 400:
201
  self._relay_response(primary_response, stream)
202
  return
203
+ if not FALLBACK_API_KEY or not should_fallback(primary_response.status_code):
204
  self._relay_response(primary_response, False)
205
  return
206
  except requests.RequestException as error: