GenbaLabs's picture
card, MLX package and runtime
88a6753 verified
Raw History Blame Contribute Delete
10.5 kB
"""OpenAI/vLLM-compatible serving shim for the DiffusionGemma engine.
The published deployment is vLLM plus its structured decision server. This shim speaks the small part of vLLM's
HTTP API that server uses — chat/completions with ``vllm_xargs`` diffusion requests — so the unmodified upstream
server runs on top of the MLX engine. That keeps the decision layer (prompt, templates, canvas, slot reads,
adaptive thinking) bit-for-bit the published one instead of a re-implementation.
Diffusion xargs understood (as sent by runtime/structured_server.py):
diffusion_seed_canvas initial canvas token ids
diffusion_canvas_length canvas width
diffusion_max_steps denoising steps
diffusion_read_only return logprobs at every canvas position, do not sample
diffusion_pinned canvas positions held at their seed values
diffusion_constrained the unembedding is read over ``logprob_token_ids`` only
diffusion_samples number of draws
and ``djev_bidir`` = flat [a1, b1, a2, b2, ...] prompt ranges that attend bidirectionally (patch_vllm_bidir.py).
"""
from __future__ import annotations
import argparse
import time
from typing import Any
import mlx.core as mx
import numpy as np
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from .engine import CANVAS_LENGTH, Engine
app = FastAPI()
ENGINE: Engine | None = None
OPTS: dict[str, Any] = {"canvas": CANVAS_LENGTH, "max_prompt": 8192, "model": "super-decider"}
class ChatRequest(BaseModel):
model: str | None = None
messages: list[dict] | None = None
prompt: Any = None
max_tokens: int | None = None
logprobs: Any = None
top_logprobs: int | None = None
logprob_token_ids: list[int] | None = None
return_tokens_as_token_ids: bool = False
chat_template_kwargs: dict | None = None
vllm_xargs: dict | None = None
stop_token_ids: list[int] | None = None
seed: int | None = None
temperature: float | None = None
stream: bool = False
def _tok(t: int, as_id: bool) -> str:
return f"token_id:{t}" if as_id else ENGINE.tokenizer.decode([t])
def _block_ids(n_prompt: int, ranges) -> mx.array | None:
"""Per-token bidirectional group ids from the flat ``djev_bidir`` ranges, -1 outside them."""
if not ranges:
return None
ids = np.full(n_prompt, -1, dtype=np.int32)
for k, i in enumerate(range(0, len(ranges) - 1, 2)):
a, b = int(ranges[i]), int(ranges[i + 1])
ids[max(a, 0) : min(b, n_prompt)] = k
return mx.array(ids)
def _usage(prompt_tokens: int, completion_tokens: int) -> dict:
return {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": prompt_tokens + completion_tokens,
}
def _read(req: ChatRequest, prompt_ids: list[int]):
"""A diffusion read: label logits at every canvas position, plus the canvas tokens themselves."""
x = req.vllm_xargs or {}
canvas = [int(t) for t in x.get("diffusion_seed_canvas") or []]
canvas_len = int(x.get("diffusion_canvas_length") or OPTS["canvas"])
steps = int(x.get("diffusion_max_steps") or 1)
pinned_pos = [int(p) for p in x.get("diffusion_pinned") or []]
samples = int(x.get("diffusion_samples") or 1)
label_ids = [int(t) for t in (req.logprob_token_ids or [])]
if not canvas:
raise ValueError("a diffusion request needs diffusion_seed_canvas")
canvas = (canvas + [0] * canvas_len)[:canvas_len]
block_ids = _block_ids(len(prompt_ids), x.get("djev_bidir"))
pinned = {p: canvas[p] for p in pinned_pos if 0 <= p < canvas_len}
probs = ENGINE.read(
prompt_ids,
canvas,
list(range(canvas_len)),
label_ids,
block_ids=block_ids,
steps=steps,
pinned=pinned,
seed=int(req.seed or 0),
samples=samples,
)
return canvas, label_ids, probs
def _top_rows(probs_row: np.ndarray, label_ids, as_id: bool, k: int):
order = np.argsort(-probs_row)[: max(k, 1)]
return [
{"token": _tok(label_ids[i], as_id), "logprob": float(probs_row[i]), "bytes": None}
for i in order
]
def _read_content(canvas, label_ids, probs, as_id: bool, top_k: int):
"""One logprobs entry per canvas position (``logprobs.content`` / ``top_logprobs``)."""
rows = []
for pos, tok in enumerate(canvas):
tops = _top_rows(probs[pos], label_ids, as_id, top_k) if label_ids else []
best = tops[0]["logprob"] if tops else 0.0
rows.append(
{"token": _tok(tok, as_id), "logprob": best, "bytes": None, "top_logprobs": tops}
)
return rows
def _token_logprobs(tokens: list[int], lps, as_id: bool, chat: bool):
"""vLLM's legacy per-token logprobs object (``content`` for chat, ``tokens``/``token_logprobs`` for completions).
``top_logprobs`` stays empty: the model reads a label set, not a token ranking, so nothing here asks for it.
"""
if lps is None:
return None
vals = [float(v) for v in lps]
if chat:
return {
"content": [
{"token": _tok(t, as_id), "logprob": v, "bytes": None, "top_logprobs": []}
for t, v in zip(tokens, vals)
],
"refusal": None,
}
return {
"tokens": [_tok(t, as_id) for t in tokens],
"token_logprobs": vals,
"top_logprobs": [{} for _ in tokens],
"text_offset": list(range(len(tokens))),
}
def _generate(req: ChatRequest, prompt_ids: list[int]):
x = req.vllm_xargs or {}
want = req.logprobs is not None
out = ENGINE.generate(
prompt_ids,
max_new_tokens=req.max_tokens,
max_denoising_steps=int(x.get("diffusion_max_steps") or 0) or None,
stop_ids=req.stop_token_ids,
block_ids=_block_ids(len(prompt_ids), x.get("djev_bidir")),
token_logprobs=want,
)
gen, lps = out if want else (out, None)
if req.stop_token_ids:
for k, t in enumerate(gen):
if t in req.stop_token_ids:
gen = gen[: k + 1]
break
if req.max_tokens:
gen, lps = gen[: req.max_tokens], (lps[: req.max_tokens] if lps else lps)
return gen, lps
@app.get("/v1/models")
@app.get("/health")
def models():
return {"object": "list", "data": [{"id": OPTS["model"], "object": "model", "owned_by": "local"}]}
def _handle(req: ChatRequest, chat: bool):
if chat:
prompt_ids = ENGINE.chat_ids(
req.messages or [],
bool((req.chat_template_kwargs or {}).get("enable_thinking", False)),
)
else:
prompt_ids = [int(t) for t in (req.prompt or [])]
if len(prompt_ids) > OPTS["max_prompt"]:
# A real 400: vLLM answers 400 here, and a 200 body without choices makes the
# decision layer raise KeyError('choices') instead of reporting the length.
return JSONResponse(
status_code=400,
content={
"error": {
"code": 400,
"message": f"state too long: {len(prompt_ids)} tokens, this build serves "
f"{OPTS['max_prompt']} (raise --max-prompt; attention cost grows quadratically)",
"type": "BadRequestError",
}
},
)
as_id = bool(req.return_tokens_as_token_ids)
diffusion = bool((req.vllm_xargs or {}).get("diffusion_seed_canvas"))
created = int(time.time())
if diffusion:
canvas, label_ids, probs = _read(req, prompt_ids)
# One choice per draw, the way vLLM answers a multi-sample request.
choices = []
for k in range(probs.shape[0]):
content = _read_content(canvas, label_ids, probs[k], as_id, int(req.top_logprobs or 20))
choice = {
"index": k,
"text": "",
"finish_reason": "stop",
"logprobs": {
"tokens": [c["token"] for c in content],
"token_logprobs": [c["logprob"] for c in content],
"top_logprobs": [{t["token"]: t["logprob"] for t in c["top_logprobs"]} for c in content],
"text_offset": list(range(len(content))),
},
}
if chat:
choice["message"] = {"role": "assistant", "content": ""}
choice["logprobs"] = {"content": content, "refusal": None}
choices.append(choice)
body = {
"id": "djev-local",
"object": "chat.completion" if chat else "text_completion",
"created": created,
"model": OPTS["model"],
"choices": choices,
"usage": _usage(len(prompt_ids), len(canvas)),
"system_fingerprint": "super-decider-metal",
}
return body
gen, lps = _generate(req, prompt_ids)
text = ENGINE.tokenizer.decode(gen)
choice = {"index": 0, "text": text, "finish_reason": "stop"}
if chat:
choice["message"] = {"role": "assistant", "content": text}
choice["logprobs"] = _token_logprobs(gen, lps, as_id, chat)
body = {
"id": "djev-local",
"object": "chat.completion" if chat else "text_completion",
"created": created,
"model": OPTS["model"],
"choices": [choice],
"usage": _usage(len(prompt_ids), len(gen)),
"system_fingerprint": "super-decider-metal",
}
return body
@app.post("/v1/chat/completions")
async def chat_completions(req: ChatRequest):
return _handle(req, chat=True)
@app.post("/v1/completions")
async def completions(req: ChatRequest):
return _handle(req, chat=False)
def main():
global ENGINE
ap = argparse.ArgumentParser(description="super-decider-metal engine server (vLLM-compatible subset)")
ap.add_argument("--model", default="artifacts/mlx-nvfp4")
ap.add_argument("--name", default="super-decider")
ap.add_argument("--host", default="127.0.0.1")
ap.add_argument("--port", type=int, default=8000)
ap.add_argument("--canvas", type=int, default=CANVAS_LENGTH)
ap.add_argument("--max-prompt", type=int, default=8192)
args = ap.parse_args()
OPTS.update(canvas=args.canvas, max_prompt=args.max_prompt, model=args.name)
ENGINE = Engine(args.model, canvas_length=args.canvas)
import uvicorn
uvicorn.run(app, host=args.host, port=args.port, log_level="warning")
if __name__ == "__main__":
main()