Download server.py from srswti/axe-diffusion-ksl-xe: direct link, hf CLI and curl.
- Browser
- Download file 18.1 kB
-
https://huggingface.co/srswti/axe-diffusion-ksl-xe/resolve/main/server.py
- Command line
-
hf download hf://srswti/axe-diffusion-ksl-xe/server.py
-
curl -L -o server.py https://huggingface.co/srswti/axe-diffusion-ksl-xe/resolve/main/server.py
18.1 kB
| #!/usr/bin/env python3 | |
| """AXE-Diffusion KSL-XE server (GoedelMachines W4A16 only). | |
| One uvicorn process, one model instance, two APIs: | |
| * Diffusion Studio UI: GET / POST /chat GET /health | |
| * OpenAI-compatible (router/cockpit): GET /v1/models | |
| POST /v1/chat/completions | |
| (OpenAI SSE stream + non-stream, usage) | |
| Serves GoedelMachines/diffusiongemma-26B-A4B-w4a16 via the checkpoint's fused | |
| W4 loader (`load_fast`, tier=turbo) on a single Intel XPU GPU. Generation runs | |
| block diffusion with live revisable drafts; the OpenAI endpoint streams | |
| committed deltas and reports denoising stats under the "axe" response key. | |
| """ | |
| import asyncio | |
| import json | |
| import os | |
| import queue | |
| import sys | |
| import threading | |
| import time | |
| import uuid | |
| from contextlib import asynccontextmanager | |
| from pathlib import Path | |
| from typing import Literal | |
| import httpx | |
| import torch | |
| import uvicorn | |
| from fastapi import FastAPI, HTTPException, Request | |
| from fastapi.responses import FileResponse, StreamingResponse | |
| from huggingface_hub import snapshot_download | |
| from pydantic import BaseModel, Field | |
| REPO = "GoedelMachines/diffusiongemma-26B-A4B-w4a16" | |
| REVISION = "0fede6e3e65d258d444822cfb6a79092cdc5c0af" | |
| SERVED_MODEL_NAME = os.environ.get("SERVED_MODEL_NAME", "diffusiongemma-w4a16") | |
| NVIDIA_BASE_URL = os.environ.get( | |
| "NVIDIA_BASE_URL", | |
| "http://172.17.0.1:8000" if Path("/.dockerenv").exists() else "http://127.0.0.1:8000", | |
| ).rstrip("/") | |
| NVIDIA_MODEL = os.environ.get("NVIDIA_MODEL", "RedHatAI/diffusiongemma-26B-A4B-it-NVFP4") | |
| state = {} | |
| generation_lock = threading.Lock() | |
| class CancelledGeneration(Exception): | |
| pass | |
| def split_channels(raw): | |
| if "<|channel>thought\n" in raw: | |
| before, body = raw.split("<|channel>thought\n", 1) | |
| if "<channel|>" in body: | |
| reasoning, answer = body.split("<channel|>", 1) | |
| return reasoning.strip(), (before + answer).strip() | |
| return body.strip(), before.strip() | |
| return "", raw.strip() | |
| class DraftStreamer: | |
| def __init__(self, tokenizer, eos_ids, events, cancelled): | |
| self.tokenizer = tokenizer | |
| self.eos_ids = eos_ids | |
| self.events = events | |
| self.cancelled = cancelled | |
| self.prompt = True | |
| self.committed = [] | |
| self.steps = 0 | |
| self.started = time.perf_counter() | |
| self.first_draft = None | |
| def check_cancel(self): | |
| if self.cancelled.is_set(): | |
| raise CancelledGeneration | |
| def trim(self, value): | |
| tokens = value.reshape(-1).tolist() | |
| end = next((i for i, token in enumerate(tokens) if token in self.eos_ids), len(tokens)) | |
| return tokens[:end] | |
| def emit(self, kind, tokens): | |
| raw = self.tokenizer.decode(tokens, skip_special_tokens=False) | |
| for marker in ("<|turn>", "<turn|>", "", "<pad>"): | |
| raw = raw.replace(marker, "") | |
| reasoning, answer = split_channels(raw) | |
| self.events.put({"type": kind, "reasoning": reasoning, "text": answer, | |
| "steps": self.steps, "elapsed": time.perf_counter() - self.started}) | |
| def put(self, value): | |
| self.check_cancel() | |
| if self.prompt: | |
| self.prompt = False | |
| return | |
| self.committed.extend(self.trim(value)) | |
| self.emit("commit", self.committed) | |
| def put_draft(self, value, **kwargs): | |
| self.check_cancel() | |
| self.steps += 1 | |
| if self.first_draft is None: | |
| self.first_draft = time.perf_counter() - self.started | |
| self.emit("draft", self.committed + self.trim(value)) | |
| def end(self): | |
| pass | |
| async def lifespan(app): | |
| os.environ["MOE_CUDA_ALIGN"] = "0" | |
| if os.environ.get("LOAD_LOCAL", "1") == "1": | |
| root = snapshot_download(REPO, revision=REVISION, local_files_only=True) | |
| sys.path.insert(0, root + "/kernels") | |
| from load_w4_checkpoint import load_fast | |
| state["model"], state["tokenizer"] = load_fast(root, device="xpu:0", tier="turbo") | |
| async with httpx.AsyncClient(timeout=httpx.Timeout(180, connect=5)) as client: | |
| state["nvidia_client"] = client | |
| yield | |
| app = FastAPI(lifespan=lifespan) | |
| def run_generation(messages, thinking, max_tokens, events, cancelled): | |
| """Shared generation core: studio /chat and /v1/chat/completions both use it.""" | |
| try: | |
| model, tokenizer = state["model"], state["tokenizer"] | |
| inputs = tokenizer.apply_chat_template( | |
| [m if isinstance(m, dict) else m.model_dump() for m in messages], | |
| enable_thinking=thinking, add_generation_prompt=True, | |
| return_tensors="pt", return_dict=True, | |
| )["input_ids"].to("xpu:0") | |
| context_limit = model.config.text_config.max_position_embeddings | |
| if inputs.shape[1] + max_tokens > context_limit: | |
| raise ValueError(f"Prompt plus output budget exceeds {context_limit} tokens.") | |
| eos = model.generation_config.eos_token_id | |
| streamer = DraftStreamer(tokenizer, set(eos if isinstance(eos, list) else [eos]), | |
| events, cancelled) | |
| with torch.no_grad(): | |
| model.generate(input_ids=inputs, max_new_tokens=max_tokens, streamer=streamer) | |
| torch.xpu.synchronize() | |
| events.put({"type": "done", "tokens": len(streamer.committed), | |
| "prompt_tokens": int(inputs.shape[1]), | |
| "elapsed": time.perf_counter() - streamer.started, | |
| "first_draft": streamer.first_draft, "steps": streamer.steps, | |
| "memory_gib": torch.xpu.memory_allocated() / 2**30}) | |
| except CancelledGeneration: | |
| pass | |
| except Exception as error: | |
| events.put({"type": "error", "message": str(error)}) | |
| finally: | |
| generation_lock.release() | |
| events.put(None) | |
| def drain_sync(events): | |
| """Collect the final text/reasoning/done event from an event queue.""" | |
| result = {"text": "", "reasoning": "", "done": None} | |
| while True: | |
| event = events.get() | |
| if event is None: | |
| break | |
| if event.get("type") == "error": | |
| raise RuntimeError(event["message"]) | |
| if event.get("type") in ("draft", "commit"): | |
| result["text"], result["reasoning"] = event.get("text", ""), event.get("reasoning", "") | |
| elif event.get("type") == "done": | |
| result["done"] = event | |
| return result | |
| # ───────────────────────────── Studio API ───────────────────────────── | |
| class StudioMessage(BaseModel): | |
| role: Literal["user", "assistant"] | |
| content: str = Field(min_length=1, max_length=100000) | |
| class StudioChatRequest(BaseModel): | |
| messages: list[StudioMessage] = Field(min_length=1, max_length=100) | |
| max_tokens: Literal[256, 512, 1024, 2048, 4096, 8192] = 512 | |
| thinking: bool = False | |
| backend: Literal["intel", "nvidia"] = "intel" | |
| def index(): | |
| return FileResponse(Path(__file__).with_name("web.html")) | |
| async def health(backend: Literal["intel", "nvidia"] = "intel"): | |
| if backend == "intel": | |
| return {"ready": "model" in state, "busy": generation_lock.locked(), | |
| "device": "Intel XPU 0", "model": SERVED_MODEL_NAME} | |
| try: | |
| response = await state["nvidia_client"].get(NVIDIA_BASE_URL + "/health", timeout=3) | |
| ready = response.is_success | |
| except httpx.HTTPError: | |
| ready = False | |
| return {"ready": ready, "busy": False, "device": "NVIDIA RTX 5090"} | |
| async def nvidia_stream(body): | |
| started = time.perf_counter() | |
| text, reasoning = "", "" | |
| first_output = None | |
| chunks = 0 | |
| usage = None | |
| finish_reason = None | |
| payload = { | |
| "model": NVIDIA_MODEL, | |
| "messages": [message.model_dump() for message in body.messages], | |
| "max_completion_tokens": body.max_tokens, | |
| "chat_template_kwargs": {"enable_thinking": body.thinking}, | |
| "stream": True, | |
| "stream_options": {"include_usage": True}, | |
| } | |
| try: | |
| async with state["nvidia_client"].stream( | |
| "POST", NVIDIA_BASE_URL + "/v1/chat/completions", json=payload | |
| ) as response: | |
| if not response.is_success: | |
| detail = (await response.aread()).decode(errors="replace") | |
| raise RuntimeError(f"NVIDIA server HTTP {response.status_code}: {detail[:1000]}") | |
| data = [] | |
| async for line in response.aiter_lines(): | |
| if line.startswith("data:"): | |
| data.append(line[5:].lstrip()) | |
| continue | |
| if line or not data: | |
| continue | |
| raw = "\n".join(data) | |
| data.clear() | |
| if raw == "[DONE]": | |
| if finish_reason is None: | |
| raise RuntimeError("NVIDIA stream ended without a finish reason.") | |
| yield "data: " + json.dumps({ | |
| "type": "done", "backend": "nvidia", | |
| "elapsed": time.perf_counter() - started, | |
| "first_output": first_output, "chunks": chunks, | |
| "tokens": usage.get("completion_tokens") if usage else None, | |
| "finish_reason": finish_reason, | |
| }) + "\n\n" | |
| return | |
| event = json.loads(raw) | |
| if event.get("error"): | |
| raise RuntimeError(str(event["error"])) | |
| if event.get("usage"): | |
| usage = event["usage"] | |
| for choice in event.get("choices", []): | |
| delta = choice.get("delta", {}) | |
| if choice.get("finish_reason"): | |
| finish_reason = choice["finish_reason"] | |
| content = delta.get("content") or "" | |
| thought = delta.get("reasoning") or delta.get("reasoning_content") or "" | |
| if not content and not thought: | |
| continue | |
| text += content | |
| reasoning += thought | |
| chunks += 1 | |
| elapsed = time.perf_counter() - started | |
| if first_output is None: | |
| first_output = elapsed | |
| yield "data: " + json.dumps({ | |
| "type": "commit", "backend": "nvidia", "text": text, | |
| "reasoning": reasoning, "chunks": chunks, "elapsed": elapsed, | |
| }) + "\n\n" | |
| raise RuntimeError("NVIDIA stream disconnected before completion.") | |
| except Exception as error: | |
| yield "data: " + json.dumps({"type": "error", "message": str(error)}) + "\n\n" | |
| async def chat(body: StudioChatRequest, request: Request): | |
| if body.messages[-1].role != "user": | |
| raise HTTPException(400, "The last message must be from the user.") | |
| if body.backend == "nvidia": | |
| return StreamingResponse(nvidia_stream(body), media_type="text/event-stream", | |
| headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) | |
| if not generation_lock.acquire(blocking=False): | |
| raise HTTPException(409, "The Intel GPU is busy. Wait for the current generation to finish.") | |
| events = queue.Queue() | |
| cancelled = threading.Event() | |
| threading.Thread(target=run_generation, | |
| args=(body.messages, body.thinking, body.max_tokens, events, cancelled), | |
| daemon=True).start() | |
| async def stream(): | |
| try: | |
| while True: | |
| if await request.is_disconnected(): | |
| cancelled.set() | |
| break | |
| try: | |
| event = events.get_nowait() | |
| except queue.Empty: | |
| await asyncio.sleep(0.015) | |
| continue | |
| if event is None: | |
| break | |
| yield "data: " + json.dumps(event) + "\n\n" | |
| finally: | |
| cancelled.set() | |
| return StreamingResponse(stream(), media_type="text/event-stream", | |
| headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) | |
| # ──────────────────────── OpenAI-compatible API ──────────────────────── | |
| class OpenAIMessage(BaseModel): | |
| role: Literal["system", "user", "assistant"] | |
| content: str = Field(min_length=1, max_length=100000) | |
| class OpenAIChatRequest(BaseModel): | |
| model: str = SERVED_MODEL_NAME | |
| messages: list[OpenAIMessage] = Field(min_length=1, max_length=100) | |
| max_tokens: int = Field(default=512, ge=1, le=131072) | |
| temperature: float | None = None # accepted for OpenAI compatibility | |
| top_p: float | None = None | |
| stream: bool = False | |
| stream_options: dict | None = None | |
| thinking: bool = False # non-standard; also honored via chat_template_kwargs | |
| chat_template_kwargs: dict = Field(default_factory=dict) | |
| async def list_models(): | |
| return {"object": "list", "data": [{ | |
| "id": SERVED_MODEL_NAME, "object": "model", "created": 0, | |
| "owned_by": "axe", "max_model_len": 262144, | |
| "architecture": "DiffusionGemma 26B-A4B W4A16 (Intel XPU)", | |
| }]} | |
| async def openai_chat(body: OpenAIChatRequest, request: Request): | |
| if body.messages[-1].role != "user": | |
| raise HTTPException(400, "The last message must be from the user.") | |
| thinking = body.thinking | |
| if body.chat_template_kwargs.get("enable_thinking") is not None: | |
| thinking = bool(body.chat_template_kwargs["enable_thinking"]) | |
| if not generation_lock.acquire(blocking=False): | |
| raise HTTPException(409, "The Intel GPU is busy. Wait for the current generation to finish.") | |
| events = queue.Queue() | |
| cancelled = threading.Event() | |
| threading.Thread( | |
| target=run_generation, | |
| args=(body.messages, thinking, body.max_tokens, events, cancelled), | |
| daemon=True, | |
| ).start() | |
| completion_id = "chatcmpl-" + uuid.uuid4().hex[:24] | |
| created = int(time.time()) | |
| started_at = time.perf_counter() | |
| async def stream(): | |
| sent_text, sent_reasoning, first_output, chunks, final = "", "", None, 0, None | |
| def chunk(delta, finish=None, usage=None): | |
| payload = {"id": completion_id, "object": "chat.completion.chunk", | |
| "created": created, "model": body.model, | |
| "choices": [{"index": 0, "delta": delta, "finish_reason": finish}]} | |
| if usage is not None: | |
| payload["usage"] = usage | |
| return payload | |
| try: | |
| yield "data: " + json.dumps(chunk({"role": "assistant"})) + "\n\n" | |
| while True: | |
| if await request.is_disconnected(): | |
| cancelled.set() | |
| break | |
| try: | |
| event = events.get_nowait() | |
| except queue.Empty: | |
| await asyncio.sleep(0.015) | |
| continue | |
| if event is None: | |
| break | |
| if event.get("type") == "error": | |
| raise RuntimeError(event["message"]) | |
| if event.get("type") in ("draft", "commit"): | |
| text, reasoning = event.get("text", ""), event.get("reasoning", "") | |
| if first_output is None and (text or reasoning): | |
| first_output = time.perf_counter() - started_at | |
| text_delta = text[len(sent_text):] | |
| reasoning_delta = reasoning[len(sent_reasoning):] | |
| if text_delta or reasoning_delta: | |
| chunks += 1 | |
| delta = {} | |
| if reasoning_delta: | |
| delta["reasoning_content"] = reasoning_delta | |
| if text_delta: | |
| delta["content"] = text_delta | |
| sent_text, sent_reasoning = text, reasoning | |
| yield "data: " + json.dumps(chunk(delta)) + "\n\n" | |
| elif event.get("type") == "done": | |
| final = event | |
| break | |
| usage = { | |
| "prompt_tokens": final.get("prompt_tokens", 0) if final else 0, | |
| "completion_tokens": final.get("tokens", 0) if final else 0, | |
| "total_tokens": (final.get("prompt_tokens", 0) + final.get("tokens", 0)) if final else 0, | |
| } | |
| yield "data: " + json.dumps(chunk({}, finish="stop", usage=usage)) + "\n\n" | |
| yield "data: [DONE]\n\n" | |
| finally: | |
| cancelled.set() | |
| if body.stream: | |
| return StreamingResponse(stream(), media_type="text/event-stream", | |
| headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) | |
| result = drain_sync(events) | |
| done = result["done"] or {} | |
| finish = "length" if (done.get("tokens") or 0) >= body.max_tokens else "stop" | |
| message = {"role": "assistant", "content": result["text"]} | |
| if result["reasoning"]: | |
| message["reasoning_content"] = result["reasoning"] | |
| return {"id": completion_id, "object": "chat.completion", "created": created, | |
| "model": body.model, | |
| "choices": [{"index": 0, "message": message, "finish_reason": finish}], | |
| "usage": {"prompt_tokens": done.get("prompt_tokens", 0), | |
| "completion_tokens": done.get("tokens", 0), | |
| "total_tokens": done.get("prompt_tokens", 0) + done.get("tokens", 0)}, | |
| "axe": {"elapsed": done.get("elapsed"), "first_draft": done.get("first_draft"), | |
| "denoising_steps": done.get("steps")}} | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) | |