Text Generation
MLX
English
diffusion_gemma
nvfp4
diffusion-gemma
diffusion-lm
structured-output
decision-making
conversational
modelopt
Instructions to use GenbaLabs/super-decider-mlx with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use GenbaLabs/super-decider-mlx with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("GenbaLabs/super-decider-mlx") prompt = "Write a story about Einstein" messages = [{"role": "user", "content": prompt}] prompt = tokenizer.apply_chat_template( messages, add_generation_prompt=True ) text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Pi
How to use GenbaLabs/super-decider-mlx with Pi:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "GenbaLabs/super-decider-mlx"
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "mlx-lm": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "GenbaLabs/super-decider-mlx" } ] } } }Run Pi
# Start Pi in your project directory: pi
- MLX LM
How to use GenbaLabs/super-decider-mlx with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Interactive chat REPL mlx_lm.chat --model "GenbaLabs/super-decider-mlx"
Run an OpenAI-compatible server
# Install MLX LM uv tool install mlx-lm # Start the server mlx_lm.server --model "GenbaLabs/super-decider-mlx" # Calling the OpenAI-compatible server with curl curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "GenbaLabs/super-decider-mlx", "messages": [ {"role": "user", "content": "Hello"} ] }' - Hermes Agent
How to use GenbaLabs/super-decider-mlx with Hermes Agent:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "GenbaLabs/super-decider-mlx"
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default GenbaLabs/super-decider-mlx
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use GenbaLabs/super-decider-mlx with OpenClaw:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "GenbaLabs/super-decider-mlx"
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "GenbaLabs/super-decider-mlx" \ --custom-provider-id mlx-lm \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Download runtime/structured_server.py from GenbaLabs/super-decider-mlx: direct link, hf CLI and curl.
- Browser
- Download file 112 kB
-
https://huggingface.co/GenbaLabs/super-decider-mlx/resolve/main/runtime/structured_server.py
- Command line
-
hf download hf://GenbaLabs/super-decider-mlx/runtime/structured_server.py
-
curl -L -o structured_server.py https://huggingface.co/GenbaLabs/super-decider-mlx/resolve/main/runtime/structured_server.py
112 kB
| """Structured decisions in front of a vLLM DiffusionGemma server. | |
| Derived from mmastrac/djev (Apache-2.0); modified by Singularity Compute. | |
| POST /v1/systemone takes Jev's request body: {"model", "state", "questions"}. | |
| "questions" maps an id to {"type", "instructions", "criteria"}, where "type" | |
| is "noul", "choice", "score", "span" or "spans" and the criteria shape | |
| follows the type: | |
| noul: optional {"true": ..., "false": ...} descriptions | |
| choice: option name -> description or null | |
| score: ordered list of levels | |
| span: optional {"max_tokens": n}; the answer is a piece of the text | |
| spans: optional {"max_tokens": n, "max_items": n}; every such piece | |
| Answers take Jev's shapes, with this server's diagnostics alongside: | |
| noul: {"noul": p} | |
| choice: {"choice", "probabilities", "confidence"} | |
| score: {"score", "legend", "probabilities", "confidence"} | |
| span: {"found", "text", "start", "end", "confidence", "coverage"} | |
| spans: {"found", "items": [{"text", "start", "end", "confidence"}]} | |
| A span is a substring of the state's text, by construction: the model fills | |
| a pinned blank by copying, only the text's own token ids are read, and the | |
| decode walks the text with those tokens. "start" and "end" are character | |
| offsets into the state when it is a string, else into its "text" field. | |
| The request body may also carry the schema keys "instructions", "samples", | |
| "auto_max", "auto_threshold", "steps", "think", "ask", "chunk_rows", | |
| "chunk_prompt" and "sequential" as extensions. Images go ahead of the | |
| state, either as multipart/form-data with the JSON body in a part named | |
| "request" and each image as a file part, or as an "images" array of data | |
| URLs in the JSON body. | |
| POST /v1/chat/completions makes the same decision from an OpenAI-shaped | |
| call. The system message is the schema JSON below and the user message is | |
| the state. The reply's `content` is the JSON answer set, {"answers": ...}; | |
| Read diagnostics are returned only when the server runs with DJEV_EXPOSE_DIAGNOSTICS=1. | |
| POST /v1/raw/chat/completions passes the body to vLLM's chat completions | |
| unchanged, for plain generation through this port. | |
| With API_KEY set in the environment, every POST needs "Authorization: | |
| Bearer <key>". --tls-port adds an HTTPS listener with a self-signed | |
| certificate kept in --cert-dir, for clients that need a secure origin. | |
| Each answer is one distribution per question, from one denoise step over a | |
| seeded canvas, averaged over a few noise draws. This server handles the | |
| canvas, tokenizer, slot resolution, noise draws and averaging. | |
| Schema (system message): | |
| {"questions": [ | |
| {"id": "urgent", "type": "noul", "instructions": "..."}, | |
| {"id": "bucket", "type": "choice", "instructions": "...", | |
| "options": [{"name": "billing", "description": "..."}, ...]}, | |
| {"id": "tone", "type": "score", "instructions": "...", | |
| "levels": ["calm", "annoyed", "furious"]}], | |
| "instructions": "optional context", | |
| "samples": "auto" | N, "auto_threshold": 0.1, "auto_max": 4, | |
| "steps": 1, "think": 0} | |
| A question may also declare: | |
| "depends_on": [ids] answered after those, with their answers in | |
| its prompt | |
| "ask_if": {id: [answers]} asked only when that question's answer is | |
| among them (a skipped answer is null) | |
| "alone": true a read of its own | |
| Questions run in stages by these dependencies. Each stage is one joint | |
| read. Later stages continue the earlier answers, prefilled for a text | |
| state and restated for an image. | |
| Up to ten questions answer as "id: label" lines. Past that the id runs | |
| straight into the label, space separated, one row fewer per question. The | |
| server splits a schema whose answer template does not fit the canvas into | |
| chunks that run together, each with its own question list. "chunk_rows" | |
| sets the rows per chunk, "ask" picks a subset of question ids for one | |
| read, "sequential": true runs the chunks in order with the earlier answers | |
| prefilled, and "chunk_prompt": "shared" lists every question in each | |
| chunk's prompt. "think": N lets the model write up to N tokens in its | |
| thought channel, as an ordinary generation, and the read then runs with | |
| that thought in its prompt. With images the model writes the thought with | |
| the image in view and the server seeds it into the canvas ahead of the | |
| answer, so the canvas bounds it. The noise draws of a decision share one | |
| thought. | |
| Serve the model with a canvas that holds the answer template, for example: | |
| vllm serve google/diffusiongemma-26B-A4B-it \ | |
| --diffusion-config '{"canvas_length": 64}' --max-logprobs 32 \ | |
| --enable-prefix-caching | |
| then run this in front of it: | |
| python structured_server.py --upstream http://127.0.0.1:8000 \ | |
| --tokenizer google/diffusiongemma-26B-A4B-it --canvas 64 --port 8011 | |
| Two flags use engine features that are not in vLLM main yet, so both are off | |
| by default. --constrained reads over the labels only (vllm-project/vllm#58216): | |
| the same argmax for about a quarter less GPU time, with logprobs normalized | |
| over the labels, so label mass is always 1. --engine-samples sends a fixed | |
| sample count as one diffusion_samples request (vllm-project/vllm#58438) | |
| instead of one request per draw. | |
| """ | |
| import argparse | |
| import json | |
| import math | |
| import os | |
| import random | |
| import re | |
| import socket | |
| import ssl | |
| import subprocess | |
| import threading | |
| import time | |
| import urllib.error | |
| import urllib.request | |
| from concurrent.futures import ThreadPoolExecutor | |
| from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer | |
| import pybase64 as base64 | |
| # samples when a request omits it: "auto" (upstream default) or a count | |
| _ds = os.environ.get("DEFAULT_SAMPLES", "auto") | |
| DEFAULT_SAMPLES = int(_ds) if _ds.isdigit() else _ds | |
| # per-type temperature on the final label distribution (p ** (1/T), renormalized), e.g. | |
| # DJEV_TEMPS="noul=0.6"; unset types stay at 1 | |
| EXPOSE_DIAGNOSTICS = os.environ.get("DJEV_EXPOSE_DIAGNOSTICS", "0") == "1" # read diagnostics in responses (debug only) | |
| TEMPS = {k.strip(): float(v) for k, v in (kv.split("=") for kv in os.environ.get("DJEV_TEMPS", "").split(",") if "=" in kv)} | |
| # noul commit band: P(yes) inside (1-hi, hi) goes to hi+0.001 / 1-hi-0.001; unset = off | |
| NOUL_COMMIT = float(os.environ.get("DJEV_NOUL_COMMIT", "0") or 0) | |
| DOC_FIRST = os.environ.get("DJEV_DOC_FIRST", "0") == "1" | |
| SANDWICH = os.environ.get("DJEV_DOC_FIRST", "0") == "2" # questions first AND their ids restated after the state | |
| JSON_INDEX = int(os.environ.get("DJEV_JSON_INDEX", "0") or 0) | |
| _AT = {k.strip(): float(v) for k, v in (kv.split("=") for kv in os.environ.get("DJEV_ADAPTIVE_THINK", "").split(",") if "=" in kv)} | |
| ADAPT_BUDGET = int(_AT.pop("budget", 1024)) if _AT else 0 | |
| ADAPT_MIN_TOKENS = int(_AT.pop("min_tokens", 0)) if _AT else 0 | |
| ADAPT_MAX_INFLIGHT = int(_AT.pop("max_inflight", 0)) if _AT else 0 | |
| ADAPT_MAX_Q = int(_AT.pop("max_questions", 0)) if _AT else 0 | |
| THINK_XARGS = {k: int(os.environ[e]) for k, e in (("diffusion_max_steps", "DJEV_THINK_STEPS"), | |
| ("diffusion_canvas_length", "DJEV_THINK_CANVAS")) if os.environ.get(e)} | |
| INFLIGHT = [0] | |
| MANY_OPTIONS = {k: (float(v) if k == 'eps' else int(v)) for k, v in (p.split('=') for p in os.environ.get('DJEV_MANY_OPTIONS', '').split(',') if '=' in p)} | |
| from prometheus_client import CollectorRegistry as _PReg, Counter as _PCounter, Gauge as _PGauge, Histogram as _PHist, generate_latest as _p_latest | |
| _P_REG = _PReg() # own registry: no process_/python_ collectors that would clash with vLLM's /metrics | |
| M_REQ = _PCounter("djev_requests", "djev HTTP requests by endpoint and status", ["endpoint", "status"], registry=_P_REG) | |
| M_LAT = _PHist("djev_request_seconds", "djev request latency (POST, whole request)", ["endpoint"], registry=_P_REG, | |
| buckets=(0.025, 0.05, 0.075, 0.1, 0.15, 0.2, 0.3, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 3.0, 5.0, 10.0, 30.0, 60.0)) | |
| M_THINK = _PCounter("djev_thoughts", "adaptive thinking passes", registry=_P_REG) | |
| M_MANY = _PCounter("djev_many_options", "choice questions with more than 26 options answered via chunks", registry=_P_REG) | |
| M_INFL = _PGauge("djev_inflight", "decisions in flight in this server", registry=_P_REG) | |
| M_INFL.set_function(lambda: INFLIGHT[0]) | |
| PLAIN_CHAT_MESSAGE = ( | |
| "This model answers typed decision questions, not free-form chat. Send exactly two messages: a system message whose " | |
| "content is a JSON schema with a \"questions\" array, and a user message with the state as JSON. Or POST /v1/systemone " | |
| "with {\"state\": ..., \"questions\": {...}}." | |
| ) | |
| _EP = ("/v1/systemone", "/v1/chat/completions", "/v1/raw/chat/completions", "/v1/models", "/health") | |
| INFLIGHT_LOCK = threading.Lock() | |
| ADAPT_GATES = _AT # type -> raw top-probability gate | |
| from transformers import AutoTokenizer | |
| ARGS = None | |
| TOK = None | |
| API_KEY = os.environ.get( | |
| "API_KEY", "" | |
| ) # when set, POST routes need "Authorization: Bearer <key>" | |
| CANVAS_LEN = 64 # the served canvas length. A request may be narrower. | |
| CANVAS_STEP = 16 # request widths are multiples of this | |
| VOCAB = 262144 | |
| TURN_CLOSE = 106 | |
| PAD = 0 | |
| TOPK = int(os.environ.get("DJEV_TOPK", "20")) # top logprobs per canvas position; labels come from logprob_token_ids | |
| MAX_QUESTIONS = 64 # per request | |
| MAX_SAMPLES = 32 # cap on reads per question, set by --max-samples | |
| MAX_PARALLEL = 16 # question groups read at once | |
| SPAN_STEPS = ( | |
| 4 # denoise steps for a span blank; one step spells it position by position | |
| ) | |
| SPAN_REGION = 24 # default blank for one span, in tokens | |
| SPAN_LIST_REGION = 64 # default blank for a list of spans | |
| SPAN_ID_CAP = 128 # vLLM's logprob_token_ids cap: the text's ids must fit | |
| SPAN_MIN_CONF = 0.3 # below this a span read is repeated with the next seed | |
| SPAN_MAX_READS = 3 | |
| SPAN_PASSES = 2 # list reads per window, merged by offset | |
| SPAN_FLOOR = -30.0 | |
| SPAN_BOUNDARY = set(" \t\n,;:.!?()[]{}\"'#$") | |
| EOS = 1 | |
| NL = None | |
| SPAN_SYSTEM = ( | |
| "Answer with the exact value copied from the text, character for character, " | |
| "or none if the text has no such value.{context}\n\nQuestion answer: {q}\n" | |
| " (the value only)\n\n" | |
| 'Reply with one line per question, in this order, formatted as "id: label".' | |
| ) | |
| SPAN_LIST_SYSTEM = ( | |
| "List every value of the requested kind found in the text, one per line, " | |
| "each copied exactly as it appears, character for character, in the order " | |
| "they appear. Write none if there is none.{context}\n\nQuestion answer: {q}\n" | |
| " (one value per line)\n\n" | |
| 'Reply with the answer lines, formatted as "id: value".' | |
| ) | |
| SPAN_CHOOSE_SYSTEM = ( | |
| "Several candidate answers were copied from the text. Choose the one that " | |
| "is exactly the right answer, no more and no less.{context}\n\n" | |
| "Question answer: {q}\nCandidates:\n{options}\n\n" | |
| 'Reply with one line, formatted as "id: label".' | |
| ) | |
| # the empty thought block the chat template leaves to the model | |
| SCAFFOLD_TEXT = "<|channel>thought\n<channel|>" | |
| SCAFFOLD = None | |
| THOUGHT_OPEN = None | |
| THOUGHT_CLOSE = None | |
| # ---------------------------------------------------------------------------- | |
| # Schema | |
| # ---------------------------------------------------------------------------- | |
| class SchemaError(ValueError): | |
| pass | |
| def parse_schema(value): | |
| if ( | |
| not isinstance(value, dict) | |
| or not isinstance(value.get("questions"), list) | |
| or not value["questions"] | |
| ): | |
| raise SchemaError("schema: needs a non-empty questions array") | |
| if len(value["questions"]) > MAX_QUESTIONS: | |
| raise SchemaError(f"schema: at most {MAX_QUESTIONS} questions") | |
| qs = [] | |
| seen = set() | |
| for q in value["questions"]: | |
| qid = str(q.get("id", "")).strip() | |
| if not qid or ":" in qid or "\n" in qid: | |
| raise SchemaError( | |
| f"question id {qid!r} must be non-empty, no ':' or newline" | |
| ) | |
| if qid in seen: | |
| raise SchemaError(f"duplicate question id {qid!r}") | |
| seen.add(qid) | |
| kind = q.get("type") | |
| if kind in ("noul", "bool", "boolean"): | |
| kind = "noul" | |
| crit = q.get("criteria") or {} | |
| choices = [("yes", crit.get("true")), ("no", crit.get("false"))] | |
| labels = ["yes", "no"] | |
| elif kind == "choice": | |
| opts = q.get("options") or [] | |
| choices = [ | |
| (o["name"], o.get("description")) | |
| if isinstance(o, dict) | |
| else (str(o), None) | |
| for o in opts | |
| ] | |
| labels = [chr(ord("A") + i) for i in range(len(choices))] | |
| elif kind == "score": | |
| choices = [(str(level), None) for level in (q.get("levels") or [])] | |
| labels = ( | |
| [str(i + 1) for i in range(len(choices))] | |
| if len(choices) <= 9 | |
| else [chr(ord("A") + i) for i in range(len(choices))] | |
| ) | |
| elif kind in ("span", "spans"): | |
| default = SPAN_REGION if kind == "span" else SPAN_LIST_REGION | |
| span_cfg = { | |
| "max_tokens": q.get("max_tokens", default), | |
| "max_items": q.get("max_items", 16), | |
| } | |
| for key, hi in (("max_tokens", 96), ("max_items", 64)): | |
| v = span_cfg[key] | |
| if isinstance(v, bool) or not isinstance(v, int) or not 1 <= v <= hi: | |
| raise SchemaError(f"question {qid!r}: {key} must be 1 to {hi}") | |
| choices, labels = [], [] | |
| else: | |
| raise SchemaError(f"question {qid!r}: unknown type {kind!r}") | |
| if kind not in ("span", "spans") and len(choices) < 2: | |
| raise SchemaError(f"question {qid!r}: needs at least two alternatives") | |
| if len(choices) > 26: | |
| raise SchemaError(f"question {qid!r}: at most 26 alternatives") | |
| deps = q.get("depends_on") or [] | |
| ask_if = q.get("ask_if") or {} | |
| if not isinstance(deps, list) or not all(isinstance(d, str) for d in deps): | |
| raise SchemaError( | |
| f"question {qid!r}: depends_on must be a list of question ids" | |
| ) | |
| if not isinstance(ask_if, dict) or not all( | |
| isinstance(v, list) and v for v in ask_if.values() | |
| ): | |
| raise SchemaError( | |
| f"question {qid!r}: ask_if must map a question id to a " | |
| "non-empty list of its answers" | |
| ) | |
| if kind in ("span", "spans") and (deps or ask_if): | |
| raise SchemaError( | |
| f"question {qid!r}: a span question cannot depend on another question" | |
| ) | |
| qs.append( | |
| { | |
| "id": qid, | |
| "type": kind, | |
| "instructions": str(q.get("instructions", "")), | |
| "choices": choices, | |
| "labels": labels, | |
| "depends_on": list(dict.fromkeys(list(deps) + list(ask_if))), | |
| "ask_if": ask_if, | |
| "alone": bool(q.get("alone", False)), | |
| "span": span_cfg if kind in ("span", "spans") else None, | |
| } | |
| ) | |
| by_id = {q["id"]: q for q in qs} | |
| for q in qs: | |
| for dep in q["depends_on"]: | |
| if dep not in by_id or dep == q["id"]: | |
| raise SchemaError( | |
| f"question {q['id']!r}: depends on unknown question {dep!r}" | |
| ) | |
| if by_id[dep]["span"]: | |
| raise SchemaError( | |
| f"question {q['id']!r}: cannot depend on the span question {dep!r}" | |
| ) | |
| for dep, vals in q["ask_if"].items(): | |
| names = [c[0] for c in by_id[dep]["choices"]] | |
| if any(v not in names for v in vals): | |
| raise SchemaError( | |
| f"question {q['id']!r}: ask_if values for {dep!r} " | |
| f"must be among {names}" | |
| ) | |
| schedule(qs) # refuses a cycle | |
| samples = value.get("samples", DEFAULT_SAMPLES) | |
| if samples == "auto": | |
| policy = { | |
| "mode": "auto", | |
| "max": max(1, min(int(value.get("auto_max", 4)), MAX_SAMPLES)), | |
| "threshold": float(value.get("auto_threshold", 0.1)), | |
| } | |
| elif isinstance(samples, int) and samples >= 1: | |
| policy = {"mode": "fixed", "n": min(samples, MAX_SAMPLES)} | |
| else: | |
| raise SchemaError('schema: samples must be a positive count or "auto"') | |
| ask = value.get("ask") | |
| if ask is not None: | |
| if not isinstance(ask, list) or not ask or any(a not in seen for a in ask): | |
| raise SchemaError("schema: ask must list question ids from this schema") | |
| for q in qs: | |
| if q["id"] in ask and any(d not in ask for d in q["depends_on"]): | |
| raise SchemaError( | |
| f"schema: ask names {q['id']!r} but not everything it depends on" | |
| ) | |
| chunk_rows = value.get("chunk_rows") | |
| if chunk_rows is not None and (not isinstance(chunk_rows, int) or chunk_rows < 8): | |
| raise SchemaError("schema: chunk_rows must be an integer of at least 8") | |
| chunk_prompt = value.get("chunk_prompt", "own") | |
| if chunk_prompt not in ("shared", "own"): | |
| raise SchemaError('schema: chunk_prompt must be "shared" or "own"') | |
| sequential = bool(value.get("sequential", False)) | |
| think = value.get("think", 0) | |
| if isinstance(think, bool) or not isinstance(think, int) or not 0 <= think <= 4096: | |
| raise SchemaError("schema: think must be a thought budget in tokens, 0 to 4096") | |
| adaptive = value.get("adaptive", True) | |
| if not isinstance(adaptive, bool): | |
| raise SchemaError("schema: adaptive must be true or false") | |
| labeled = [q for q in qs if not q["span"]] | |
| fmt = "lines" if len(labeled) <= 10 else "indexed" | |
| if fmt == "indexed" and not all(fits_indexed(q) for q in labeled): | |
| fmt = "lines" # an id ran into its label; "id: label" keeps them apart | |
| return { | |
| "questions": qs, | |
| "instructions": value.get("instructions"), | |
| "policy": policy, | |
| "steps": max(1, min(int(value.get("steps", 1)), 8)), | |
| "think": think, | |
| "adaptive": adaptive, | |
| "ask": ask, | |
| "chunk_rows": chunk_rows, | |
| "chunk_prompt": chunk_prompt, | |
| "sequential": sequential, | |
| "format": fmt, | |
| } | |
| _indexed_fit_cache = {} | |
| def fits_indexed(q): | |
| """Whether each of the question's labels stays one token when written | |
| straight after its id, as "indexed" does ("toxicno" merges, "q0no" not).""" | |
| key = (q["id"], tuple(q["labels"])) | |
| if key not in _indexed_fit_cache: | |
| if len(_indexed_fit_cache) > 4096: | |
| _indexed_fit_cache.clear() | |
| try: | |
| resolve_template([q], [], FORMATS["indexed"][0], "indexed") | |
| _indexed_fit_cache[key] = True | |
| except SchemaError: | |
| _indexed_fit_cache[key] = False | |
| return _indexed_fit_cache[key] | |
| # Answer template shape: (join between questions, what precedes the label, | |
| # reply instruction). A small schema gets "lines", which is readable. | |
| # "indexed" ("0yes 1no") costs three tokens a question against four or five | |
| # and agreed with "lines" on every set tried: 42 booleans, ten 26-way | |
| # choices, twenty 5-level scores. Past ten questions the saved rows keep a | |
| # schema in one read. Two tokens a question, or no id at all, loses | |
| # alignment beyond about twenty questions, because the id ties a label to | |
| # its question. | |
| FORMATS = { | |
| "lines": ( | |
| "\n", | |
| "{id}: ", | |
| 'Reply with one line per question, in this order, formatted as "id: label".', | |
| ), | |
| "indexed": ( | |
| " ", | |
| "{id}", | |
| "Reply on one line with each question's id immediately followed by its " | |
| "label, separated by single spaces.", | |
| ), | |
| } | |
| def system_text(schema, chunked=False): | |
| s = ( | |
| "Answer a fixed set of questions about the state the user provides. " | |
| "Each question lists its allowed answers; reply with exactly one label " | |
| "per question.\n" | |
| ) | |
| if schema.get("instructions"): | |
| s += "\n" + str(schema["instructions"]).strip() + "\n" | |
| for q in schema["questions"]: | |
| s += f"\nQuestion {q['id']}: {q['instructions'].strip()}\n" | |
| for (name, desc), label in zip(q["choices"], q["labels"]): | |
| if q["type"] == "noul": | |
| s += f" {label}: {str(desc).strip()}\n" if desc else f" {label}\n" | |
| elif desc: | |
| s += f" {label}: {name} ({str(desc).strip()})\n" | |
| else: | |
| s += f" {label}: {name}\n" | |
| s += "\n" + FORMATS[schema.get("format", "lines")][2] | |
| if chunked: | |
| s += ( | |
| " A reply may cover only some of the questions; answer every line " | |
| "that is present." | |
| ) | |
| return s | |
| DECISION_PREAMBLE = ( | |
| "Answer a fixed set of questions about the state the user provides. " | |
| "Each question lists its allowed answers; reply with exactly one label " | |
| "per question.\n" | |
| ) | |
| def chat_messages(sys_text, state_content): | |
| """The system and user messages of a read. With DJEV_DOC_FIRST=1 a decision | |
| read keeps only the fixed preamble in the system message and moves the | |
| questions after the state in the user turn, so every request about the | |
| same state starts with the same tokens and vLLM's prefix cache reuses the | |
| state's prefill. Other system texts (spans) keep their layout.""" | |
| if SANDWICH and sys_text.startswith(DECISION_PREAMBLE): | |
| ids = re.findall(r"^Question (\S+): ", sys_text[len(DECISION_PREAMBLE):], re.M) | |
| tail = "\n\nQuestions to answer: " + ", ".join(ids) | |
| user = state_content + tail if isinstance(state_content, str) else list(state_content) + [{"type": "text", "text": tail}] | |
| return [{"role": "system", "content": sys_text}, {"role": "user", "content": user}] | |
| if not (DOC_FIRST and sys_text.startswith(DECISION_PREAMBLE)): | |
| return [ | |
| {"role": "system", "content": sys_text}, | |
| {"role": "user", "content": state_content}, | |
| ] | |
| rest = sys_text[len(DECISION_PREAMBLE):].lstrip("\n") | |
| if isinstance(state_content, str): | |
| user = state_content + "\n\n" + rest | |
| else: # multimodal parts: the questions follow as one more text part | |
| user = list(state_content) + [{"type": "text", "text": "\n\n" + rest}] | |
| return [ | |
| {"role": "system", "content": DECISION_PREAMBLE.rstrip("\n")}, | |
| {"role": "user", "content": user}, | |
| ] | |
| def answer_text(qs, labels, fmt="lines"): | |
| join, lead, _ = FORMATS[fmt] | |
| return join.join( | |
| lead.format(id=q["id"]) + q["labels"][i] for q, i in zip(qs, labels) | |
| ) | |
| def enc(text): | |
| return TOK.encode(text, add_special_tokens=False) | |
| def init_tokenizer(tok): | |
| global TOK, SCAFFOLD, THOUGHT_OPEN, THOUGHT_CLOSE, EOS, NL | |
| TOK = tok | |
| if tok.eos_token_id is not None: | |
| EOS = int(tok.eos_token_id) | |
| NL = enc("\n")[0] | |
| THOUGHT_OPEN = enc("<|channel>thought\n") | |
| THOUGHT_CLOSE = enc("<channel|>") | |
| SCAFFOLD = enc(SCAFFOLD_TEXT) | |
| assert THOUGHT_OPEN + THOUGHT_CLOSE == SCAFFOLD, ( | |
| "the thought tags must tokenize apart" | |
| ) | |
| def resolve_template(qs, head, lead, fmt): | |
| """Tokenize the answer template and find each question's slot. Every label | |
| must change exactly one token, at the same position for all of a question's | |
| labels, or this raises SchemaError. ``head`` is the token run the canvas | |
| starts with: the empty thought block for a plain read, and empty when the | |
| prompt already ends the thought channel. ``lead`` is the text before the | |
| first answer: the join when earlier answers are in the prompt, so the | |
| tokens match one joint template.""" | |
| base_labels = [0] * len(qs) | |
| base = head + enc(lead + answer_text(qs, base_labels, fmt)) | |
| if len(base) + 1 > CANVAS_LEN: | |
| raise SchemaError( | |
| f"answer template is {len(base)} tokens; the canvas holds {CANVAS_LEN - 1}" | |
| ) | |
| if len(qs) == 1 and len(base) + 1 > CANVAS_LEN: | |
| raise SchemaError( | |
| f"question {qs[0]['id']!r} alone needs {len(base) + 1} canvas rows" | |
| ) | |
| slots = [] | |
| for qi, q in enumerate(qs): | |
| pos = None | |
| ids = [0] * len(q["labels"]) | |
| for li in range(1, len(q["labels"])): | |
| labels = list(base_labels) | |
| labels[qi] = li | |
| e = head + enc(lead + answer_text(qs, labels, fmt)) | |
| if len(e) != len(base): | |
| raise SchemaError( | |
| f"question {q['id']!r}: label {q['labels'][li]!r} is not a " | |
| "single token" | |
| ) | |
| diffs = [i for i in range(len(e)) if e[i] != base[i]] | |
| if len(diffs) != 1 or (pos is not None and diffs[0] != pos): | |
| raise SchemaError( | |
| f"question {q['id']!r}: labels do not share one template slot" | |
| ) | |
| pos = diffs[0] | |
| ids[li] = e[pos] | |
| ids[0] = base[pos] | |
| if len(set(ids)) != len(ids): | |
| raise SchemaError( | |
| f"question {q['id']!r}: two labels tokenize to the same id" | |
| ) | |
| slots.append({"pos": pos, "label_ids": ids}) | |
| return base, slots | |
| _template_cache = {} | |
| def template_for(schema, head, lead): | |
| fmt = schema.get("format", "lines") | |
| key = json.dumps( | |
| [head, lead, fmt] + [(q["id"], q["labels"]) for q in schema["questions"]] | |
| ) | |
| if key not in _template_cache: | |
| if len(_template_cache) > 4096: # bounded like the other caches (one entry per distinct question set) | |
| _template_cache.clear() | |
| _template_cache[key] = resolve_template(schema["questions"], head, lead, fmt) | |
| return _template_cache[key] | |
| # ---------------------------------------------------------------------------- | |
| # Reads | |
| # ---------------------------------------------------------------------------- | |
| def canvas_width(template): | |
| """Smallest multiple of CANVAS_STEP that holds the template and the turn close.""" | |
| need = len(template) + 1 | |
| return min(CANVAS_LEN, -(-need // CANVAS_STEP) * CANVAS_STEP) | |
| def constrained_xargs(): | |
| """Read over the labels only. The engine runs the unembedding, sampler and | |
| self-conditioning over the request's logprob_token_ids instead of the whole | |
| vocabulary. Same argmax, about a quarter less GPU time per read. Logprobs | |
| come back normalized over the labels.""" | |
| return {"diffusion_constrained": True} if ARGS.constrained else {} | |
| def pin_xargs(template, slots, steps, samples=1): | |
| """Past one denoise step the template must be held, or accept/renoise | |
| rewrites it: pin every canvas position that is not an answer slot. A | |
| multi-sample request pins the same positions, which tells the engine | |
| what to re-noise per sample.""" | |
| if steps <= 1 and samples <= 1: | |
| return {} | |
| free = {s["pos"] for s in slots} | |
| return { | |
| "diffusion_pinned": [p for p in range(canvas_width(template)) if p not in free] | |
| } | |
| def build_canvas(template, slots, seed): | |
| rng = random.Random(seed) | |
| canvas = list(template) + [TURN_CLOSE] | |
| canvas += [PAD] * (canvas_width(template) - len(canvas)) | |
| for s in slots: | |
| canvas[s["pos"]] = rng.randrange(VOCAB) | |
| return canvas | |
| def label_id_union(slots): | |
| ids = sorted({i for s in slots for i in s["label_ids"]}) | |
| return ids[:128] # vLLM's cap per request. A schema needs far fewer. | |
| def upstream_chat(body, timeout=600): | |
| req = urllib.request.Request( | |
| ARGS.upstream.rstrip("/") + "/v1/chat/completions", | |
| data=json.dumps(body).encode(), | |
| headers={"content-type": "application/json"}, | |
| ) | |
| return json.load(urllib.request.urlopen(req, timeout=timeout)) | |
| def upstream_completions(body, timeout=600): | |
| req = urllib.request.Request( | |
| ARGS.upstream.rstrip("/") + "/v1/completions", | |
| data=json.dumps(body).encode(), | |
| headers={"content-type": "application/json"}, | |
| ) | |
| return json.load(urllib.request.urlopen(req, timeout=timeout)) | |
| def chat_prompt_ids(sys_text, state_text, thinking=False): | |
| """The prompt the chat endpoint would build, as token ids, ending after | |
| the model turn marker. Text states only. ``thinking`` turns the chat | |
| template's thinking marker on.""" | |
| messages = chat_messages(sys_text, state_text) | |
| out = TOK.apply_chat_template( | |
| messages, tokenize=True, add_generation_prompt=True, enable_thinking=thinking | |
| ) | |
| ids = ( | |
| out["input_ids"] if hasattr(out, "keys") else out | |
| ) # newer transformers return a dict | |
| return [int(t) for t in ids] | |
| def think(sys_text, state_text, budget): | |
| """A read prefix that ends with a thought the model wrote: the chat prompt | |
| with thinking on, the open tag, up to ``budget`` generated tokens, the | |
| close tag. Returns the prefix and a diagnostics dict for the thought.""" | |
| prompt = chat_prompt_ids(sys_text, state_text, thinking=True) + THOUGHT_OPEN | |
| started = time.time() | |
| d = upstream_completions( | |
| { | |
| "model": ARGS.model, | |
| "prompt": prompt, | |
| "max_tokens": budget, | |
| "logprobs": 0, | |
| "return_tokens_as_token_ids": True, | |
| "stop_token_ids": THOUGHT_CLOSE, | |
| **({"vllm_xargs": THINK_XARGS} if THINK_XARGS else {}), | |
| } | |
| ) | |
| ids = [int(t.split(":")[1]) for t in d["choices"][0]["logprobs"]["tokens"]] | |
| closed = THOUGHT_CLOSE[0] in ids | |
| if closed: | |
| ids = ids[: ids.index(THOUGHT_CLOSE[0])] | |
| info = { | |
| "tokens": len(ids), | |
| "closed": closed, | |
| "ms": (time.time() - started) * 1e3, | |
| "text": TOK.decode(ids), | |
| } | |
| return prompt + ids + THOUGHT_CLOSE, info | |
| def think_chat(sys_text, state_content, budget): | |
| """A thought written with the image in view: the chat endpoint with | |
| thinking on, capped at ``budget`` tokens and cut at the close tag. | |
| Returns the thought's token ids and a diagnostics dict.""" | |
| body = { | |
| "model": ARGS.model, | |
| "messages": chat_messages(sys_text, state_content), | |
| "max_tokens": budget, | |
| "logprobs": True, | |
| "top_logprobs": 0, | |
| "return_tokens_as_token_ids": True, | |
| "stop_token_ids": THOUGHT_CLOSE, | |
| "chat_template_kwargs": {"enable_thinking": True}, | |
| } | |
| started = time.time() | |
| d = upstream_chat(body) | |
| choice = d["choices"][0] | |
| ids = [ | |
| int(t["token"].split(":")[1]) | |
| for t in (choice.get("logprobs") or {}).get("content") or [] | |
| ] | |
| if ids[: len(THOUGHT_OPEN)] == THOUGHT_OPEN: # the model opens the channel itself | |
| ids = ids[len(THOUGHT_OPEN) :] | |
| closed = THOUGHT_CLOSE[0] in ids | |
| if closed: | |
| ids = ids[: ids.index(THOUGHT_CLOSE[0])] | |
| return ids, { | |
| "tokens": len(ids), | |
| "closed": closed, | |
| "ms": (time.time() - started) * 1e3, | |
| "text": TOK.decode(ids), | |
| } | |
| def read_xargs(schema, template, slots, seed, samples): | |
| x = { | |
| "diffusion_seed_canvas": build_canvas(template, slots, seed), | |
| "diffusion_canvas_length": canvas_width(template), | |
| "diffusion_max_steps": schema["steps"], | |
| "diffusion_read_only": True, | |
| **pin_xargs(template, slots, schema["steps"], samples), | |
| **constrained_xargs(), | |
| } | |
| if samples > 1: | |
| x["diffusion_samples"] = samples | |
| return x | |
| def samples_body(seed, samples): | |
| """The engine draws the noise of a multi-sample request from the request | |
| seed, so the draws reproduce for a schema seed.""" | |
| return {"seed": seed} if samples > 1 else {} | |
| def one_read( | |
| schema, | |
| template, | |
| slots, | |
| sys_text, | |
| state_content, | |
| seed, | |
| prefix=None, | |
| thinking=False, | |
| samples=1, | |
| ): | |
| """One request, ``samples`` noise draws of the canvas: a list of one | |
| distribution per question for each draw, and the usage.""" | |
| if prefix is not None: | |
| return one_read_continuation(schema, template, slots, prefix, seed, samples) | |
| messages = chat_messages(sys_text, state_content) | |
| body = { | |
| "model": ARGS.model, | |
| "messages": messages, | |
| "max_tokens": len(template) + 1, | |
| "logprobs": True, | |
| "top_logprobs": TOPK, | |
| # Exact logprobs for every label at every position. With a long | |
| # option list most labels never rank in the top-k, and the model's | |
| # mass sits on tokens that spell the option name instead. | |
| "logprob_token_ids": label_id_union(slots), | |
| "return_tokens_as_token_ids": True, | |
| "chat_template_kwargs": {"enable_thinking": thinking}, | |
| "vllm_xargs": read_xargs(schema, template, slots, seed, samples), | |
| **samples_body(seed, samples), | |
| } | |
| d = upstream_chat(body) | |
| draws = [] | |
| for choice in d["choices"]: | |
| content = choice["logprobs"]["content"] | |
| out = [] | |
| for q, s in zip(schema["questions"], slots): | |
| top = { | |
| int(t["token"].split(":")[1]): t["logprob"] | |
| for t in content[s["pos"]]["top_logprobs"] | |
| } | |
| out.append(slot_distribution(top, s["label_ids"])) | |
| draws.append(out) | |
| return draws, d.get("usage", {}) | |
| def slot_distribution(top, label_ids): | |
| """Label probabilities at one slot from the returned logprobs: every | |
| label's own value plus the argmax token. Read-only logprobs are at | |
| temperature 1, so the label softmax uses them directly. The entropy is | |
| over that returned set.""" | |
| floor = min(top.values()) - 5.0 | |
| lp_t = [top.get(i, floor) for i in label_ids] | |
| mx = max(lp_t) | |
| ex = [math.exp(x - mx) for x in lp_t] | |
| probs = [e / sum(ex) for e in ex] | |
| top_p = [math.exp(v) for v in top.values()] | |
| return { | |
| "probs": probs, | |
| "label_mass": sum(math.exp(x) for x in lp_t), | |
| "entropy": -sum(p * math.log(p) for p in top_p if p > 0), | |
| "argmax_is_label": max(top, key=top.get) in label_ids, | |
| } | |
| def one_read_continuation(schema, template, slots, prompt_ids, seed, samples=1): | |
| """A read whose prompt already holds the thought scaffold and earlier | |
| answer lines, sent as token ids so the chat template cannot alter it.""" | |
| body = { | |
| "model": ARGS.model, | |
| "prompt": prompt_ids, | |
| "max_tokens": len(template) + 1, | |
| "logprobs": TOPK, | |
| "logprob_token_ids": label_id_union(slots), | |
| "return_tokens_as_token_ids": True, | |
| "vllm_xargs": read_xargs(schema, template, slots, seed, samples), | |
| **samples_body(seed, samples), | |
| } | |
| d = upstream_completions(body) | |
| draws = [] | |
| for choice in d["choices"]: | |
| rows = choice["logprobs"]["top_logprobs"] | |
| out = [] | |
| for q, sl in zip(schema["questions"], slots): | |
| top = {int(k.split(":")[1]): v for k, v in rows[sl["pos"]].items()} | |
| out.append(slot_distribution(top, sl["label_ids"])) | |
| draws.append(out) | |
| return draws, d.get("usage", {}) | |
| def read_many( | |
| schema, | |
| template, | |
| slots, | |
| sys_text, | |
| state_content, | |
| seed, | |
| n, | |
| prefix=None, | |
| thinking=False, | |
| engine=False, | |
| ): | |
| """``n`` noise draws of the read: one diffusion_samples request when | |
| ``engine`` is set, otherwise one request per draw.""" | |
| if n > 1 and engine and ARGS.engine_samples: | |
| draws, usage = one_read( | |
| schema, template, slots, sys_text, state_content, seed, prefix, thinking, n | |
| ) | |
| return draws, [usage] + [None] * (len(draws) - 1) | |
| results = [None] * n | |
| errors = [None] * n | |
| usages = [None] * n | |
| def run(k): | |
| try: | |
| (results[k],), usages[k] = one_read( | |
| schema, | |
| template, | |
| slots, | |
| sys_text, | |
| state_content, | |
| seed + k * 7919, | |
| prefix, | |
| thinking, | |
| ) | |
| except Exception as e: # raised again below | |
| errors[k] = e | |
| threads = [threading.Thread(target=run, args=(k,)) for k in range(n)] | |
| for t in threads: | |
| t.start() | |
| for t in threads: | |
| t.join() | |
| for e in errors: | |
| if e is not None: | |
| raise e | |
| return results, usages | |
| def schedule(qs): | |
| """Questions in stages: a question's stage comes after the stages of | |
| everything it depends on. Declaration order is kept within a stage.""" | |
| ids = {q["id"] for q in qs} | |
| pending = list(qs) | |
| done: set = set() | |
| levels = [] | |
| while pending: | |
| level = [ | |
| q | |
| for q in pending | |
| if all(d in done or d not in ids for d in q["depends_on"]) | |
| ] | |
| if not level: | |
| raise SchemaError( | |
| "schema: dependency cycle among " + ", ".join(q["id"] for q in pending) | |
| ) | |
| levels.append(level) | |
| done |= {q["id"] for q in level} | |
| pending = [q for q in pending if q["id"] not in done] | |
| return levels | |
| def chunk_groups(schema, qs): | |
| """``qs`` split, in order, into the fewest groups whose answer templates | |
| fit ``chunk_rows`` (the canvas by default). A question marked alone gets | |
| its own group.""" | |
| limit = schema.get("chunk_rows") or CANVAS_LEN | |
| groups, group = [], [] | |
| for q in qs: | |
| if q["alone"]: | |
| if group: | |
| groups.append(group) | |
| group = [] | |
| groups.append([q]) | |
| continue | |
| trial = group + [q] | |
| rows = ( | |
| len(SCAFFOLD) | |
| + len( | |
| enc(answer_text(trial, [0] * len(trial), schema.get("format", "lines"))) | |
| ) | |
| + 1 | |
| ) | |
| if rows > limit and group: | |
| groups.append(group) | |
| group = [q] | |
| else: | |
| group = trial | |
| if group: | |
| groups.append(group) | |
| return groups | |
| def answer_name(q, a): | |
| """The answer as the name ask_if compares against: yes or no, an option | |
| name, or a level name.""" | |
| if a is None: | |
| return None | |
| return a["label"] if q["type"] == "noul" else a.get("choice", a.get("level")) | |
| def decide(schema, state_content, seed): | |
| """One decision. Questions run in stages by their dependencies. A stage | |
| is one joint read, chunked by the canvas, with a question marked alone | |
| in its own read. Later stages condition on every earlier answer: a | |
| prefilled continuation for a text state, or the answers restated in the | |
| state for an image. A question whose ask_if condition failed is skipped | |
| and its answer is null.""" | |
| started = time.time() | |
| qs = [ | |
| q | |
| for q in schema["questions"] | |
| if not schema.get("ask") or q["id"] in schema["ask"] | |
| ] | |
| span_qs = [q for q in qs if q["span"]] | |
| qs = [q for q in qs if not q["span"]] | |
| # Span questions are reads of their own, run beside the label reads. | |
| span_pool = ThreadPoolExecutor(max_workers=1) if span_qs else None | |
| span_future = ( | |
| span_pool.submit(decide_spans, schema, span_qs, state_content, seed) | |
| if span_qs | |
| else None | |
| ) | |
| schema = dict(schema, questions=[q for q in schema["questions"] if not q["span"]]) | |
| if not qs: | |
| span_answers, span_diag = span_future.result() | |
| span_pool.shutdown(wait=False) | |
| diagnostics = { | |
| "steps": schema["steps"], | |
| "stages": [], | |
| "skipped": {}, | |
| "chunks": [], | |
| "spans": span_diag, | |
| "timing": { | |
| "total_ms": (time.time() - started) * 1e3, | |
| "reads": span_diag["reads"], | |
| }, | |
| "questions": {}, | |
| "engine": "vllm", | |
| } | |
| return {"answers": span_answers, "diagnostics": diagnostics}, span_diag["rows"] | |
| levels = schedule(qs) | |
| text_state = isinstance(state_content, str) | |
| fmt = schema["format"] | |
| join = FORMATS[fmt][0] | |
| # More than one read in sequence needs the full question list in every | |
| # prompt, so that later reads continue one answer. | |
| chained = len(levels) > 1 or schema["sequential"] | |
| sys_full = system_text(schema, chunked=not text_state and chained) | |
| base_ids, thought = None, None | |
| if chained and text_state: | |
| if schema["think"]: | |
| base_ids, thought = think(sys_full, state_content, schema["think"]) | |
| else: | |
| base_ids = chat_prompt_ids(sys_full, state_content) + SCAFFOLD | |
| shared = schema["chunk_prompt"] == "shared" | |
| answered, lines, earlier = {}, [], [] | |
| parts, stages, chunks, skipped = [], [], [], {} | |
| by_id = {q["id"]: q for q in schema["questions"]} | |
| def run(group, k, conditioned): | |
| sub = dict(schema, questions=group) | |
| # The thought is written once: in the prefix of a chained text decision, | |
| # or in the first read of anything else. | |
| sub["think"] = 0 if (chained and text_state) or conditioned else schema["think"] | |
| if text_state: | |
| if conditioned: | |
| prefix, lead, sys_text = ( | |
| base_ids + enc(join.join(lines)), | |
| join, | |
| sys_full, | |
| ) | |
| elif chained: | |
| prefix, lead, sys_text = (base_ids if thought else None), "", sys_full | |
| else: | |
| prefix, lead, sys_text = ( | |
| None, | |
| "", | |
| (system_text(schema, chunked=True) if shared else system_text(sub)), | |
| ) | |
| return decide_group( | |
| sub, sys_text, state_content, seed + 104729 * k, prefix, lead | |
| ) | |
| state = state_content | |
| if conditioned: | |
| text = next( | |
| (p["text"] for p in state_content if p.get("type") == "text"), "" | |
| ) | |
| text += "\n\nAnswers so far:\n" + "\n".join(earlier) | |
| state = [p for p in state_content if p.get("type") != "text"] + [ | |
| {"type": "text", "text": text} | |
| ] | |
| sys_text = sys_full | |
| else: | |
| sys_text = sys_full if (chained or shared) else system_text(sub) | |
| return decide_group(sub, sys_text, state, seed + 104729 * k) | |
| def absorb(group, body, rows): | |
| parts.append((body, rows)) | |
| chunks.append([q["id"] for q in group]) | |
| answered.update(body["answers"]) | |
| lines.append( | |
| answer_text( | |
| group, | |
| [q["labels"].index(body["answers"][q["id"]]["label"]) for q in group], | |
| fmt, | |
| ) | |
| ) | |
| earlier.extend( | |
| f"{q['id']}: {answer_name(q, body['answers'][q['id']])}" for q in group | |
| ) | |
| k = 0 | |
| for level in levels: | |
| asked = [] | |
| for q in level: | |
| failed = next( | |
| ( | |
| (dep, vals) | |
| for dep, vals in q["ask_if"].items() | |
| if answer_name(by_id[dep], answered.get(dep)) not in vals | |
| ), | |
| None, | |
| ) | |
| if failed: | |
| answered[q["id"]] = None | |
| skipped[q["id"]] = { | |
| "because": failed[0], | |
| "was": answer_name(by_id[failed[0]], answered.get(failed[0])), | |
| "wanted": failed[1], | |
| } | |
| continue | |
| asked.append(q) | |
| if not asked: | |
| continue | |
| stages.append([q["id"] for q in asked]) | |
| groups = chunk_groups(schema, asked) | |
| conditioned = bool(lines) | |
| if schema["sequential"] or len(groups) == 1: | |
| for group in groups: | |
| body, rows = run( | |
| group, k, conditioned or (schema["sequential"] and bool(lines)) | |
| ) | |
| absorb(group, body, rows) | |
| k += 1 | |
| else: | |
| with ThreadPoolExecutor(max_workers=min(len(groups), MAX_PARALLEL)) as ex: | |
| results = list( | |
| ex.map( | |
| lambda gk, conditioned=conditioned: run( | |
| gk[1], gk[0], conditioned | |
| ), | |
| [(k + i, g) for i, g in enumerate(groups)], | |
| ) | |
| ) | |
| for group, (body, rows) in zip(groups, results): | |
| absorb(group, body, rows) | |
| k += len(groups) | |
| answers = {q["id"]: answered.get(q["id"]) for q in qs} | |
| diag_q = {} | |
| for body, _ in parts: | |
| diag_q.update(body["diagnostics"]["questions"]) | |
| # A thought written here (a chained text decision) is outside every | |
| # group's row count. One written inside a group is already counted there. | |
| extra_rows = thought["tokens"] if thought else 0 | |
| if thought is None: | |
| thoughts = [b["diagnostics"].get("thought") for b, _ in parts] | |
| thought = ( | |
| thoughts[0] if len(parts) == 1 else ([t for t in thoughts if t] or None) | |
| ) | |
| one = len(parts) == 1 and not skipped | |
| diagnostics = { | |
| "steps": schema["steps"], | |
| "stages": stages, | |
| "skipped": skipped, | |
| "chunks": chunks, | |
| "chunk_prompt": "full" if chained else schema["chunk_prompt"], | |
| "sequential": schema["sequential"], | |
| "conditioning": ( | |
| None | |
| if len(stages) <= 1 and not schema["sequential"] | |
| else ("prefill" if text_state else "restated") | |
| ), | |
| "thought": thought, | |
| "samples": ( | |
| parts[0][0]["diagnostics"]["samples"] | |
| if one | |
| else { | |
| "n": [b["diagnostics"]["samples"]["n"] for b, _ in parts], | |
| "tops": [b["diagnostics"]["samples"]["tops"] for b, _ in parts], | |
| "policy": [b["diagnostics"]["samples"]["policy"] for b, _ in parts], | |
| } | |
| ), | |
| "timing": { | |
| "total_ms": (time.time() - started) * 1e3, | |
| "reads": sum(b["diagnostics"]["timing"]["reads"] for b, _ in parts), | |
| "requests": sum(b["diagnostics"]["timing"]["requests"] for b, _ in parts), | |
| }, | |
| "prompt_tokens": max( | |
| (b["diagnostics"].get("prompt_tokens") or 0) for b, _ in parts | |
| ) | |
| or None, | |
| "questions": diag_q, | |
| "engine": "vllm", | |
| } | |
| total_rows = sum(rows for _, rows in parts) + extra_rows | |
| if span_future is not None: | |
| try: | |
| span_answers, span_diag = span_future.result() | |
| finally: | |
| span_pool.shutdown(wait=False) | |
| answers.update(span_answers) | |
| diagnostics["spans"] = span_diag | |
| diagnostics["timing"]["reads"] += span_diag["reads"] | |
| diagnostics["timing"]["total_ms"] = (time.time() - started) * 1e3 | |
| total_rows += span_diag["rows"] | |
| order = [q["id"] for q in qs] + [q["id"] for q in span_qs] | |
| answers = {qid: answers.get(qid) for qid in order} | |
| return {"answers": answers, "diagnostics": diagnostics}, total_rows | |
| # ---------------------------------------------------------------------------- | |
| # Spans | |
| # ---------------------------------------------------------------------------- | |
| def span_text_of(state): | |
| """The text a span's offsets index for a state as the client sent it: the | |
| state itself when it is a string, its "text" field when it is an object | |
| holding one as a string, else None (spans then ground in the rendered | |
| state, see span_source).""" | |
| if isinstance(state, str): | |
| return state | |
| if isinstance(state, dict) and isinstance(state.get("text"), str): | |
| return state["text"] | |
| return None | |
| def span_source(state_content, schema=None): | |
| """The text a span is grounded in and its offsets refer to. The request's | |
| span_text when it has one (a string state, or an object state's "text" | |
| field); else the state as the model reads it: the message when it is a | |
| string, the text part of an image state. The model reads an object state | |
| as JSON, so grounding in that JSON would put offsets into the JSON and | |
| make any escaped character (a newline, a quote) impossible to copy.""" | |
| if schema is not None and schema.get("span_text") is not None: | |
| return schema["span_text"] | |
| if isinstance(state_content, str): | |
| return state_content | |
| return next((p["text"] for p in state_content if p.get("type") == "text"), "") | |
| _piece_cache = {} | |
| _piece_lock = threading.Lock() | |
| def span_pieces(text): | |
| """id -> the string that token contributes: every token of the text in | |
| both spacings, plus each word's own first token with and without a | |
| leading space, since the model opens a value like "A-1042" with " A" | |
| while the text only holds "#A".""" | |
| with _piece_lock: | |
| if text in _piece_cache: | |
| return _piece_cache[text] | |
| variants = [" " + text, text] | |
| for w in set(text.split()): | |
| variants += [" " + w, w] | |
| bare = w.lstrip("#$([\"'") | |
| if bare and bare != w: | |
| variants += [" " + bare, bare] | |
| pieces = {} | |
| for v in variants: | |
| ids = enc(v) | |
| for tid, piece in zip(ids, TOK.convert_ids_to_tokens(ids)): | |
| if piece and not piece.startswith("<"): | |
| pieces[tid] = piece.replace("\u2581", " ") | |
| with _piece_lock: | |
| if len(_piece_cache) > 256: | |
| _piece_cache.clear() | |
| _piece_cache[text] = pieces | |
| return pieces | |
| def span_read_canvas(sys_text, state_content, body_ids, free, allowed, seed, steps): | |
| """One pinned read of a canvas whose body follows the scaffold, with the | |
| positions in ``free`` left as noise. Returns per body position the | |
| logprobs of the ``allowed`` ids, and the emitted ids.""" | |
| rng = random.Random(seed) | |
| shown = [rng.randrange(VOCAB) if i in free else t for i, t in enumerate(body_ids)] | |
| template = SCAFFOLD + shown | |
| width = canvas_width(template) | |
| if len(template) + 1 > width: | |
| raise SchemaError( | |
| f"a span blank of {len(body_ids)} rows does not fit the canvas" | |
| ) | |
| canvas = template + [TURN_CLOSE] | |
| canvas += [PAD] * (width - len(canvas)) | |
| want = sorted(allowed) | |
| if len(want) > SPAN_ID_CAP: | |
| raise SchemaError( | |
| f"{len(want)} distinct token ids; a read allows {SPAN_ID_CAP}" | |
| ) | |
| pinned = [p for p in range(width) if (p - len(SCAFFOLD)) not in free] | |
| d = upstream_chat( | |
| { | |
| "model": ARGS.model, | |
| "messages": chat_messages(sys_text, state_content), | |
| "max_tokens": width, | |
| "logprobs": True, | |
| "top_logprobs": 1, | |
| "logprob_token_ids": want, | |
| "return_tokens_as_token_ids": True, | |
| "chat_template_kwargs": {"enable_thinking": False}, | |
| "vllm_xargs": { | |
| "diffusion_seed_canvas": canvas, | |
| "diffusion_canvas_length": width, | |
| "diffusion_max_steps": steps, | |
| "diffusion_read_only": True, | |
| "diffusion_pinned": pinned, | |
| }, | |
| } | |
| ) | |
| content = d["choices"][0]["logprobs"]["content"] | |
| rows, emitted = [], [] | |
| for c in content[len(SCAFFOLD) : len(SCAFFOLD) + len(body_ids)]: | |
| rows.append( | |
| {int(t["token"].split(":")[1]): t["logprob"] for t in c["top_logprobs"]} | |
| ) | |
| emitted.append(int(c["token"].split(":")[1])) | |
| return rows, emitted, width | |
| def span_decode(text, rows, pieces, none_id, max_slop=2, slop_cost=-1.5, min_start=0): | |
| """The best contiguous substring under the blank's logprobs. Each token | |
| scores as regret against the model's own top pick at its position, so a | |
| long span pays only where it departs from what the model wanted to | |
| write. Up to ``max_slop`` blank positions may be skipped at ``slop_cost`` | |
| each without advancing in the text, so an inserted token (a fix, a | |
| closing quote) does not break the walk. Returns (score, start, end, | |
| confidence, coverage); start is None for none. Confidence is the weakest | |
| normalised probability along the span and its end; coverage is the mass | |
| the allowed ids held there.""" | |
| n = len(rows) | |
| L = len(text) | |
| end_ids = {TURN_CLOSE, EOS, NL} | |
| matches = [[] for _ in range(L + 1)] | |
| starts = [[] for _ in range(L + 1)] | |
| for tid, pc in pieces.items(): | |
| c = text.find(pc) | |
| while c != -1: | |
| matches[c].append((tid, len(pc))) | |
| c = text.find(pc, c + 1) | |
| if pc.startswith(" ") and len(pc) > 1: | |
| q = pc[1:] | |
| c = text.find(q) | |
| while c != -1: | |
| starts[c].append((tid, len(q))) | |
| # one space from the model where the text has a run of whitespace | |
| w = c | |
| while w > 0 and text[w - 1].isspace(): | |
| w -= 1 | |
| if c - w > 1: | |
| matches[w].append((tid, c - w + len(q))) | |
| c = text.find(q, c + 1) | |
| space_ids = [tid for tid, pc in pieces.items() if pc == " "] | |
| z = [sum(math.exp(v) for v in r.values()) or 1e-9 for r in rows] | |
| top = [max(r.values()) if r else SPAN_FLOOR for r in rows] | |
| end_lp = [ | |
| max(r.get(e, SPAN_FLOOR) for e in end_ids) - top[k] for k, r in enumerate(rows) | |
| ] | |
| punct = { | |
| tid | |
| for tid, pc in pieces.items() | |
| if pc.strip() and all(ch in SPAN_BOUNDARY for ch in pc.strip()) | |
| } | |
| end_conf = [ | |
| sum(math.exp(r[t]) for t in r if t in end_ids or t in punct) / z[k] | |
| for k, r in enumerate(rows) | |
| ] | |
| cov = [min(1.0, zk) for zk in z] | |
| best = None | |
| for a in range(min_start, L): | |
| if text[a].isspace() or not (a == 0 or text[a - 1] in SPAN_BOUNDARY): | |
| continue | |
| dp = {(a, 0): (0.0, 1.0)} | |
| for k in range(n - 1): | |
| nxt = {} | |
| for (c, used), (sc, mn) in dp.items(): | |
| opts = matches[c] | |
| if k == 0: # a value may open with a lone space token | |
| opts = opts + starts[c] + [(t, 0) for t in space_ids] | |
| for tid, ln in opts: | |
| lp = rows[k].get(tid, SPAN_FLOOR) | |
| if lp <= SPAN_FLOOR: | |
| continue | |
| cand = (sc + lp - top[k], min(mn, math.exp(lp) / z[k])) | |
| key = (c + ln, used) | |
| if key not in nxt or cand[0] > nxt[key][0]: | |
| nxt[key] = cand | |
| if used < max_slop and c > a: | |
| key = (c, used + 1) | |
| cand = (sc + slop_cost, mn) | |
| if key not in nxt or cand[0] > nxt[key][0]: | |
| nxt[key] = cand | |
| dp = nxt | |
| if not dp: | |
| break | |
| for (c, used), (sc, mn) in dp.items(): | |
| if c == a or not (c == L or text[c] in SPAN_BOUNDARY): | |
| continue | |
| total = sc + end_lp[k + 1] | |
| if best is None or total > best[0]: | |
| best = ( | |
| total, | |
| a, | |
| c, | |
| min(mn, end_conf[k + 1]), | |
| sum(cov[: k + 2]) / (k + 2), | |
| ) | |
| none = rows[0].get(none_id, SPAN_FLOOR) - top[0] + (end_lp[1] if n > 1 else 0.0) | |
| if best is None or none > best[0]: | |
| return ( | |
| none, | |
| None, | |
| None, | |
| math.exp(rows[0].get(none_id, SPAN_FLOOR)) / z[0], | |
| cov[0], | |
| ) | |
| return best | |
| def span_trim(text, a, b): | |
| """Trailing sentence punctuation the model copied along ("No!" -> "No").""" | |
| while b - a > 1 and text[b - 1] in ".,;:!?" and not text[b - 2].isdigit(): | |
| b -= 1 | |
| return b | |
| def span_windows(text, reserve=0): | |
| """(start, end) windows whose token ids fit one read, split at sentence | |
| ends and newlines with one sentence of overlap. The whole text when it | |
| fits.""" | |
| budget = SPAN_ID_CAP - 4 - reserve | |
| if len(span_pieces(text)) <= budget: | |
| return [(0, len(text))] | |
| units = [ | |
| (m.start(), m.end()) | |
| for m in re.finditer(r"[^.!?\n]*[.!?\n]+\s*|[^.!?\n]+$", text) | |
| if text[m.start() : m.end()].strip() | |
| ] | |
| split = [] | |
| for s_, e in units: | |
| if len(span_pieces(text[s_:e])) <= budget: | |
| split.append((s_, e)) | |
| continue | |
| cur = s_ | |
| for m in re.finditer(r"\S+\s*", text[s_:e]): | |
| if ( | |
| len(span_pieces(text[cur : s_ + m.end()])) > budget | |
| and s_ + m.start() > cur | |
| ): | |
| split.append((cur, s_ + m.start())) | |
| cur = s_ + m.start() | |
| split.append((cur, e)) | |
| windows, cur = [], [] | |
| for s_, e in split: | |
| if cur and len(span_pieces(text[cur[0][0] : e])) > budget: | |
| windows.append((cur[0][0], cur[-1][1])) | |
| cur = [cur[-1]] if len(span_pieces(text[cur[-1][0] : e])) <= budget else [] | |
| cur.append((s_, e)) | |
| if cur: | |
| windows.append((cur[0][0], cur[-1][1])) | |
| return windows | |
| def span_context(schema): | |
| ins = schema.get("instructions") | |
| return ("\n\n" + str(ins).strip()) if ins else "" | |
| def span_choose(schema, q, state_content, candidates, seed): | |
| """A one-step choice over candidate strings with the whole state in | |
| view: the verify step when windows disagree. Returns (index, probs).""" | |
| letters = [chr(ord("A") + i) for i in range(len(candidates))] | |
| options = "\n".join(f" {ltr}: {c}" for ltr, c in zip(letters, candidates)) | |
| sys_text = SPAN_CHOOSE_SYSTEM.format( | |
| q=q["instructions"], context=span_context(schema), options=options | |
| ) | |
| ids = [enc(" " + ltr)[0] for ltr in letters] | |
| body = enc("answer:") + [PAD] | |
| rows, _, _ = span_read_canvas( | |
| sys_text, | |
| state_content, | |
| body, | |
| {len(body) - 1}, | |
| set(ids) | {TURN_CLOSE, EOS, NL}, | |
| seed, | |
| 1, | |
| ) | |
| dist = slot_distribution(rows[-1], ids) | |
| probs = dict(zip(candidates, dist["probs"])) | |
| return max(range(len(candidates)), key=lambda i: dist["probs"][i]), probs | |
| def read_span_window(schema, q, sys_text, state_content, text, window, seed): | |
| """One span question in one window: up to SPAN_MAX_READS reads with | |
| successive seeds until the answer clears SPAN_MIN_CONF. A rare bad read | |
| places the end early and shows up as low confidence.""" | |
| wtext = text[window[0] : window[1]] | |
| pieces = span_pieces(wtext) | |
| none_id = enc(" none")[0] | |
| allowed = set(pieces) | {TURN_CLOSE, EOS, NL, none_id} | |
| head = enc("answer:") | |
| region = max( | |
| 1, min(q["span"]["max_tokens"], CANVAS_LEN - len(SCAFFOLD) - len(head) - 1) | |
| ) | |
| body = head + [PAD] * region | |
| free = set(range(len(head), len(body))) | |
| steps = max(SPAN_STEPS, schema["steps"]) | |
| best, reads, rows_used = None, 0, 0 | |
| for i in range(SPAN_MAX_READS): | |
| rows, _, width = span_read_canvas( | |
| sys_text, state_content, body, free, allowed, seed + i, steps | |
| ) | |
| reads += 1 | |
| rows_used += width | |
| _, a, b, conf, cov = span_decode(wtext, rows[len(head) :], pieces, none_id) | |
| if a is None: | |
| ans = { | |
| "type": "span", | |
| "found": False, | |
| "text": None, | |
| "start": None, | |
| "end": None, | |
| } | |
| else: | |
| b = span_trim(wtext, a, b) | |
| ans = { | |
| "type": "span", | |
| "found": True, | |
| "text": wtext[a:b], | |
| "start": a + window[0], | |
| "end": b + window[0], | |
| } | |
| ans["confidence"] = round(conf, 4) | |
| ans["coverage"] = round(cov, 4) | |
| if best is None or ans["confidence"] > best["confidence"]: | |
| best = ans | |
| if ans["confidence"] >= SPAN_MIN_CONF: | |
| break | |
| best["reads"] = reads | |
| return best, rows_used | |
| def read_span(schema, q, state_content, seed): | |
| """One span question: the whole text in the prompt, the read restricted | |
| to each window's ids, windows in parallel, and one verify choice when | |
| they disagree.""" | |
| text = span_source(state_content, schema) | |
| sys_text = SPAN_SYSTEM.format(q=q["instructions"], context=span_context(schema)) | |
| windows = span_windows(text) | |
| with ThreadPoolExecutor(max_workers=min(8, len(windows))) as ex: | |
| parts = list( | |
| ex.map( | |
| lambda w: read_span_window( | |
| schema, q, sys_text, state_content, text, w, seed | |
| ), | |
| windows, | |
| ) | |
| ) | |
| rows = sum(r for _, r in parts) | |
| found = [a for a, _ in parts if a["found"]] | |
| if not found: | |
| a = max((a for a, _ in parts), key=lambda a: a["confidence"] * a["coverage"]) | |
| else: | |
| found.sort(key=lambda a: -a["confidence"] * a["coverage"]) | |
| distinct = [] | |
| for a in found: | |
| if a["text"] not in [d["text"] for d in distinct]: | |
| distinct.append(a) | |
| a = distinct[0] | |
| if len(distinct) > 1: | |
| i, probs = span_choose( | |
| schema, q, state_content, [d["text"] for d in distinct[:8]], seed | |
| ) | |
| a = distinct[i] | |
| a["resolved"] = probs | |
| a["reads"] += 1 | |
| a["reads"] = sum(p["reads"] for p, _ in parts) + (1 if "resolved" in a else 0) | |
| if len(windows) > 1: | |
| a["windows"] = len(windows) | |
| return a, rows | |
| def read_spans_window(schema, q, sys_text, state_content, text, window, seed): | |
| """A list of spans in one window: one read writes the values one per | |
| line, each line is decoded as a span whose start follows the previous | |
| item, so repeated mentions map to successive occurrences.""" | |
| wtext = text[window[0] : window[1]] | |
| pieces = span_pieces(wtext) | |
| none_id = enc(" none")[0] | |
| allowed = set(pieces) | {TURN_CLOSE, EOS, NL, none_id} | |
| head = enc("answer:") | |
| region = max( | |
| 1, min(q["span"]["max_tokens"], CANVAS_LEN - len(SCAFFOLD) - len(head) - 1) | |
| ) | |
| body = head + [PAD] * region | |
| free = set(range(len(head), len(body))) | |
| steps = max(SPAN_STEPS, schema["steps"]) | |
| rows, emitted, width = span_read_canvas( | |
| sys_text, state_content, body, free, allowed, seed, steps | |
| ) | |
| region_rows, region_em = rows[len(head) :], emitted[len(head) :] | |
| items, seg_start, min_start = [], 0, 0 | |
| for k, t in enumerate(region_em + [TURN_CLOSE]): | |
| if t not in (TURN_CLOSE, EOS, NL) and t != PAD: | |
| continue | |
| # the model repeats an "answer:" label on later lines: its word is | |
| # outside the allowed set, its colon may not be (the text can hold | |
| # "02:14"), so skip unreadable tokens and a colon after them | |
| if seg_start < k and region_em[seg_start] not in allowed: | |
| while seg_start < k and region_em[seg_start] not in allowed: | |
| seg_start += 1 | |
| if seg_start < k and pieces.get(region_em[seg_start], "").strip() == ":": | |
| seg_start += 1 | |
| seg = region_rows[seg_start : k + 1] | |
| if k > seg_start and seg: | |
| _, a, b, conf, cov = span_decode(wtext, seg, pieces, none_id) | |
| dup = False | |
| if a is not None and a < min_start: | |
| if wtext.find(wtext[a:b], min_start) != -1: | |
| _, a, b, conf, cov = span_decode( | |
| wtext, seg, pieces, none_id, min_start=min_start | |
| ) | |
| else: | |
| a, dup = None, True # the model repeated a line it wrote | |
| if a is not None: | |
| b = span_trim(wtext, a, b) | |
| items.append( | |
| { | |
| "text": wtext[a:b], | |
| "start": a + window[0], | |
| "end": b + window[0], | |
| "confidence": round(conf, 4), | |
| "coverage": round(cov, 4), | |
| } | |
| ) | |
| min_start = b | |
| elif not items and not dup: | |
| break # the first line is none | |
| seg_start = k + 1 | |
| if t != NL or len(items) >= q["span"]["max_items"]: | |
| break | |
| return items, width | |
| def read_spans(schema, q, state_content, seed): | |
| """Every span of a kind: windows and SPAN_PASSES seeds in parallel, | |
| merged by offset, low-confidence lines dropped.""" | |
| text = span_source(state_content, schema) | |
| sys_text = SPAN_LIST_SYSTEM.format( | |
| q=q["instructions"], context=span_context(schema) | |
| ) | |
| windows = span_windows(text) | |
| jobs = [(w, seed + p) for p in range(SPAN_PASSES) for w in windows] | |
| with ThreadPoolExecutor(max_workers=min(8, len(jobs))) as ex: | |
| parts = list( | |
| ex.map( | |
| lambda j: read_spans_window( | |
| schema, q, sys_text, state_content, text, j[0], j[1] | |
| ), | |
| jobs, | |
| ) | |
| ) | |
| items = [] | |
| for part, _ in parts: | |
| for it in part: | |
| if it["confidence"] < SPAN_MIN_CONF: | |
| continue | |
| same = [ | |
| o for o in items if it["start"] < o["end"] and it["end"] > o["start"] | |
| ] | |
| if same: | |
| if it["confidence"] > same[0]["confidence"]: | |
| same[0].update(it) | |
| continue | |
| items.append(it) | |
| items.sort(key=lambda i: i["start"]) | |
| a = {"type": "spans", "found": bool(items), "items": items, "reads": len(jobs)} | |
| if len(windows) > 1: | |
| a["windows"] = len(windows) | |
| return a, sum(r for _, r in parts) | |
| def decide_spans(schema, span_qs, state_content, seed): | |
| """Every span question of a decision, in parallel.""" | |
| started = time.time() | |
| if not span_source(state_content, schema).strip(): | |
| raise SchemaError("a span question needs a text state to point into") | |
| def run(iq): | |
| i, q = iq | |
| fn = read_spans if q["type"] == "spans" else read_span | |
| return fn(schema, q, state_content, seed + 104729 * (i + 1)) | |
| with ThreadPoolExecutor(max_workers=min(MAX_PARALLEL, len(span_qs))) as ex: | |
| results = list(ex.map(run, enumerate(span_qs))) | |
| answers = {q["id"]: a for q, (a, _) in zip(span_qs, results)} | |
| diag = { | |
| "steps": max(SPAN_STEPS, schema["steps"]), | |
| "reads": sum(a["reads"] for a, _ in results), | |
| "rows": sum(r for _, r in results), | |
| "source": "state" if isinstance(state_content, str) else "text part", | |
| "ms": (time.time() - started) * 1e3, | |
| } | |
| return answers, diag | |
| def decide_group(schema, sys_text, state_content, seed, prefix=None, lead=""): | |
| """Single-pass read; with DJEV_ADAPTIVE_THINK, uncertain questions are re-answered after a thought.""" | |
| out, n_tok = _decide_group(schema, sys_text, state_content, seed, prefix, lead) | |
| if not (ADAPT_GATES and schema.get("adaptive", True) and not schema["think"] and prefix is None | |
| and isinstance(state_content, str)): | |
| return out, n_tok | |
| if ADAPT_MIN_TOKENS and (out["diagnostics"].get("prompt_tokens") or 0) < ADAPT_MIN_TOKENS: | |
| return out, n_tok | |
| if ADAPT_MAX_INFLIGHT and INFLIGHT[0] > ADAPT_MAX_INFLIGHT and not schema.get("adaptive_force"): # djev-thinking-mode | |
| return out, n_tok | |
| if ADAPT_MAX_Q and len(schema["questions"]) > ADAPT_MAX_Q: | |
| return out, n_tok | |
| raw = out["diagnostics"]["raw_top"] | |
| low = [q["id"] for q in schema["questions"] if raw[q["id"]] < ADAPT_GATES.get(q["type"], 0.0)] | |
| if not low: | |
| return out, n_tok | |
| started = time.time() | |
| M_THINK.inc() | |
| try: | |
| out2, n2 = _decide_group(dict(schema, think=schema.get("adaptive_budget") or ADAPT_BUDGET), sys_text, state_content, seed, None, lead) # djev-thinking-mode | |
| except SchemaError: | |
| return out, n_tok # no room for a thought: keep the single-pass answers | |
| for qid in low: | |
| out["answers"][qid] = out2["answers"][qid] | |
| out["diagnostics"]["adaptive_think"] = { | |
| "questions": low, | |
| "ms": (time.time() - started) * 1e3, | |
| "thought_tokens": (out2["diagnostics"].get("thought") or {}).get("tokens"), | |
| } | |
| return out, n_tok + n2 | |
| def _decide_group(schema, sys_text, state_content, seed, prefix=None, lead=""): | |
| started = time.time() | |
| thought = None | |
| head = SCAFFOLD if prefix is None else [] | |
| thinking = False | |
| if prefix is None and schema["think"]: | |
| if isinstance(state_content, str): | |
| prefix, thought = think(sys_text, state_content, schema["think"]) | |
| head = [] | |
| else: | |
| # With images the model writes the thought with the image in view, and | |
| # the server seeds it into the canvas ahead of the answer, so the read | |
| # keeps the image. The canvas bounds the thought. | |
| answer = enc( | |
| answer_text( | |
| schema["questions"], | |
| [0] * len(schema["questions"]), | |
| schema.get("format", "lines"), | |
| ) | |
| ) | |
| fits = CANVAS_LEN - 1 - len(THOUGHT_OPEN) - len(THOUGHT_CLOSE) - len(answer) | |
| if fits < 8: | |
| raise SchemaError( | |
| f"think: the canvas leaves {fits} rows for a thought " | |
| "beside this template" | |
| ) | |
| ids, thought = think_chat( | |
| sys_text, state_content, min(schema["think"], fits) | |
| ) | |
| thought["budget"] = min(schema["think"], fits) | |
| head = THOUGHT_OPEN + ids + THOUGHT_CLOSE | |
| thinking = True | |
| template, slots = template_for(schema, head, lead) | |
| policy = schema["policy"] | |
| if policy["mode"] == "fixed": | |
| reads, usages = read_many( | |
| schema, | |
| template, | |
| slots, | |
| sys_text, | |
| state_content, | |
| seed, | |
| policy["n"], | |
| prefix, | |
| thinking, | |
| engine=True, | |
| ) | |
| extended = None | |
| first_entropy = None | |
| else: | |
| reads, usages = read_many( | |
| schema, template, slots, sys_text, state_content, seed, 1, prefix, thinking | |
| ) | |
| first_entropy = { | |
| q["id"]: r["entropy"] for q, r in zip(schema["questions"], reads[0]) | |
| } | |
| extended = ( | |
| max(first_entropy.values()) > policy["threshold"] and policy["max"] > 1 | |
| ) | |
| if extended: | |
| more, more_usages = read_many( | |
| schema, | |
| template, | |
| slots, | |
| sys_text, | |
| state_content, | |
| seed + 1, | |
| policy["max"] - 1, | |
| prefix, | |
| thinking, | |
| ) | |
| reads += more | |
| usages += more_usages | |
| prompt_tokens = next( | |
| (u["prompt_tokens"] for u in usages if u and u.get("prompt_tokens")), None | |
| ) | |
| elapsed_ms = (time.time() - started) * 1e3 | |
| answers = {} | |
| diag_q = {} | |
| raw_top = {} | |
| n = len(reads) | |
| for qi, q in enumerate(schema["questions"]): | |
| per = [r[qi]["probs"] for r in reads] | |
| mean = [sum(p[i] for p in per) / n for i in range(len(q["labels"]))] | |
| raw_top[q["id"]] = max(mean) | |
| t = TEMPS.get(q["type"], 1.0) | |
| if t != 1.0: | |
| w = [max(m, 1e-12) ** (1.0 / t) for m in mean] | |
| mean = [x / sum(w) for x in w] | |
| if NOUL_COMMIT and q["type"] == "noul" and 1 - NOUL_COMMIT < mean[0] < NOUL_COMMIT: | |
| y = NOUL_COMMIT + 0.001 if mean[0] >= 0.5 else 1 - NOUL_COMMIT - 0.001 | |
| mean = [y, 1 - y] | |
| top = max(range(len(mean)), key=lambda i: mean[i]) | |
| a = { | |
| "type": q["type"], | |
| "label": q["labels"][top], | |
| "confidence": mean[top], | |
| "probabilities": {c[0]: m for c, m in zip(q["choices"], mean)}, | |
| } | |
| if q["type"] == "noul": | |
| a["noul"] = mean[0] | |
| elif q["type"] == "choice": | |
| a["choice"] = q["choices"][top][0] | |
| else: | |
| a["score"] = sum((i + 1) * m for i, m in enumerate(mean)) | |
| a["level"] = q["choices"][top][0] | |
| if n > 1: | |
| var = sum((p[top] - mean[top]) ** 2 for p in per) / (n - 1) | |
| a["stderr"] = (var / n) ** 0.5 | |
| a["agreement"] = ( | |
| sum(1 for p in per if max(range(len(p)), key=lambda i: p[i]) == top) / n | |
| ) | |
| answers[q["id"]] = a | |
| diag_q[q["id"]] = { | |
| "pos": slots[qi]["pos"], | |
| "entropy": [r[qi]["entropy"] for r in reads], | |
| "label_mass": reads[0][qi]["label_mass"], | |
| "argmax_is_label": reads[0][qi]["argmax_is_label"], | |
| } | |
| tops = [ | |
| { | |
| q["id"]: [ | |
| q["labels"][ | |
| max(range(len(r[qi]["probs"])), key=lambda i: r[qi]["probs"][i]) | |
| ], | |
| max(r[qi]["probs"]), | |
| r[qi]["entropy"], | |
| ] | |
| for qi, q in enumerate(schema["questions"]) | |
| } | |
| for r in reads | |
| ] | |
| return { | |
| "answers": answers, | |
| "diagnostics": { | |
| "steps": schema["steps"], | |
| "samples": { | |
| "n": n, | |
| "tops": tops, | |
| "policy": dict( | |
| policy, extended=extended, first_read_entropy=first_entropy | |
| ), | |
| }, | |
| "timing": { | |
| "total_ms": elapsed_ms, | |
| "reads": n, | |
| "requests": sum(1 for u in usages if u is not None), | |
| }, | |
| "thought": thought, | |
| "raw_top": raw_top, | |
| "prompt_tokens": prompt_tokens, | |
| "questions": diag_q, | |
| "engine": "vllm", | |
| }, | |
| }, len(template) + 1 + (thought["tokens"] if thought else 0) | |
| # ---------------------------------------------------------------------------- | |
| # Jev's contract | |
| # ---------------------------------------------------------------------------- | |
| JEV_EXTENSIONS = ( | |
| "instructions", | |
| "samples", | |
| "auto_max", | |
| "auto_threshold", | |
| "steps", | |
| "think", | |
| "ask", | |
| "chunk_rows", | |
| "chunk_prompt", | |
| "sequential", | |
| "adaptive", | |
| ) | |
| def jev_schema(body): | |
| """This server's schema from a Jev request body.""" | |
| qs = body.get("questions") | |
| if not isinstance(qs, dict) or not qs: | |
| raise SchemaError("questions: needs a non-empty map of id -> question") | |
| out = [] | |
| for qid, q in qs.items(): | |
| if not isinstance(q, dict): | |
| raise SchemaError(f"question {qid!r}: must be an object") | |
| kind, crit, ins = q.get("type"), q.get("criteria"), q.get("instructions", "") | |
| item = { | |
| "id": qid, | |
| "type": kind, | |
| "instructions": ins if isinstance(ins, str) else json.dumps(ins), | |
| } | |
| if kind == "noul": | |
| if crit is not None and not isinstance(crit, dict): | |
| raise SchemaError( | |
| f"question {qid!r}: noul criteria must be an object with " | |
| "true and false" | |
| ) | |
| item["criteria"] = crit | |
| elif kind == "choice": | |
| if not isinstance(crit, dict) or not crit: | |
| raise SchemaError( | |
| f"question {qid!r}: choice criteria must map option names " | |
| "to descriptions" | |
| ) | |
| item["options"] = [ | |
| {"name": str(n), "description": d} for n, d in crit.items() | |
| ] | |
| elif kind == "score": | |
| if not isinstance(crit, list): | |
| raise SchemaError( | |
| f"question {qid!r}: score criteria must be an ordered list " | |
| "of levels" | |
| ) | |
| item["levels"] = crit | |
| elif kind in ("span", "spans"): | |
| if crit is not None and not isinstance(crit, dict): | |
| raise SchemaError( | |
| f"question {qid!r}: {kind} criteria must be an object, " | |
| 'e.g. {"max_tokens": 24}' | |
| ) | |
| for key in ("max_tokens", "max_items"): | |
| if crit and key in crit: | |
| item[key] = crit[key] | |
| else: | |
| raise SchemaError(f"question {qid!r}: unknown type {kind!r}") | |
| for key in ("depends_on", "ask_if", "alone"): | |
| if key in q: | |
| item[key] = q[key] | |
| out.append(item) | |
| mode, budget = body.get("thinking"), body.get("thinking_budget") # djev-thinking-mode | |
| if budget is not None and (isinstance(budget, bool) or not isinstance(budget, int) or not 16 <= budget <= 4096): | |
| raise SchemaError("thinking_budget: thought tokens, 16 to 4096") | |
| if mode is not None: | |
| if mode not in ("off", "auto", "auto_forced", "always"): | |
| raise SchemaError('thinking: one of "off", "auto", "auto_forced", "always"') | |
| body = dict(body) | |
| body["adaptive"] = mode in ("auto", "auto_forced") | |
| body["think"] = (budget or ADAPT_BUDGET or 512) if mode == "always" else 0 | |
| schema = {k: body[k] for k in JEV_EXTENSIONS if k in body} | |
| schema["questions"] = out | |
| parsed = parse_schema(schema) | |
| parsed["adaptive_force"] = mode == "auto_forced" | |
| parsed["adaptive_budget"] = budget if mode in ("auto", "auto_forced") else None | |
| return parsed | |
| def image_part(content_type, data): | |
| return { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": f"data:{content_type};base64," + base64.b64encode(data).decode() | |
| }, | |
| } | |
| def jev_images(value): | |
| """Image parts from the body's "images": data URLs, or objects with | |
| content_type and base64.""" | |
| parts = [] | |
| for i, im in enumerate(value or []): | |
| if isinstance(im, str) and im.startswith("data:image/"): | |
| parts.append({"type": "image_url", "image_url": {"url": im}}) | |
| elif ( | |
| isinstance(im, dict) | |
| and str(im.get("content_type", "")).startswith("image/") | |
| and isinstance(im.get("base64"), str) | |
| ): | |
| parts.append( | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": f"data:{im['content_type']};base64,{im['base64']}" | |
| }, | |
| } | |
| ) | |
| else: | |
| raise SchemaError( | |
| f"images[{i}]: a data:image/... URL or an object with " | |
| "content_type and base64" | |
| ) | |
| return parts | |
| def index_arrays(x): | |
| """Number the objects of long arrays ("_index": i, first key) so a question can point at one record.""" | |
| if isinstance(x, list): | |
| items = [index_arrays(e) for e in x] | |
| if len(items) >= JSON_INDEX and all(isinstance(e, dict) for e in items): | |
| items = [{"_index": i, **e} for i, e in enumerate(items)] | |
| return items | |
| if isinstance(x, dict): | |
| return {k: index_arrays(v) for k, v in x.items()} | |
| return x | |
| def upstream_client_error(detail): | |
| """A readable reason for an upstream 400.""" | |
| low = detail.lower() | |
| if "context length" in low or "max_model_len" in low or "too long" in low or "maximum" in low and "tokens" in low: | |
| return "state too long for this deployment's context window (" + detail[:200] + ")" | |
| if "image" in low or "multimodal" in low or "mm_" in low: | |
| return "images are not supported by this deployment (" + detail[:200] + ")" | |
| return "invalid request: " + detail[:300] | |
| def jev_state(body, image_parts=()): | |
| """The user message: the state as text (as given, or as JSON), with any | |
| images ahead of it.""" | |
| state = body.get("state") | |
| if state is None: | |
| raise SchemaError("state: required") | |
| # ensure_ascii=False: the model reads "°" as itself, not as \u00b0, so a | |
| # span can copy it. | |
| if JSON_INDEX and not isinstance(state, str): | |
| state = index_arrays(state) | |
| text = state if isinstance(state, str) else json.dumps(state, ensure_ascii=False) | |
| if not image_parts: | |
| return text | |
| return list(image_parts) + [{"type": "text", "text": text}] | |
| def jev_answer(q, a): | |
| if a is None: | |
| return None | |
| if q["type"] == "noul": | |
| return {"type": "noul", "noul": a["noul"]} | |
| if q["type"] == "choice": | |
| return { | |
| "type": "choice", | |
| "choice": a["choice"], | |
| "probabilities": a["probabilities"], | |
| "confidence": a["confidence"], | |
| } | |
| if q["type"] in ("span", "spans"): | |
| return a | |
| names = [c[0] for c in q["choices"]] | |
| probs = {str(i): a["probabilities"][n] for i, n in enumerate(names)} | |
| return { | |
| "type": "score", | |
| "score": sum(i * p for i, p in enumerate(probs.values())), | |
| "legend": {str(i): n for i, n in enumerate(names)}, | |
| "probabilities": probs, | |
| "confidence": a["confidence"], | |
| } | |
| def chat_answer(q, a): | |
| """The chat endpoint's answer: the Jev shape plus a readable label (and, for noul, its probabilities).""" | |
| j = jev_answer(q, a) | |
| if j is None or q["type"] not in ("noul", "choice", "score"): | |
| return j | |
| j = dict(j) | |
| if q["type"] == "noul": | |
| j["label"] = "yes" if a["noul"] >= 0.5 else "no" | |
| j["confidence"] = a["confidence"] | |
| j["probabilities"] = a["probabilities"] | |
| elif q["type"] == "choice": | |
| j["label"] = a["choice"] | |
| else: | |
| j["label"] = a["level"] | |
| return j | |
| # ---------------------------------------------------------------------------- | |
| # HTTP | |
| # ---------------------------------------------------------------------------- | |
| def answer_brief(v): | |
| """One word of an answer for the log line.""" | |
| if v is None: | |
| return "skipped" | |
| if "label" in v: | |
| return v["label"] | |
| if "items" in v: | |
| return f"{len(v['items'])}spans" | |
| return repr(v.get("text")) if v.get("found") else "none" | |
| def message_text(m): | |
| c = m.get("content", "") | |
| if isinstance(c, list): | |
| return "".join(p.get("text", "") for p in c if isinstance(p, dict)) | |
| return c if isinstance(c, str) else "" | |
| def is_structured(req): | |
| """True when a chat request is a structured read: its first message is a | |
| system (or developer) message whose text is a JSON object with a | |
| "questions" key. Anything else is ordinary chat for vLLM.""" | |
| msgs = req.get("messages") or [] | |
| if not msgs or not isinstance(msgs[0], dict): | |
| return False | |
| if msgs[0].get("role") not in ("system", "developer"): | |
| return False | |
| try: | |
| value = json.loads(message_text(msgs[0])) | |
| except ValueError: | |
| return False | |
| return isinstance(value, dict) and "questions" in value | |
| class Server(ThreadingHTTPServer): | |
| # The default backlog of 5 resets connections when a client opens as many | |
| # at once as the engine serves sequences. | |
| request_queue_size = 256 | |
| daemon_threads = True | |
| def server_bind(self): | |
| if os.environ.get("DJEV_REUSE_PORT") == "1": # several processes share the port; the kernel spreads connections | |
| self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | |
| super().server_bind() | |
| class Handler(BaseHTTPRequestHandler): | |
| def log_message(self, fmt, *args): | |
| pass | |
| def send_response(self, code, message=None): | |
| self._djev_status = code | |
| return super().send_response(code, message) | |
| def _observe(self, method, fn): | |
| t0 = time.time() | |
| self._djev_status = 0 | |
| try: | |
| return fn() | |
| finally: | |
| p = self.path.split("?", 1)[0] | |
| ep = p if p in _EP else ("/v1/other" if p.startswith("/v1/") else "other") | |
| M_REQ.labels(ep, str(self._djev_status)).inc() | |
| if method == "POST": | |
| M_LAT.labels(ep).observe(time.time() - t0) | |
| def do_POST(self): | |
| return self._observe("POST", self._do_post) | |
| def do_GET(self): | |
| if self.path.split("?", 1)[0] == "/metrics": | |
| return self._metrics() | |
| return self._observe("GET", self._do_get) | |
| def _metrics(self): | |
| """Prometheus scrape: djev_* plus vLLM's metrics; not served through the public ingress.""" | |
| if self.headers.get("x-forwarded-for") or self.headers.get("x-real-ip"): | |
| return self._json(404, {"error": {"message": "unknown route"}}) | |
| parts = [_p_latest(_P_REG)] | |
| try: | |
| with urllib.request.urlopen(ARGS.upstream.rstrip("/") + "/metrics", timeout=5) as r: | |
| parts.append(r.read()) | |
| except OSError: | |
| pass | |
| body = b"".join(parts) | |
| self.send_response(200) | |
| self.send_header("content-type", "text/plain; version=0.0.4; charset=utf-8") | |
| self.send_header("content-length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def _json(self, code, obj): | |
| if isinstance(obj, dict) and not EXPOSE_DIAGNOSTICS: # no internal state in client responses | |
| obj = {k: v for k, v in obj.items() if k != "diagnostics"} | |
| body = json.dumps(obj).encode() | |
| self.send_response(code) | |
| self.send_header("content-type", "application/json") | |
| self.send_header("content-length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def _do_get(self): | |
| if self.path == "/health": | |
| return self._json(200, {"status": "ok"}) | |
| if self.path == "/v1/models": | |
| return self._models() | |
| if ARGS.pages: | |
| return self._page() | |
| return self._json(404, {"error": {"message": "unknown route"}}) | |
| def _page(self): | |
| """With --pages DIR, GET /name serves DIR/name.html and / serves | |
| DIR/index.html, for test pages that post to /v1/systemone.""" | |
| name = self.path.split("?", 1)[0].strip("/") or "index" | |
| path = os.path.join(ARGS.pages, name + ".html") | |
| if not re.fullmatch(r"[A-Za-z0-9_-]+", name) or not os.path.isfile(path): | |
| return self._json(404, {"error": {"message": "unknown route"}}) | |
| with open(path, "rb") as f: | |
| body = f.read() | |
| self.send_response(200) | |
| self.send_header("content-type", "text/html; charset=utf-8") | |
| self.send_header("content-length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def _models(self): | |
| """Pass GET /v1/models through to vLLM, so this port lists the same | |
| served name as the upstream. An OpenAI router that discovers models by | |
| probing this route can then send /v1/systemone here by the body's | |
| "model".""" | |
| try: | |
| with urllib.request.urlopen( | |
| ARGS.upstream.rstrip("/") + "/v1/models", timeout=10 | |
| ) as r: | |
| code, body = r.status, r.read() | |
| except urllib.error.HTTPError as e: | |
| code, body = e.code, e.read() | |
| except OSError as e: | |
| return self._json( | |
| 503, {"error": {"message": f"upstream unavailable: {e}"}} | |
| ) | |
| self.send_response(code) | |
| self.send_header("content-type", "application/json") | |
| self.send_header("content-length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def _read_request(self): | |
| """-> (body, image parts): a JSON body, or multipart/form-data with the | |
| JSON in a part named request and each image as a file part, in order.""" | |
| raw = self.rfile.read(int(self.headers.get("content-length", "0"))) | |
| self._raw = raw # kept so a request that is not ours can be relayed as sent | |
| ctype = self.headers.get("content-type", "") | |
| if not ctype.lower().startswith("multipart/form-data"): | |
| return json.loads(raw), [] | |
| from email.parser import BytesParser | |
| from email.policy import HTTP | |
| msg = BytesParser(policy=HTTP).parsebytes( | |
| b"Content-Type: " + ctype.encode() + b"\r\n\r\n" + raw | |
| ) | |
| body, images = None, [] | |
| for part in msg.iter_parts(): | |
| name = part.get_param("name", header="content-disposition") | |
| data = part.get_payload(decode=True) | |
| if name == "request": | |
| body = json.loads(data) | |
| elif part.get_content_type().startswith("image/"): | |
| images.append(image_part(part.get_content_type(), data)) | |
| else: | |
| raise ValueError( | |
| f"part {name!r}: neither the request JSON nor an image" | |
| ) | |
| if body is None: | |
| raise ValueError( | |
| "multipart needs a part named request holding the JSON body" | |
| ) | |
| return body, images | |
| def _do_post(self): | |
| if API_KEY and self.headers.get("authorization", "") != f"Bearer {API_KEY}": | |
| return self._json( | |
| 401, | |
| { | |
| "error": { | |
| "message": "missing or wrong bearer token", | |
| "type": "authentication_error", | |
| } | |
| }, | |
| ) | |
| if self.path == "/v1/raw/chat/completions": | |
| return self._raw_chat() | |
| try: | |
| req, images = self._read_request() | |
| except Exception as e: | |
| return self._json( | |
| 400, | |
| { | |
| "error": { | |
| "message": f"invalid body: {e}", | |
| "type": "invalid_request_error", | |
| } | |
| }, | |
| ) | |
| if self.path == "/v1/systemone": | |
| return self._systemone(req, images) | |
| if self.path == "/v1/chat/completions": | |
| if is_structured(req): | |
| return self._chat(req) | |
| return self._json(400, {"error": {"message": PLAIN_CHAT_MESSAGE, "type": "invalid_request_error", | |
| "code": "structured_request_required"}}) | |
| if self.path.startswith("/v1/"): | |
| return self._relay(self.path, self._raw) | |
| return self._json(404, {"error": {"message": "unknown route"}}) | |
| def _raw_chat(self): | |
| """Pass the body and status through to vLLM's chat completions.""" | |
| raw = self.rfile.read(int(self.headers.get("content-length", "0"))) | |
| return self._relay("/v1/chat/completions", raw) | |
| def _relay(self, path, raw): | |
| """Send a request to vLLM as it arrived and return vLLM's status, | |
| content type and body unchanged. The body is copied as it arrives, so a | |
| streamed (SSE) response streams; this handler speaks HTTP/1.0, so a | |
| response without a length ends when the connection closes.""" | |
| req = urllib.request.Request( | |
| ARGS.upstream.rstrip("/") + path, | |
| data=raw, | |
| headers={ | |
| "content-type": self.headers.get("content-type", "application/json") | |
| }, | |
| ) | |
| try: | |
| r = urllib.request.urlopen(req, timeout=600) | |
| except urllib.error.HTTPError as e: | |
| r = e | |
| except OSError as e: | |
| return self._json( | |
| 503, {"error": {"message": f"upstream unavailable: {e}"}} | |
| ) | |
| with r: | |
| self.send_response(r.code) | |
| self.send_header( | |
| "content-type", r.headers.get("content-type", "application/json") | |
| ) | |
| if r.headers.get("content-length"): | |
| self.send_header("content-length", r.headers["content-length"]) | |
| self.end_headers() | |
| while True: | |
| chunk = r.read1(65536) if hasattr(r, "read1") else r.read(65536) | |
| if not chunk: | |
| break | |
| self.wfile.write(chunk) | |
| self.wfile.flush() | |
| def _decide(self, schema, state, seed): | |
| """-> (status, body) with the error body already shaped.""" | |
| with INFLIGHT_LOCK: | |
| INFLIGHT[0] += 1 | |
| try: | |
| return self._decide_inner(schema, state, seed) | |
| finally: | |
| with INFLIGHT_LOCK: | |
| INFLIGHT[0] -= 1 | |
| def _decide_inner(self, schema, state, seed): | |
| try: | |
| return 200, decide(schema, state, seed) | |
| except SchemaError as e: | |
| return 422, {"error": {"message": str(e), "type": "validation_error"}} | |
| except urllib.error.HTTPError as e: | |
| detail = e.read()[:400].decode(errors="replace") | |
| if e.code == 400: | |
| return 400, {"error": {"message": upstream_client_error(detail), "type": "invalid_request_error"}} | |
| return 502, { | |
| "error": { | |
| "message": f"upstream {e.code}: {detail[:300]}", | |
| "type": "server_error", | |
| } | |
| } | |
| except Exception as e: | |
| return 500, {"error": {"message": repr(e), "type": "server_error"}} | |
| def _systemone(self, req, images): | |
| many = [k for k, q in (req.get("questions") or {}).items() if isinstance(q, dict) and q.get("type") == "choice" | |
| and isinstance(q.get("criteria"), dict) and len(q["criteria"]) > 26] if MANY_OPTIONS else [] | |
| if many: | |
| return self._systemone_many(req, images, many) | |
| return self._systemone_one(req, images) | |
| def _systemone_core(self, req, images): | |
| """-> (code, body) for one /v1/systemone request, without writing it.""" | |
| try: | |
| schema = jev_schema(req) | |
| state = jev_state(req, images + jev_images(req.get("images"))) | |
| schema["span_text"] = span_text_of(req.get("state")) | |
| except SchemaError as e: | |
| return 422, {"error": {"message": str(e), "type": "validation_error"}} | |
| code, result = self._decide(schema, state, int(req.get("seed", 42))) | |
| if code != 200: | |
| return code, result | |
| body, completion_tokens = result | |
| answers = {q["id"]: jev_answer(q, body["answers"][q["id"]]) for q in schema["questions"]} | |
| usage = {"input_tokens": body["diagnostics"].get("prompt_tokens") or ( | |
| len(chat_prompt_ids(system_text(schema), state)) if isinstance(state, str) else 0), "output_tokens": completion_tokens} | |
| return 200, {"model": ARGS.model, "answers": answers, "usage": usage, "diagnostics": body["diagnostics"]} | |
| def _systemone_many(self, req, images, many): | |
| chunk, top, eps = MANY_OPTIONS.get("chunk", 20), MANY_OPTIONS.get("top", 3), MANY_OPTIONS.get("eps", 0.02) | |
| t0 = time.time() | |
| qs1, parts = {}, {} | |
| for k, q in req["questions"].items(): | |
| if k not in many: | |
| qs1[k] = q | |
| continue | |
| items = list(q["criteria"].items()) | |
| m = -(-len(items) // chunk) | |
| size = -(-len(items) // m) | |
| parts[k] = [] | |
| for j in range(m): | |
| sub = items[j * size:(j + 1) * size] | |
| if not sub: | |
| continue | |
| if len(sub) == 1: # a single leftover option rides with the previous chunk | |
| parts[k][-1][1].update(sub) | |
| qs1[parts[k][-1][0]]["criteria"] = parts[k][-1][1] | |
| continue | |
| sid = f"{k}__p{j}" | |
| parts[k].append((sid, dict(sub))) | |
| qs1[sid] = {**q, "criteria": dict(sub)} | |
| req1 = {**req, "questions": qs1} | |
| if not MANY_OPTIONS.get("think1", 1): | |
| req1["adaptive"] = False | |
| code, r1 = self._systemone_core(req1, images) | |
| if code != 200: | |
| return self._json(code, r1) | |
| answers = {k: r1["answers"][k] for k in req["questions"] if k not in many} | |
| usage = dict(r1["usage"]) | |
| diag = {"many_options": {}, "round1": r1["diagnostics"]} | |
| for k in many: | |
| p1 = {} | |
| finalists = [] | |
| for sid, crit in parts[k]: | |
| pr = r1["answers"][sid]["probabilities"] | |
| p1.update(pr) | |
| finalists += sorted(crit, key=lambda n: -pr[n])[:top] | |
| if len(finalists) > 26: | |
| finalists = sorted(finalists, key=lambda n: -p1[n])[:26] | |
| order = [n for n in req["questions"][k]["criteria"] if n in set(finalists)] | |
| q2 = {**req["questions"][k], "criteria": {n: req["questions"][k]["criteria"][n] for n in order}} | |
| req2 = {**req, "questions": {k: q2}} | |
| if not MANY_OPTIONS.get("think2", 1): | |
| req2["adaptive"] = False | |
| code, r2 = self._systemone_core(req2, images) | |
| if code != 200: | |
| return self._json(code, r2) | |
| p2 = r2["answers"][k]["probabilities"] | |
| rest = [n for n in req["questions"][k]["criteria"] if n not in p2] | |
| rest_mass = sum(p1[n] for n in rest) or 1.0 | |
| probs = {} | |
| for n in req["questions"][k]["criteria"]: | |
| probs[n] = (1 - eps) * p2[n] if n in p2 else eps * p1[n] / rest_mass | |
| z = sum(probs.values()) | |
| probs = {n: v / z for n, v in probs.items()} | |
| best = max(probs, key=probs.get) | |
| answers[k] = {"type": "choice", "choice": best, "probabilities": probs, "confidence": probs[best]} | |
| usage["input_tokens"] += r2["usage"]["input_tokens"] | |
| usage["output_tokens"] += r2["usage"]["output_tokens"] | |
| M_MANY.inc() | |
| diag["many_options"][k] = {"options": len(probs), "chunks": len(parts[k]), "finalists": len(order)} | |
| diag.setdefault("round2", {})[k] = r2["diagnostics"] | |
| diag["timing"] = {"total_ms": (time.time() - t0) * 1000} | |
| brief = " ".join(str(k) + "=" + str(answers[k]["choice"]) for k in many) | |
| print(f"systemone-many: {brief} {diag['timing']['total_ms']:.0f}ms", flush=True) | |
| self._json(200, {"model": ARGS.model, "answers": answers, "usage": usage, "diagnostics": diag}) | |
| def _systemone_one(self, req, images): | |
| try: | |
| schema = jev_schema(req) | |
| state = jev_state(req, images + jev_images(req.get("images"))) | |
| schema["span_text"] = span_text_of(req.get("state")) | |
| except SchemaError as e: | |
| return self._json( | |
| 422, {"error": {"message": str(e), "type": "validation_error"}} | |
| ) | |
| code, result = self._decide(schema, state, int(req.get("seed", 42))) | |
| if code != 200: | |
| return self._json(code, result) | |
| body, completion_tokens = result | |
| answers = { | |
| q["id"]: jev_answer(q, body["answers"][q["id"]]) | |
| for q in schema["questions"] | |
| } | |
| labels = " ".join(f"{k}={answer_brief(v)}" for k, v in body["answers"].items()) | |
| print( | |
| f"systemone: {labels} " | |
| f"reads={body['diagnostics']['timing']['reads']} " | |
| f"{body['diagnostics']['timing']['total_ms']:.0f}ms", | |
| flush=True, | |
| ) | |
| self._json( | |
| 200, | |
| { | |
| "model": ARGS.model, | |
| "answers": answers, | |
| # vLLM's prompt count when the reads reported one, since it covers | |
| # images. Otherwise the tokenizer's count of the text prompt. | |
| "usage": { | |
| "input_tokens": body["diagnostics"].get("prompt_tokens") | |
| or ( | |
| len(chat_prompt_ids(system_text(schema), state)) | |
| if isinstance(state, str) | |
| else 0 | |
| ), | |
| "output_tokens": completion_tokens, | |
| }, | |
| "diagnostics": body["diagnostics"], | |
| }, | |
| ) | |
| def _chat(self, req): | |
| msgs = req.get("messages") or [] | |
| if ( | |
| len(msgs) != 2 | |
| or msgs[0].get("role") not in ("system", "developer") | |
| or msgs[1].get("role") != "user" | |
| ): | |
| return self._json( | |
| 400, | |
| { | |
| "error": { | |
| "message": ( | |
| "a structured request is exactly two messages: the " | |
| "schema (system) and the state JSON (user)" | |
| ), | |
| "type": "invalid_request_error", | |
| } | |
| }, | |
| ) | |
| try: | |
| schema_value = json.loads(message_text(msgs[0])) | |
| schema = parse_schema(schema_value) | |
| content = msgs[1].get("content", "") | |
| has_image = isinstance(content, list) and any( | |
| isinstance(p, dict) and p.get("type") in ("image_url", "image") | |
| for p in content | |
| ) | |
| if has_image: | |
| # image parts pass through to vLLM unchanged, with text parts as context | |
| state = content | |
| else: | |
| state = message_text(msgs[1]).strip() | |
| schema["span_text"] = span_text_of(json.loads(state)) | |
| except SchemaError as e: | |
| return self._json( | |
| 400, {"error": {"message": str(e), "type": "invalid_request_error"}} | |
| ) | |
| except Exception as e: | |
| return self._json( | |
| 400, | |
| { | |
| "error": { | |
| "message": ( | |
| "system must be a JSON question schema and user " | |
| f"must be JSON state or image parts: {e}" | |
| ), | |
| "type": "invalid_request_error", | |
| } | |
| }, | |
| ) | |
| code, result = self._decide(schema, state, int(req.get("seed", 42))) | |
| if code != 200: | |
| if code == 422: | |
| result["error"]["type"] = "invalid_request_error" | |
| code = 400 | |
| return self._json(code, result) | |
| body, completion_tokens = result | |
| payload = { | |
| "answers": { | |
| q["id"]: chat_answer(q, body["answers"].get(q["id"])) | |
| for q in schema["questions"] | |
| } | |
| } | |
| if req.get("diagnostics") and EXPOSE_DIAGNOSTICS: # only when the server opts in | |
| payload["diagnostics"] = body["diagnostics"] | |
| content = json.dumps(payload, ensure_ascii=False) | |
| prompt_tokens = body["diagnostics"].get("prompt_tokens") or ( | |
| len(chat_prompt_ids(system_text(schema), state)) if isinstance(state, str) else 0 | |
| ) | |
| labels = " ".join(f"{k}={answer_brief(v)}" for k, v in body["answers"].items()) | |
| print( | |
| f"structured: {labels} " | |
| f"reads={body['diagnostics']['timing']['reads']} " | |
| f"{body['diagnostics']['timing']['total_ms']:.0f}ms", | |
| flush=True, | |
| ) | |
| self._json( | |
| 200, | |
| { | |
| "id": f"chatcmpl-{int(time.time() * 1000)}", | |
| "object": "chat.completion", | |
| "created": int(time.time()), | |
| "model": req.get("model", "super-decider"), | |
| "choices": [ | |
| { | |
| "index": 0, | |
| "message": {"role": "assistant", "content": content}, | |
| "finish_reason": "stop", | |
| } | |
| ], | |
| "usage": { | |
| "prompt_tokens": prompt_tokens, | |
| "completion_tokens": completion_tokens, | |
| "total_tokens": prompt_tokens + completion_tokens, | |
| }, | |
| }, | |
| ) | |
| def self_signed(cert_dir): | |
| """Paths of a self-signed certificate and key in cert_dir, made with | |
| openssl on first use.""" | |
| os.makedirs(cert_dir, exist_ok=True) | |
| cert, key = os.path.join(cert_dir, "djev.crt"), os.path.join(cert_dir, "djev.key") | |
| if not (os.path.exists(cert) and os.path.exists(key)): | |
| subprocess.run( | |
| [ | |
| "openssl", | |
| "req", | |
| "-x509", | |
| "-newkey", | |
| "rsa:2048", | |
| "-nodes", | |
| "-days", | |
| "3650", | |
| "-subj", | |
| "/CN=djev", | |
| "-keyout", | |
| key, | |
| "-out", | |
| cert, | |
| ], | |
| check=True, | |
| capture_output=True, | |
| ) | |
| return cert, key | |
| def serve_tls(host, port, cert_dir): | |
| cert, key = self_signed(cert_dir) | |
| ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | |
| ctx.load_cert_chain(cert, key) | |
| srv = Server((host, port), Handler) | |
| srv.socket = ctx.wrap_socket(srv.socket, server_side=True) | |
| threading.Thread(target=srv.serve_forever, daemon=True).start() | |
| return srv | |
| def main(): | |
| global ARGS, CANVAS_LEN, CANVAS_STEP, MAX_SAMPLES | |
| p = argparse.ArgumentParser() | |
| p.add_argument("--upstream", default="http://127.0.0.1:8010") | |
| p.add_argument("--model", default="super-decider") | |
| p.add_argument("--tokenizer", default="/models/super-decider", help="HF id or local path") | |
| p.add_argument("--canvas", type=int, default=64, help="the served canvas length") | |
| p.add_argument( | |
| "--canvas-step", | |
| type=int, | |
| default=16, | |
| help="request widths round up to a multiple of this", | |
| ) | |
| p.add_argument( | |
| "--constrained", | |
| action="store_true", | |
| help="read over the labels only (diffusion_constrained). Needs an " | |
| "engine with vllm-project/vllm#58216", | |
| ) | |
| p.add_argument("--host", default="0.0.0.0") | |
| p.add_argument("--port", type=int, default=8011) | |
| p.add_argument( | |
| "--max-samples", | |
| type=int, | |
| default=32, | |
| help="cap on reads per question. A fixed count must also fit the " | |
| "engine's diffusion_config.max_samples", | |
| ) | |
| p.add_argument( | |
| "--engine-samples", | |
| action="store_true", | |
| help="send a fixed sample count as one diffusion_samples request " | |
| "instead of one request per draw. Needs an engine with " | |
| "vllm-project/vllm#58438", | |
| ) | |
| p.add_argument( | |
| "--tls-port", type=int, default=0, help="also listen with HTTPS here (0 = off)" | |
| ) | |
| p.add_argument( | |
| "--pages", | |
| default="", | |
| help="serve DIR/name.html at GET /name and DIR/index.html at / " | |
| "(off when empty)", | |
| ) | |
| p.add_argument( | |
| "--cert-dir", | |
| default=os.path.expanduser("~/.cache/djev"), | |
| help="directory for the self-signed certificate", | |
| ) | |
| ARGS = p.parse_args() | |
| CANVAS_LEN = ARGS.canvas | |
| CANVAS_STEP = ARGS.canvas_step | |
| MAX_SAMPLES = max(1, ARGS.max_samples) | |
| init_tokenizer(AutoTokenizer.from_pretrained(ARGS.tokenizer)) | |
| if ARGS.tls_port: | |
| serve_tls(ARGS.host, ARGS.tls_port, ARGS.cert_dir) | |
| print( | |
| f"structured server https on {ARGS.host}:{ARGS.tls_port} (self-signed)", | |
| flush=True, | |
| ) | |
| print( | |
| f"structured server on {ARGS.host}:{ARGS.port} -> {ARGS.upstream} " | |
| f"(canvas {CANVAS_LEN})", | |
| flush=True, | |
| ) | |
| Server((ARGS.host, ARGS.port), Handler).serve_forever() | |
| # djev-bidir-server | |
| BIDIR = os.environ.get("DJEV_BIDIR", "") | |
| BIDIR_MAXTOK = int(os.environ.get("DJEV_BIDIR_MAXTOK", "0") or 0) | |
| BIDIR_MAXPROMPT = int(os.environ.get("DJEV_BIDIR_MAXPROMPT", "8192") or 0) # longer prompts are read causally: with ranges the | |
| # attention kernel loses its causal tile skip, and on 30-40K-token prompts (BRIGHT/ToolRet) that is a 2.7x slowdown | |
| BIDIR_SALT = "djev-bq-" + BIDIR | |
| _BIDIR_REST = {} # hash(user content) -> length of the questions block text at its end | |
| _BIDIR_IDS = {} # hash(tuple(chat prompt ids)) -> (a, b) | |
| _BIDIR_STATS = {"chat": 0, "completions": 0, "none": 0, "capped": 0, "over_2496": 0} | |
| _chat_messages_causal = chat_messages | |
| def chat_messages(sys_text, state_content): | |
| msgs = _chat_messages_causal(sys_text, state_content) | |
| if BIDIR and isinstance(state_content, str) and len(msgs) == 2 and msgs[0]["content"] == DECISION_PREAMBLE.rstrip("\n"): | |
| user = msgs[1]["content"] | |
| if isinstance(user, str) and user.startswith(state_content + "\n\n"): | |
| if len(_BIDIR_REST) > 20000: | |
| _BIDIR_REST.clear() | |
| _BIDIR_REST[hash(user)] = len(user) - len(state_content) - 2 | |
| return msgs | |
| def _bidir_range(messages, thinking): | |
| user = messages[-1]["content"] | |
| n = _BIDIR_REST.get(hash(user)) if isinstance(user, str) else None | |
| if not n: | |
| return None, None | |
| text = TOK.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=thinking) | |
| enc = TOK(text, add_special_tokens=False, return_offsets_mapping=True) | |
| if BIDIR_MAXPROMPT and len(enc["input_ids"]) > BIDIR_MAXPROMPT: | |
| _BIDIR_STATS["long_causal"] = _BIDIR_STATS.get("long_causal", 0) + 1 | |
| return enc["input_ids"], None | |
| e = text.rfind(user) + len(user); a = e - n | |
| idx = [i for i, (x, y) in enumerate(enc["offset_mapping"]) if x >= a and y <= e and y > x] | |
| if not idx or idx[-1] - idx[0] + 1 != len(idx): | |
| return enc["input_ids"], None | |
| rg = (idx[0], idx[-1] + 1) | |
| if BIDIR_MAXTOK and rg[1] - rg[0] > BIDIR_MAXTOK: | |
| _BIDIR_STATS["capped"] += 1 | |
| return enc["input_ids"], None | |
| if BIDIR == "all": | |
| return enc["input_ids"], [(0, len(enc["input_ids"]))] | |
| if BIDIR == "qsep": # one range per question (question line + its options), causal across questions | |
| rest = user[-n:] | |
| starts = [m.start() for m in re.finditer(r"(?m)^Question \S+: ", rest)] | |
| endq = rest.rfind("\n\nReply with") | |
| endq = endq if endq > 0 else len(rest) | |
| out = [] | |
| for k, s0 in enumerate(starts): | |
| c0, c1 = a + s0, a + (starts[k + 1] - 2 if k + 1 < len(starts) else endq) | |
| jdx = [i for i, (x, y) in enumerate(enc["offset_mapping"]) if x >= c0 and y <= c1 and y > x] | |
| if jdx and jdx[-1] - jdx[0] + 1 == len(jdx) and jdx[-1] > jdx[0]: | |
| out.append((jdx[0], jdx[-1] + 1)) | |
| return enc["input_ids"], (out or None) | |
| if BIDIR == "qdoc": | |
| d0 = text.rfind(user) # user = state + "\n\n" + questions block; the state is the document block | |
| d1 = d0 + len(user) - n - 2 | |
| jdx = [i for i, (x, y) in enumerate(enc["offset_mapping"]) if x >= d0 and y <= d1 and y > x] | |
| if jdx and jdx[-1] - jdx[0] + 1 == len(jdx) and jdx[-1] < rg[0]: | |
| return enc["input_ids"], [(jdx[0], jdx[-1] + 1), rg] | |
| return enc["input_ids"], [rg] | |
| def _bidir_attach(body, rg, kind): | |
| if rg is None: | |
| _BIDIR_STATS["none"] += 1 | |
| return body | |
| _BIDIR_STATS[kind] += 1 | |
| if max(r[1] - r[0] for r in rg) > 2496: # vLLM's default prefill chunk here; longer blocks get split across chunks | |
| _BIDIR_STATS["over_2496"] += 1 | |
| if (_BIDIR_STATS["chat"] + _BIDIR_STATS["completions"]) % 500 == 0: | |
| print("djev-bidir stats", _BIDIR_STATS, flush=True) | |
| return dict(body, cache_salt=BIDIR_SALT, vllm_xargs=dict(body.get("vllm_xargs") or {}, djev_bidir=[int(x) for a, b in rg for x in (a, b)])) # flat [a1, b1, a2, b2, ...]: vLLM rejects nested lists | |
| _chat_prompt_ids_causal = chat_prompt_ids | |
| def chat_prompt_ids(sys_text, state_text, thinking=False): | |
| ids = _chat_prompt_ids_causal(sys_text, state_text, thinking) | |
| if BIDIR: | |
| enc_ids, rg = _bidir_range(chat_messages(sys_text, state_text), thinking) | |
| if rg is not None and list(enc_ids) == ids: | |
| if len(_BIDIR_IDS) > 20000: | |
| _BIDIR_IDS.clear() | |
| _BIDIR_IDS[hash(tuple(ids))] = rg | |
| return ids | |
| _upstream_chat_causal = upstream_chat | |
| def upstream_chat(body, timeout=600): | |
| if BIDIR and isinstance(body.get("messages"), list) and body["messages"]: | |
| thinking = bool((body.get("chat_template_kwargs") or {}).get("enable_thinking", False)) | |
| _, rg = _bidir_range(body["messages"], thinking) | |
| body = _bidir_attach(body, rg, "chat") | |
| return _upstream_chat_causal(body, timeout) | |
| BIDIR_THINK = os.environ.get("DJEV_BIDIR_THINK", "bidir") # "causal": thought generation reads the prompt causally | |
| _upstream_completions_causal = upstream_completions | |
| def upstream_completions(body, timeout=600): | |
| pr = body.get("prompt") | |
| if BIDIR and BIDIR_THINK == "causal" and body.get("stop_token_ids") and not body.get("logprob_token_ids"): | |
| _BIDIR_STATS["think_causal"] = _BIDIR_STATS.get("think_causal", 0) + 1 | |
| return _upstream_completions_causal(dict(body, cache_salt=BIDIR_SALT + "-thinkcausal"), timeout) | |
| if BIDIR and isinstance(pr, list) and pr and isinstance(pr[0], int): | |
| rg = None | |
| cuts = [i for i, t in enumerate(pr) if THOUGHT_OPEN and t == THOUGHT_OPEN[0]] + [len(pr)] | |
| for c in cuts: | |
| rg = _BIDIR_IDS.get(hash(tuple(pr[:c]))) | |
| if rg is not None: | |
| break | |
| body = _bidir_attach(body, rg, "completions") | |
| return _upstream_completions_causal(body, timeout) | |
| if __name__ == "__main__": | |
| main() | |