Files
autoreg-skills-max/skills/lobehub-pool/scripts/lobehub_proxy_v2.py

224 lines
7.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""LobeHub Proxy v2 — OpenAI-compatible API using lh CLI. 601 accounts, token rotation, :20140"""
import json, time, subprocess, threading, os, sys
from http.server import HTTPServer, BaseHTTPRequestHandler
from pathlib import Path
from datetime import datetime
from urllib.parse import urlparse
ACCOUNTS_FILE = Path(r"C:\Users\User\lobehub_accounts.json")
PORT = 20140
MODELS = [
"openai/gpt-5.5", "openai/gpt-5.4", "openai/gpt-5.4-mini", "openai/gpt-5.3-codex-spark",
"anthropic/claude-opus-4.8", "anthropic/claude-opus-4.7", "anthropic/claude-opus-4.6", "anthropic/claude-opus-4.5",
"anthropic/claude-sonnet-5", "anthropic/claude-sonnet-4.6", "anthropic/claude-sonnet-4.5", "anthropic/claude-haiku-4.5",
"deepseek/deepseek-v4-pro", "deepseek/deepseek-v4-flash",
"z-ai/glm-5.2", "z-ai/glm-5.1",
"moonshotai/kimi-k2.7-code", "moonshotai/kimi-k2.6",
]
pool_lock = threading.Lock()
accounts = []
account_index = 0
stats = {"total_requests": 0, "errors": 0, "started": datetime.now().isoformat()}
def init():
global accounts
with open(ACCOUNTS_FILE) as f:
raw = json.load(f)
with pool_lock:
accounts = [{
"email": a["email"],
"token": a.get("token", ""),
"used": 0,
"last_used": None,
"errors": 0,
"cooldown_until": 0
} for a in raw]
print(f"[Pool] {len(accounts)} accounts loaded")
def get_next_account():
global account_index
with pool_lock:
now = time.time()
for _ in range(len(accounts)):
acc = accounts[account_index]
account_index = (account_index + 1) % len(accounts)
if acc["cooldown_until"] < now:
acc["used"] += 1
acc["last_used"] = datetime.now().isoformat()
return acc
time.sleep(2)
acc = accounts[0]
acc["used"] += 1
acc["last_used"] = datetime.now().isoformat()
return acc
def cooldown_account(email, seconds=30):
with pool_lock:
for a in accounts:
if a["email"] == email:
a["cooldown_until"] = time.time() + seconds
a["errors"] += 1
def generate(model, messages, max_tokens=4096, temperature=0.7):
system = ""
user_prompt = ""
for m in messages:
if m["role"] == "system":
system = m["content"]
elif m["role"] == "user":
user_prompt = m["content"]
if not user_prompt:
user_prompt = " ".join(m["content"] for m in messages if m["role"] != "system")
acc = get_next_account()
token = acc["token"]
env = os.environ.copy()
env["LOBEHUB_JWT"] = token
# Use node directly to avoid Windows cmd/batch issues
NODE = r"C:\Users\User\.openagents\nodejs\node"
CLI = r"C:\Users\User\AppData\Roaming\npm\node_modules\@lobehub\cli\dist\index.js"
args = [NODE, CLI, "generate", "text", user_prompt, "--model", model,
"--max-tokens", str(max_tokens), "--json"]
if system:
args.extend(["--system", system])
if temperature != 0.7:
args.extend(["--temperature", str(temperature)])
try:
proc = subprocess.run(args, capture_output=True, text=True, timeout=120, env=env)
if proc.returncode != 0:
stderr = proc.stderr.strip()
if "rate" in stderr.lower() or "429" in stderr:
cooldown_account(acc["email"], 60)
elif "unauthorized" in stderr.lower() or "401" in stderr:
cooldown_account(acc["email"], 300)
else:
cooldown_account(acc["email"], 30)
raise Exception(f"CLI error: {stderr[:200]}")
output = proc.stdout.strip()
if not output:
raise Exception("Empty CLI output")
result = json.loads(output)
with pool_lock:
stats["total_requests"] += 1
return {
"id": result.get("id", f"chatcmpl-{int(time.time())}"),
"object": "chat.completion",
"created": int(time.time()),
"model": model,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": result["content"][0]["text"] if "content" in result else result.get("text", "")
},
"finish_reason": result.get("stop_reason", "stop")
}],
"usage": {
"prompt_tokens": result.get("usage", {}).get("input_tokens", 0),
"completion_tokens": result.get("usage", {}).get("output_tokens", 0),
"total_tokens": (result.get("usage", {}).get("input_tokens", 0) +
result.get("usage", {}).get("output_tokens", 0))
}
}
except json.JSONDecodeError:
cooldown_account(acc["email"], 30)
raise Exception("JSON parse error")
except Exception as e:
with pool_lock:
stats["errors"] += 1
raise
class LobeHubHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass
def _send_json(self, data, status=200):
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(json.dumps(data).encode())
def _send_error(self, msg, status=500):
self._send_json({"error": {"message": msg, "type": "server_error"}}, status)
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 do_GET(self):
path = urlparse(self.path).path
if path == "/v1/models":
models_list = [{"id": m, "object": "model", "created": 0, "owned_by": "lobehub"} for m in MODELS]
self._send_json({"object": "list", "data": models_list})
elif path == "/health":
self._send_json({"status": "ok", "accounts": len(accounts), "stats": stats})
elif path == "/stats":
self._send_json(stats)
else:
self._send_error("Not found", 404)
def do_POST(self):
path = urlparse(self.path).path
if path != "/v1/chat/completions":
self._send_error("Not found", 404)
return
try:
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length)
data = json.loads(body)
except:
self._send_error("Invalid JSON", 400)
return
model = data.get("model", "anthropic/claude-opus-4.8")
messages = data.get("messages", [])
max_tokens = data.get("max_tokens", 4096)
temperature = data.get("temperature", 0.7)
if not messages:
self._send_error("No messages provided", 400)
return
try:
result = generate(model, messages, max_tokens, temperature)
self._send_json(result)
except Exception as e:
self._send_error(str(e), 500)
# Add npm bin to PATH (hardcoded - Windows)
NPM_BIN = r"C:\Users\User\AppData\Roaming\npm"
if NPM_BIN not in os.environ.get("PATH", ""):
os.environ["PATH"] = NPM_BIN + os.pathsep + os.environ.get("PATH", "")
os.environ["Path"] = NPM_BIN + os.pathsep + os.environ.get("Path", "")
if __name__ == "__main__":
print(f"[LobeHub Proxy v2] Starting on :{PORT}")
init()
server = HTTPServer(("0.0.0.0", PORT), LobeHubHandler)
print(f"[LobeHub Proxy v2] Ready - {len(accounts)} accounts, {len(MODELS)} models")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\n[LobeHub Proxy v2] Shutting down")
server.shutdown()