q-prime-demo / app.py
Sam-QGI-dev's picture
Initial Q-Prime demo Space
6cfdd06 verified
Raw
History Blame Contribute Delete
9.81 kB
"""Q-Prime demo Space (Gradio).
Calls the QGI managed API; does not run any model weights locally.
Set QGI_API_KEY and QGI_API_URL as Space Secrets before launch.
"""
from __future__ import annotations
import os
import time
from dataclasses import dataclass
import gradio as gr
import requests
API_URL = os.environ.get("QGI_API_URL", "https://api.qgi.dev/v1/qprime/embed")
API_KEY = os.environ.get("QGI_API_KEY", "")
MODEL_URL = "https://huggingface.co/QGI-dev/q-prime"
PAPER_URL = "https://huggingface.co/QGI-dev/q-prime#accompanying-papers-qag-series"
GATED_URL = "https://huggingface.co/QGI-dev/q-prime" # has Request access button
DEFAULT_LABELS = (
"mandatory, prohibitive, conditional, default, exception, null"
)
DEFAULT_A = "A regulated entity must report any incident within 72 hours."
DEFAULT_B = (
"A regulated entity must not report an incident while a law-enforcement "
"investigation is active."
)
RATE_WINDOW_SECS = 60
RATE_MAX_CALLS = 30
_session_log: dict[str, list[float]] = {}
@dataclass
class Signals:
relevance: list[float]
overlap: float
conflict: float
predicates: list[str]
born_rule: dict[str, float]
def _rate_limit(session_id: str) -> bool:
now = time.time()
hits = _session_log.setdefault(session_id, [])
hits[:] = [t for t in hits if now - t < RATE_WINDOW_SECS]
if len(hits) >= RATE_MAX_CALLS:
return False
hits.append(now)
return True
def _call_api(clause_a: str, clause_b: str, labels: list[str]) -> Signals:
if not API_KEY:
raise gr.Error(
"This demo is not yet connected to the Q-Prime API. "
"Join the waitlist at https://qgi.dev or request evaluation access "
f"at {GATED_URL}."
)
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"User-Agent": "qprime-demo-space/1.0",
}
payload = {
"inputs": [clause_a, clause_b],
"tasks": ["relevance", "conflict", "overlap", "predicate", "classify"],
"classify_labels": labels,
}
try:
r = requests.post(API_URL, headers=headers, json=payload, timeout=30)
except requests.RequestException as exc:
raise gr.Error(f"Q-Prime API unreachable: {exc}") from exc
if r.status_code == 401:
raise gr.Error(
"Space's QGI_API_KEY was rejected. QGI staff: rotate in Space secrets."
)
if r.status_code == 429:
raise gr.Error("Q-Prime API rate limit hit. Try again in a moment.")
if r.status_code >= 400:
raise gr.Error(f"Q-Prime API error {r.status_code}: {r.text[:200]}")
data = r.json()
return Signals(
relevance=data.get("relevance", [0.0, 0.0]),
overlap=float(data.get("overlap", [[0.0]])[0][1]),
conflict=float(data.get("conflict", [[0.0]])[0][1]),
predicates=data.get("predicate", [[], []])[0],
born_rule=data.get("classify", [{}])[0],
)
def _format_signed_meter(name: str, value: float) -> str:
v = max(-1.0, min(1.0, value))
pos_pct = max(0.0, v) * 100.0
neg_pct = max(0.0, -v) * 100.0
colour = "#2E4A8F" if v >= 0 else "#B03A2E"
if v > 0.05:
label = "reinforce"
elif v < -0.05:
label = "contradict"
else:
label = "neutral"
track_style = (
"flex:1;display:flex;height:18px;background:#f4f4f6;"
"border-radius:4px;overflow:hidden"
)
left_half_style = "width:50%;display:flex;justify-content:flex-end"
right_half_style = "width:50%;display:flex;justify-content:flex-start"
left_fill = (
f"<div style='height:100%;width:{neg_pct:.1f}%;background:{colour}'></div>"
)
right_fill = (
f"<div style='height:100%;width:{pos_pct:.1f}%;background:{colour}'></div>"
)
return (
"<div style='display:flex;align-items:center;gap:8px;margin:4px 0'>"
f"<div style='width:150px;font-weight:600'>{name}</div>"
f"<div style='{track_style}'>"
f"<div style='{left_half_style}'>{left_fill}</div>"
f"<div style='{right_half_style}'>{right_fill}</div>"
"</div>"
"<div style='width:150px;text-align:right;"
"font-variant-numeric:tabular-nums'>"
f"{v:+.3f} <em style='color:#666'>({label})</em>"
"</div>"
"</div>"
)
def _format_probs(probs: dict[str, float]) -> str:
if not probs:
return "<em>(no classification returned)</em>"
rows = sorted(probs.items(), key=lambda kv: -kv[1])
out = ["<table style='border-collapse:collapse;width:100%'>"]
for label, p in rows:
p = max(0.0, min(1.0, float(p)))
out.append(
"<tr>"
f"<td style='padding:2px 8px;font-weight:600'>{label}</td>"
"<td style='padding:2px 8px;width:70%'>"
f"<div style='height:12px;background:#f4f4f6;border-radius:3px'>"
f"<div style='height:100%;width:{p*100:.1f}%;background:#2E4A8F;border-radius:3px'></div>"
"</div></td>"
f"<td style='padding:2px 8px;font-variant-numeric:tabular-nums'>{p:.3f}</td>"
"</tr>"
)
out.append("</table>")
return "".join(out)
def compare(clause_a: str, clause_b: str, labels_raw: str, request: gr.Request):
if not clause_a.strip() or not clause_b.strip():
raise gr.Error("Both clauses must be non-empty.")
if len(clause_a) > 2000 or len(clause_b) > 2000:
raise gr.Error("Clauses limited to 2000 chars in the demo.")
session_id = getattr(request, "session_hash", "default") or "default"
if not _rate_limit(session_id):
raise gr.Error(
f"Demo rate limit hit ({RATE_MAX_CALLS} calls / {RATE_WINDOW_SECS}s). "
f"Request a full evaluation key at {GATED_URL}."
)
labels = [x.strip() for x in labels_raw.split(",") if x.strip()]
sig = _call_api(clause_a, clause_b, labels)
header = "<h3 style='margin-bottom:4px'>Intelligence signals</h3>"
signed = _format_signed_meter("Conflict (signed)", sig.conflict)
overlap = _format_signed_meter("Overlap", sig.overlap)
rel_a = _format_signed_meter("Relevance A", sig.relevance[0] if sig.relevance else 0.0)
rel_b = _format_signed_meter("Relevance B", sig.relevance[1] if len(sig.relevance) > 1 else 0.0)
predicate_html = (
"<h4 style='margin-top:14px'>Predicates (clause A)</h4><ul>"
+ "".join(f"<li><code>{p}</code></li>" for p in sig.predicates)
+ "</ul>"
if sig.predicates
else "<h4 style='margin-top:14px'>Predicates (clause A)</h4><em>none</em>"
)
classify_html = (
"<h4 style='margin-top:14px'>Born-rule classifier (clause A)</h4>"
+ _format_probs(sig.born_rule)
)
return header + signed + overlap + rel_a + rel_b + predicate_html + classify_html
def _disabled_banner() -> str:
if API_KEY:
return ""
return (
"<div style='padding:10px 14px;background:#fff3cd;border:1px solid #ffeeba;"
"border-radius:6px;margin-bottom:12px;color:#664d03'>"
"<strong>Demo is inactive.</strong> This Space ships without a live key. "
f"Request an evaluation API key via the <a href='{GATED_URL}'>Q-Prime model "
"card</a> to try the full API, or read the "
f"<a href='{PAPER_URL}'>accompanying papers</a>."
"</div>"
)
EXAMPLES = [
[
DEFAULT_A,
DEFAULT_B,
DEFAULT_LABELS,
],
[
"All employees shall complete annual security training.",
"Employees on medical leave are exempt from annual security training.",
DEFAULT_LABELS,
],
[
"Customer data must be encrypted at rest using AES-256.",
"Customer data may be stored in plaintext in development environments.",
DEFAULT_LABELS,
],
]
with gr.Blocks(
title="Q-Prime — QAG conflict and intelligence signals",
theme=gr.themes.Soft(primary_hue="blue"),
) as demo:
gr.Markdown(
f"""
# Q-Prime — live QAG signals
Enter two rule-bearing clauses and the demo calls the Q-Prime API to return the
signed conflict signal, the relevance scores, the overlap, predicates, and a
zero-shot Born-rule classification.
Clauses never leave QGI infrastructure; this Space forwards them to the QGI
managed API. The model weights are not distributed.
Model card: [{MODEL_URL}]({MODEL_URL}) · Papers: [QAG series]({PAPER_URL})
"""
)
gr.HTML(_disabled_banner())
with gr.Row():
with gr.Column():
in_a = gr.Textbox(
label="Clause A",
value=DEFAULT_A,
lines=3,
placeholder="A regulated entity must report ...",
)
in_b = gr.Textbox(
label="Clause B",
value=DEFAULT_B,
lines=3,
placeholder="A regulated entity must not ...",
)
in_labels = gr.Textbox(
label="Zero-shot labels (comma-separated)",
value=DEFAULT_LABELS,
lines=1,
)
btn = gr.Button("Compare", variant="primary")
with gr.Column():
out = gr.HTML(
"<em>Results will appear here.</em>",
elem_id="qp-signals",
)
gr.Examples(
examples=EXAMPLES,
inputs=[in_a, in_b, in_labels],
label="Try one",
)
btn.click(fn=compare, inputs=[in_a, in_b, in_labels], outputs=out)
gr.Markdown(
f"""
---
Q-Prime is a commercial model licensed under the QGI Commercial Model License v1.0.
[Request evaluation access]({GATED_URL}) · `contact@qgi.dev` · © 2025–2026 Quantum General Intelligence, Inc.
"""
)
if __name__ == "__main__":
demo.queue(max_size=32).launch()