Spaces:
Running on Zero
Running on Zero
File size: 3,764 Bytes
db45a11 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | """JEV-9B Decision API — one clean JSON call over System 1.
Inference core (`jev_core.py`) is the model authors' documented `transformers` + `peft`
path, reused verbatim from the official demo Space (Apache-2.0). This file only wraps it
in a single machine-friendly Gradio endpoint (`/decide`) that returns JSON, instead of the
upstream multi-question HTML UI, so a local agent can call it headlessly.
ZeroGPU rules honoured here:
* `import spaces` MUST precede torch / transformers / any CUDA-touching import
(jev_core imports torch, so `spaces` is imported before it).
* models are placed on `cuda` at MODULE level (inside jev_core, at import) — not
inside the `@spaces.GPU` function.
"""
import os
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False")
import spaces # noqa: E402 -- MUST precede torch / transformers / any CUDA-touching import
import json # noqa: E402
import gradio as gr # noqa: E402
import jev_core as jc # noqa: E402
def _clean_options(raw) -> list:
"""Accept a comma/newline-separated string (or a list) -> list of option strings."""
if isinstance(raw, (list, tuple)):
items = [str(o).strip() for o in raw]
else:
items = [o.strip() for o in str(raw or "").replace("\n", ",").split(",")]
return [o for o in items if o]
@spaces.GPU(duration=45)
def decide(kind: str, state: str, question: str, options: str = "") -> str:
"""Answer ONE typed System 1 decision; return calibrated probabilities as JSON.
kind: "noul" | "choice" | "score"
state: the text to reason about (truncated to 1024 tokens, head 60% / tail 40%)
question: the decision question
options: comma-separated options (choice only, 2-16); ignored for noul/score
"""
kind = (kind or "").strip()
state = (state or "").strip()
question = (question or "").strip()
if kind not in jc.KINDS:
return json.dumps({"error": f"kind must be one of {list(jc.KINDS)}"})
if not state:
return json.dumps({"error": "state is required"})
if not question:
return json.dumps({"error": "question is required"})
opts = _clean_options(options)
if kind == "choice":
if not 2 <= len(opts) <= 16:
return json.dumps({"error": "choice needs 2-16 comma-separated options"})
if len(set(opts)) != len(opts):
return json.dumps({"error": "choice options must be unique"})
else:
opts = None
state_used, truncated, n_tok = jc.truncate_state(state)
dist = jc.decide(kind, state_used, question, opts)
best = max(dist, key=dist.get)
return json.dumps(
{
"kind": kind,
"distribution": dist,
"best": best,
"confidence": dist[best],
"state_tokens": n_tok,
"truncated": truncated,
}
)
with gr.Blocks(title="JEV-9B Decision API") as demo:
gr.Markdown(
"## JEV-9B System 1 — typed decisions in one prefill pass\n"
"`noul` (yes/no) · `choice` (2–16 options) · `score` (0–5). "
"Returns a calibrated probability distribution as JSON."
)
with gr.Row():
kind_in = gr.Dropdown(list(jc.KINDS), value="noul", label="kind")
state_in = gr.Textbox(label="state", lines=3)
question_in = gr.Textbox(label="question")
options_in = gr.Textbox(label="options (comma-separated; choice only)")
out_json = gr.Code(label="decision JSON", language="json")
btn = gr.Button("Decide", variant="primary")
btn.click(
decide,
inputs=[kind_in, state_in, question_in, options_in],
outputs=[out_json],
api_name="decide",
)
demo.launch()
|