autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
224
skills/lobehub-pool/scripts/lobehub_proxy_v2.py
Normal file
224
skills/lobehub-pool/scripts/lobehub_proxy_v2.py
Normal file
@@ -0,0 +1,224 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user