136 lines
6.3 KiB
Python
136 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""LobeHub Proxy — CLI-based, 601 accounts, rotation"""
|
|
import json, time, random, threading, subprocess, os
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
BASE_URL = "http://[REDACTED-IP]:3210"
|
|
ACCOUNTS_FILE = Path(r"C:\Users\User\lobehub_accounts.json")
|
|
CLI = r"C:\Users\User\AppData\Roaming\npm\node_modules\@lobehub\cli\dist\index.js"
|
|
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",
|
|
"qwen/qwen3.7-plus","qwen/qwen3.7-max",
|
|
]
|
|
|
|
pool_lock = threading.Lock()
|
|
accounts = []
|
|
account_index = 0
|
|
stats = {"total_requests": 0, "errors": 0, "started": datetime.now().isoformat()}
|
|
|
|
def load_accounts():
|
|
global accounts
|
|
with open(ACCOUNTS_FILE) as f:
|
|
raw = json.load(f)
|
|
with pool_lock:
|
|
accounts = [{"email": a["email"], "password": a["password"], "token": a.get("token",""),
|
|
"valid": bool(a.get("token","")), "last_used": None, "request_count": 0, "errors": 0} for a in raw]
|
|
print(f"[Pool] {len(accounts)} accounts, {sum(1 for a in accounts if a['valid'])} with tokens")
|
|
|
|
def generate_text(model, messages, max_tokens, token):
|
|
"""LobeHub CLI subprocess"""
|
|
prompt = messages[-1]["content"] if messages else ""
|
|
system = next((m["content"] for m in messages if m["role"] == "system"), "")
|
|
|
|
env = os.environ.copy()
|
|
env["LOBEHUB_JWT"] = token
|
|
env["LOBEHUB_SERVER"] = BASE_URL
|
|
env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
|
|
|
|
args = ["node", CLI, "generate", "text", prompt, "--model", model, "--max-tokens", str(max_tokens), "--stream"]
|
|
if system: args.extend(["--system", system])
|
|
|
|
try:
|
|
proc = subprocess.run(args, capture_output=True, text=True, timeout=120, env=env)
|
|
output = proc.stdout or proc.stderr or ""
|
|
result = ""
|
|
for line in output.split("\n"):
|
|
line = line.strip()
|
|
if not line: continue
|
|
if line.startswith("in_progress"): result += line[len("in_progress"):]
|
|
elif line.startswith("data: "): result += line[6:]
|
|
elif line in ("message_stop","end_turn","message_start","text_delta"): pass
|
|
elif line.startswith(("error_","id:","[","message_","end_")): pass
|
|
elif line == "undefined": pass
|
|
else: result += line
|
|
result = result.strip()
|
|
for a in ["message_stop","end_turn","message_start","text_delta"]: result = result.replace(a,"")
|
|
if result: return {"content": result.strip(), "success": True}
|
|
return {"error": output[:100], "success": False}
|
|
except subprocess.TimeoutExpired: return {"error": "Timeout", "success": False}
|
|
except Exception as e: return {"error": str(e), "success": False}
|
|
|
|
def get_next_account():
|
|
global account_index
|
|
with pool_lock:
|
|
valid = [a for a in accounts if a["valid"]]
|
|
if not valid: return None
|
|
for _ in range(min(20, len(accounts))):
|
|
account_index = (account_index + 1) % len(accounts)
|
|
if accounts[account_index]["valid"]:
|
|
accounts[account_index]["last_used"] = datetime.now().isoformat()
|
|
accounts[account_index]["request_count"] += 1
|
|
return accounts[account_index]
|
|
acc = random.choice(valid)
|
|
acc["last_used"] = datetime.now().isoformat()
|
|
acc["request_count"] += 1
|
|
return acc
|
|
|
|
class Proxy(BaseHTTPRequestHandler):
|
|
def _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,ensure_ascii=False).encode())
|
|
|
|
def _auth(self):
|
|
return self.headers.get("Authorization","") in ("Bearer local","local")
|
|
|
|
def do_GET(self):
|
|
if self.path == "/health":
|
|
with pool_lock: valid = sum(1 for a in accounts if a["valid"])
|
|
self._json({"status":"ok","accounts":{"total":len(accounts),"valid":valid},"models":len(MODELS),"backend":"lobehub-cli","stats":stats})
|
|
elif self.path == "/v1/models":
|
|
self._json({"object":"list","data":[{"id":m,"object":"model"} for m in MODELS]})
|
|
else: self._json({"error":"not found"},404)
|
|
|
|
def do_POST(self):
|
|
if self.path != "/v1/chat/completions": self._json({"error":"not found"},404); return
|
|
if not self._auth(): self._json({"error":"Unauthorized"},401); return
|
|
|
|
stats["total_requests"] += 1
|
|
try: body = json.loads(self.rfile.read(int(self.headers.get("Content-Length",0))))
|
|
except: self._json({"error":"Invalid JSON"},400); return
|
|
|
|
model = body.get("model","openai/gpt-5.5")
|
|
messages = body.get("messages",[])
|
|
if not messages: self._json({"error":"messages required"},400); return
|
|
|
|
acc = get_next_account()
|
|
if not acc: self._json({"error":"No valid accounts"},503); return
|
|
|
|
result = generate_text(model, messages, body.get("max_tokens",4096), acc["token"])
|
|
if result["success"]:
|
|
self._json({
|
|
"id":f"lobehub-{int(time.time())}","object":"chat.completion","created":int(time.time()),
|
|
"model":model,"choices":[{"index":0,"message":{"role":"assistant","content":result["content"]},"finish_reason":"stop"}],
|
|
"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0},
|
|
"_pool":{"account":acc["email"][:3]+"***","engine":"lobehub-cli"}
|
|
})
|
|
else:
|
|
stats["errors"] += 1; acc["errors"] += 1
|
|
self._json({"error":result.get("error","Generation failed")},500)
|
|
|
|
def log_message(self,f,*a): pass
|
|
|
|
# === START ===
|
|
load_accounts()
|
|
print(f"[LobeHub CLI Proxy] :{PORT} | {len(accounts)} accounts | Ready")
|
|
while True:
|
|
try: HTTPServer(("127.0.0.1",PORT), Proxy).serve_forever()
|
|
except Exception as e: print(f"[Crash] {e}"); time.sleep(3) |