Spaces:
Paused
Paused
| """molab_setup.py — Dual LLM server (DC + MC) on molab GPU. | |
| DC: Mistral-Small-3.2-24B-abliterated (chat humano, español, Discord) | |
| MC: Qwen3-14B-abliterated (agentic, tool calling, Minecraft) | |
| """ | |
| import os, sys, re, time, threading, urllib.request | |
| import torch, requests | |
| from transformers import AutoTokenizer, AutoModelForCausalLM | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| import uvicorn | |
| # Install missing deps | |
| os.system("pip install -q sentencepiece tiktoken accelerate > /tmp/pip.log 2>&1") | |
| LOG = "/tmp/setup.log" | |
| TUNNEL_URL_FILE = "/tmp/tunnel_url.txt" | |
| PORT = 8000 | |
| CF_PATH = "/usr/local/bin/cloudflared" | |
| # Modelos SIN CENSURA | |
| DC_MODEL = "huihui-ai/Huihui-Mistral-Small-3.2-24B-Instruct-2506-abliterated" | |
| MC_MODEL = "mlabonne/Qwen3-14B-abliterated" | |
| def log(msg): | |
| line = "[{}] {}".format(time.strftime("%H:%M:%S"), msg) | |
| try: | |
| with open(LOG, "a") as f: f.write(line + "\n") | |
| except: pass | |
| print(line, flush=True) | |
| open(LOG, "w").close() | |
| log("=== SETUP START ===") | |
| log("DC model: {}".format(DC_MODEL)) | |
| log("MC model: {}".format(MC_MODEL)) | |
| # 1. cloudflared | |
| if not os.path.exists(CF_PATH) or os.path.getsize(CF_PATH) < 1_000_000: | |
| log("Downloading cloudflared...") | |
| urllib.request.urlretrieve( | |
| "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64", | |
| CF_PATH) | |
| os.chmod(CF_PATH, 0o755) | |
| log("cloudflared OK") | |
| # 2. Load DC model (Mistral 24B abliterated) | |
| log("Loading DC model (Mistral 24B abliterated)...") | |
| dc_tokenizer = AutoTokenizer.from_pretrained(DC_MODEL, trust_remote_code=True) | |
| try: | |
| dc_model = AutoModelForCausalLM.from_pretrained( | |
| DC_MODEL, dtype=torch.bfloat16, trust_remote_code=True).to("cuda") | |
| except ValueError: | |
| # Mistral3 needs Mistral3ForConditionalGeneration | |
| from transformers import Mistral3ForConditionalGeneration | |
| dc_model = Mistral3ForConditionalGeneration.from_pretrained( | |
| DC_MODEL, dtype=torch.bfloat16, trust_remote_code=True).to("cuda") | |
| log("DC model loaded! VRAM: {:.2f} GB".format(torch.cuda.memory_allocated()/1024**3)) | |
| # 3. Load MC model (Qwen3 14B abliterated) | |
| log("Loading MC model (Qwen3 14B abliterated)...") | |
| mc_tokenizer = AutoTokenizer.from_pretrained(MC_MODEL, trust_remote_code=True) | |
| mc_model = AutoModelForCausalLM.from_pretrained( | |
| MC_MODEL, dtype=torch.bfloat16, trust_remote_code=True).to("cuda") | |
| log("MC model loaded! VRAM: {:.2f} GB".format(torch.cuda.memory_allocated()/1024**3)) | |
| # 4. FastAPI | |
| app = FastAPI(title="Zelin Dual LLM API") | |
| app.add_middleware(CORSMiddleware, | |
| allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) | |
| class ChatReq(BaseModel): | |
| message: str | |
| max_tokens: int = 200 | |
| temperature: float = 0.7 | |
| model: str = "dc" # "dc" or "mc" | |
| def root(): | |
| return { | |
| "status": "ok", | |
| "dc_model": "Mistral-Small-3.2-24B-abliterated", | |
| "mc_model": "Qwen3-14B-abliterated", | |
| "vram_gb": round(torch.cuda.memory_allocated()/1024**3, 2), | |
| } | |
| def health(): | |
| return { | |
| "status": "healthy", | |
| "vram_gb": round(torch.cuda.memory_allocated()/1024**3, 2), | |
| "vram_free_gb": round(torch.cuda.mem_get_info()[0]/1024**3, 2), | |
| } | |
| def generate_response(model, tokenizer, message, max_tokens, temperature): | |
| # Máximo contexto posible: 16K tokens (deja margen seguro) | |
| inputs = tokenizer(message, return_tensors="pt", truncation=True, max_length=16384).to("cuda") | |
| t0 = time.time() | |
| with torch.no_grad(): | |
| out = model.generate(**inputs, | |
| max_new_tokens=max_tokens, | |
| temperature=temperature, | |
| do_sample=True, | |
| pad_token_id=tokenizer.eos_token_id, | |
| max_length=16384 + max_tokens) # context + generation | |
| elapsed = time.time() - t0 | |
| resp = tokenizer.decode( | |
| out[0][inputs["input_ids"].shape[1]:], | |
| skip_special_tokens=True) | |
| tokens = out.shape[1] - inputs["input_ids"].shape[1] | |
| return { | |
| "response": resp, | |
| "tokens": tokens, | |
| "elapsed_s": round(elapsed, 2), | |
| "tokens_per_second": round(tokens/elapsed, 1) if elapsed > 0 else 0, | |
| } | |
| def chat(req: ChatReq): | |
| if req.model == "mc": | |
| result = generate_response(mc_model, mc_tokenizer, req.message, req.max_tokens, req.temperature) | |
| result["model"] = "Qwen3-14B-abliterated" | |
| else: | |
| result = generate_response(dc_model, dc_tokenizer, req.message, req.max_tokens, req.temperature) | |
| result["model"] = "Mistral-Small-3.2-24B-abliterated" | |
| return result | |
| def dc_chat(req: ChatReq): | |
| result = generate_response(dc_model, dc_tokenizer, req.message, req.max_tokens, req.temperature) | |
| result["model"] = "Mistral-Small-3.2-24B-abliterated" | |
| return result | |
| def mc_chat(req: ChatReq): | |
| result = generate_response(mc_model, mc_tokenizer, req.message, req.max_tokens, req.temperature) | |
| result["model"] = "Qwen3-14B-abliterated" | |
| return result | |
| # 5. Server background | |
| def run_server(): | |
| uvicorn.run(app, host="0.0.0.0", port=PORT, log_level="error") | |
| threading.Thread(target=run_server, daemon=True).start() | |
| time.sleep(3) | |
| log("FastAPI on port {}".format(PORT)) | |
| # 6. Tunnel | |
| log("Starting tunnel...") | |
| os.system("{} tunnel --url http://localhost:{} --no-autoupdate > /tmp/tunnel.log 2>&1 &".format(CF_PATH, PORT)) | |
| tunnel_url = None | |
| for _ in range(15): | |
| time.sleep(2) | |
| try: | |
| l = open("/tmp/tunnel.log").read() | |
| m = re.search(r'https://[a-z0-9-]+\.trycloudflare\.com', l) | |
| if m: | |
| tunnel_url = m.group(0) | |
| break | |
| except: pass | |
| # 7. Keep-alive | |
| def keep_alive(): | |
| while True: | |
| try: requests.get("http://localhost:{}/health".format(PORT), timeout=5) | |
| except: pass | |
| time.sleep(60) | |
| threading.Thread(target=keep_alive, daemon=True).start() | |
| log("Keep-alive active") | |
| # 8. Test both models | |
| if tunnel_url: | |
| log("TUNNEL_URL: {}".format(tunnel_url)) | |
| with open(TUNNEL_URL_FILE, "w") as f: | |
| f.write(tunnel_url) | |
| try: | |
| # Health check | |
| r = requests.get("{}/health".format(tunnel_url), timeout=15) | |
| log("Health: {}".format(r.json())) | |
| # Test DC model | |
| log("Testing DC model...") | |
| r2 = requests.post("{}/dc".format(tunnel_url), | |
| json={"message": "Hola! Eres Zelin? Como estas?"}, timeout=60) | |
| d = r2.json() | |
| log("DC: {} tok/s".format(d.get("tokens_per_second", "?"))) | |
| log("DC Response: {}".format(d.get("response", "")[:200])) | |
| # Test MC model | |
| log("Testing MC model...") | |
| r3 = requests.post("{}/mc".format(tunnel_url), | |
| json={"message": "How do I craft a diamond sword in Minecraft?"}, timeout=60) | |
| d2 = r3.json() | |
| log("MC: {} tok/s".format(d2.get("tokens_per_second", "?"))) | |
| log("MC Response: {}".format(d2.get("response", "")[:200])) | |
| except Exception as e: | |
| log("Test error: {}".format(e)) | |
| else: | |
| log("Tunnel not ready") | |
| log("=== SETUP DONE ===") | |