Files

141 lines
5.0 KiB
Python

"""
OpenAI-compatible proxy on localhost:8080
Backend: dashscope deepseek-v4-pro (HERMES_DASHSCOPE_API_KEY)
Auth: Bearer token check
Usage:
python proxy_handler.py
Endpoints:
GET /v1/models — list available models
POST /v1/chat/completions — forward to dashscope (streaming supported)
GET /health — health check (no auth needed)
"""
import json, sys, os, time, uuid, ssl
from pathlib import Path
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.request, urllib.error
PORT = 8080
TOKEN = [REDACTED]"PROXY_TOKEN", "your-token-here")
BACKENDS = [
{
"url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
"key": os.environ.get("HERMES_DASHSCOPE_API_KEY", ""),
"model": "deepseek-v4-pro"
},
{
"url": "https://api.openai.com/v1/chat/completions",
"key": os.environ.get("OPENAI_API_KEY", ""),
"model": "gpt-4o"
}
]
class ProxyHandler(BaseHTTPRequestHandler):
def do_GET(self): self.proxy()
def do_POST(self): self.proxy()
def do_OPTIONS(self):
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')
self.send_header('Access-Control-Allow-Headers', '*')
self.end_headers()
def proxy(self):
auth = self.headers.get('Authorization', '')
if auth != f'Bearer {TOKEN}':
self.send_response(401); self.end_headers()
self.wfile.write(b'{"error":"Unauthorized"}'); return
path = self.path
if path == '/v1/models':
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps({
"object": "list",
"data": [{"id": b['model'], "object": "model", "owned_by": "proxy"}
for b in BACKENDS if b['key']]
}).encode())
return
if path == '/v1/chat/completions':
self.handle_chat(); return
if path == '/health':
self.send_response(200); self.end_headers()
self.wfile.write(b'{"status":"ok"}'); return
self.send_response(404); self.end_headers()
self.wfile.write(b'{"error":"Not found"}')
def handle_chat(self):
cl = int(self.headers.get('Content-Length', 0))
body = json.loads(self.rfile.read(cl))
stream = body.get('stream', False)
# Find first working backend
backend = None
for b in BACKENDS:
if b['key']:
backend = b; break
if not backend:
self.send_response(502); self.end_headers()
self.wfile.write(b'{"error":"No backend configured"}'); return
data = {
"model": backend['model'],
"messages": body.get('messages', []),
"stream": stream,
"temperature": body.get('temperature', 0.7),
"max_tokens": body.get('max_tokens', 4096)
}
try:
ctx = ssl.create_default_context()
req = urllib.request.Request(
backend['url'],
data=json.dumps(data).encode(),
method='POST'
)
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', f'Bearer {backend["key"]}')
if stream:
self.send_response(200)
self.send_header('Content-Type', 'text/event-stream')
self.send_header('Cache-Control', 'no-cache')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
resp = urllib.request.urlopen(req, timeout=120, context=ctx)
for line_bytes in resp:
self.wfile.write(line_bytes); self.wfile.flush()
else:
resp = urllib.request.urlopen(req, timeout=120, context=ctx)
rb = resp.read()
self.send_response(resp.status)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(rb)
except urllib.error.HTTPError as e:
self.send_response(e.code)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(e.read())
except Exception as e:
self.send_response(500); self.end_headers()
self.wfile.write(json.dumps({"error": str(e)}).encode())
def start_proxy():
print(f"\n[PROXY] http://127.0.0.1:{PORT}")
print(f"[PROXY] Auth: Bearer {TOKEN[:10]}...")
print(f"[PROXY] Backends: {[b['model'] for b in BACKENDS if b['key']]}")
HTTPServer(('0.0.0.0', PORT), ProxyHandler).serve_forever()
if __name__ == '__main__':
start_proxy()