modelbeast/server/operators/llm_local/run.py
type-two 110b2298b7 audio/video/text operator suite: stt_local, lipsync_local, voice_clone_local, wan_video, llm_local
- stt_local: Whisper large-v3-turbo MLX, transcript + timestamped json
- lipsync_local: Rhubarb visemes (CPU lane), dialog-guided
- voice_clone_local: Chatterbox on MPS (needs setuptools<81 for perth/pkg_resources)
- wan_video: Wan 2.2 TI2V-5B t2v/i2v via resident ComfyUI, frames->mp4
- llm_local: Qwen3-30B-A3B-4bit dialogue writer (primary-only)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:26:51 +10:00

44 lines
1.4 KiB
Python

import argparse
import json
import re
import sys
import time
from pathlib import Path
ap = argparse.ArgumentParser()
ap.add_argument("--input", action="append", default=[])
ap.add_argument("--outdir", required=True)
ap.add_argument("--params", default="{}")
a = ap.parse_args()
p = json.loads(a.params)
prompt = (p.get("prompt") or "").strip()
if not prompt:
print("ERROR: prompt is required")
sys.exit(1)
from mlx_lm import load, generate # noqa: E402
from mlx_lm.sample_utils import make_sampler # noqa: E402
MODEL = "mlx-community/Qwen3-30B-A3B-4bit"
t0 = time.time()
model, tokenizer = load(MODEL)
print(f"model loaded in {time.time() - t0:.1f}s", flush=True)
messages = []
if (p.get("system") or "").strip():
messages.append({"role": "system", "content": p["system"].strip()})
messages.append({"role": "user", "content": prompt})
chat = tokenizer.apply_chat_template(
messages, add_generation_prompt=True, tokenize=False, enable_thinking=False)
t1 = time.time()
text = generate(model, tokenizer, prompt=chat,
max_tokens=int(p.get("max_tokens", 1024)),
sampler=make_sampler(temp=float(p.get("temperature", 0.7))))
text = re.sub(r"<think>.*?</think>", "", text, flags=re.S).strip()
out = Path(a.outdir) / "llm_output.txt"
out.write_text(text + "\n")
print(f"done in {time.time() - t1:.1f}s, {len(text)} chars:\n{text[:400]}", flush=True)