87 lines
3.0 KiB
Python
Executable File
87 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""龙虾 simpleapi 本地代理:把 model=auto 改写为 gpt-5.4,避免 /responses 502。"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from urllib.error import HTTPError, URLError
|
|
from urllib.request import Request, urlopen
|
|
|
|
UPSTREAM = "https://simpleapi.quwanzhi.com"
|
|
LISTEN_HOST = "127.0.0.1"
|
|
LISTEN_PORT = 63849
|
|
FALLBACK_MODEL = "gpt-5.4"
|
|
REWRITE_MODELS = {"auto", ""}
|
|
|
|
|
|
def rewrite_body(raw: bytes) -> bytes:
|
|
try:
|
|
data = json.loads(raw.decode("utf-8"))
|
|
except Exception:
|
|
return raw
|
|
if not isinstance(data, dict):
|
|
return raw
|
|
model = str(data.get("model", "") or "").strip()
|
|
if model in REWRITE_MODELS:
|
|
data["model"] = FALLBACK_MODEL
|
|
return json.dumps(data, ensure_ascii=False).encode("utf-8")
|
|
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
protocol_version = "HTTP/1.1"
|
|
|
|
def log_message(self, fmt: str, *args) -> None:
|
|
sys.stderr.write(f"[simpleapi-proxy] {self.address_string()} {fmt % args}\n")
|
|
|
|
def do_GET(self) -> None:
|
|
self._proxy()
|
|
|
|
def do_POST(self) -> None:
|
|
self._proxy()
|
|
|
|
def _proxy(self) -> None:
|
|
length = int(self.headers.get("Content-Length", "0") or 0)
|
|
body = self.rfile.read(length) if length else b""
|
|
if self.command == "POST" and body:
|
|
body = rewrite_body(body)
|
|
|
|
headers = {k: v for k, v in self.headers.items() if k.lower() not in {"host", "content-length", "transfer-encoding"}}
|
|
url = f"{UPSTREAM}{self.path}"
|
|
req = Request(url, data=body if self.command == "POST" else None, headers=headers, method=self.command)
|
|
|
|
try:
|
|
with urlopen(req, timeout=120) as resp:
|
|
data = resp.read()
|
|
self.send_response(resp.status)
|
|
for k, v in resp.headers.items():
|
|
if k.lower() not in {"transfer-encoding", "connection"}:
|
|
self.send_header(k, v)
|
|
self.send_header("Content-Length", str(len(data)))
|
|
self.end_headers()
|
|
self.wfile.write(data)
|
|
except HTTPError as e:
|
|
err = e.read()
|
|
self.send_response(e.code)
|
|
self.send_header("Content-Type", e.headers.get("Content-Type", "application/json"))
|
|
self.send_header("Content-Length", str(len(err)))
|
|
self.end_headers()
|
|
self.wfile.write(err)
|
|
except URLError as e:
|
|
msg = json.dumps({"error": {"message": str(e.reason), "type": "proxy_error"}}).encode()
|
|
self.send_response(502)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.send_header("Content-Length", str(len(msg)))
|
|
self.end_headers()
|
|
self.wfile.write(msg)
|
|
|
|
|
|
def main() -> None:
|
|
server = ThreadingHTTPServer((LISTEN_HOST, LISTEN_PORT), Handler)
|
|
print(f"[simpleapi-proxy] listening http://{LISTEN_HOST}:{LISTEN_PORT} -> {UPSTREAM}", flush=True)
|
|
server.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|