jeanbatuli's picture
Sync from GitHub main
f4d4613 verified
Raw
History Blame Contribute Delete
8.27 kB
from __future__ import annotations
import json
import sys
import tempfile
from pathlib import Path
from typing import Any
import gradio as gr
import numpy as np
import spaces
ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT / "src"))
from limen_runtime_audit import audit_arrays # noqa: E402
MAX_UPLOAD_BYTES = 250 * 1024 * 1024
def _create_demo_file() -> Path:
"""Create a deterministic synthetic trajectory for interface testing."""
demo_dir = Path(tempfile.gettempdir()) / "limen-runtime-audit"
demo_dir.mkdir(parents=True, exist_ok=True)
demo_path = demo_dir / "demo_trajectory.npz"
rng = np.random.default_rng(20260725)
n_tokens, n_layers, hidden_dim, vocabulary = 16, 12, 32, 64
layer_drift = rng.normal(0.0, 0.10, size=(n_tokens, n_layers, hidden_dim))
token_context = rng.normal(0.0, 0.20, size=(n_tokens, 1, hidden_dim))
hidden_states = (token_context + np.cumsum(layer_drift, axis=1)).astype(np.float32)
logits = rng.normal(0.0, 1.0, size=(n_tokens, vocabulary)).astype(np.float32)
np.savez_compressed(
demo_path,
hidden_states=hidden_states,
logits=logits,
)
return demo_path
DEMO_PATH = _create_demo_file()
DEMO_METADATA = json.dumps(
{
"model": "synthetic-demonstration",
"prompt_id": "demo-001",
"note": "Deterministic synthetic arrays; not measurements from a real model.",
}
)
PLAIN_LANGUAGE = {
"step_norm": "How far the representation moves between consecutive tokens",
"curvature": "How sharply the trajectory changes direction",
"residual_ratio": "How much movement remains after removing the dominant depth profile",
"entropy": "How uncertain the model output distribution is",
"margin": "Gap between the two most likely output tokens",
}
def _format_number(value: Any) -> str:
if value is None:
return "β€”"
try:
number = float(value)
except (TypeError, ValueError):
return str(value)
if not np.isfinite(number):
return "β€”"
return f"{number:.6g}"
def _summary_rows(audit: dict[str, Any]) -> list[list[Any]]:
rows: list[list[Any]] = []
metrics = audit.get("summary", {})
if not isinstance(metrics, dict):
return rows
for name, values in metrics.items():
if not isinstance(values, dict):
continue
if not any(key in values for key in ("median", "mean", "q1", "q3", "n")):
continue
rows.append([
name,
PLAIN_LANGUAGE.get(name, "Descriptive measurement"),
values.get("n", "β€”"),
_format_number(values.get("median", values.get("mean"))),
_format_number(values.get("q1")),
_format_number(values.get("q3")),
])
return rows
def _markdown_report(audit: dict[str, Any], source_name: str) -> str:
rows = _summary_rows(audit)
lines = [
"# LIMEN Runtime Audit",
"",
f"Source: `{source_name}`",
"",
"This report describes activation-trajectory geometry. It does not establish "
"functional localization, causality, reasoning ability, or correctness.",
"",
"| Metric | Plain-language meaning | N | Median | Q1 | Q3 |",
"|---|---|---:|---:|---:|---:|",
]
for row in rows:
lines.append("| " + " | ".join(str(value).replace("|", "\\|") for value in row) + " |")
if not rows:
lines.append("| No summary metric found | β€” | β€” | β€” | β€” | β€” |")
lines.extend([
"",
"## Interpretation boundary",
"",
"These measurements are descriptive signals. Comparisons require matched "
"prompts, extraction settings, model revisions, and appropriate controls.",
"",
])
return "\n".join(lines)
@spaces.GPU(duration=5)
def zerogpu_probe() -> str:
"""Minimal registered function required by a ZeroGPU-configured Space."""
return "ZeroGPU runtime available"
def run_audit(file_path: str | None, metadata_text: str) -> tuple:
if not file_path:
raise gr.Error("Choose a trajectory.npz file first.")
source = Path(file_path)
if source.stat().st_size > MAX_UPLOAD_BYTES:
raise gr.Error("The file is larger than the 250 MB Space limit.")
try:
metadata = json.loads(metadata_text) if metadata_text.strip() else {}
except json.JSONDecodeError as exc:
raise gr.Error(f"Metadata must be valid JSON: {exc.msg}") from exc
if not isinstance(metadata, dict):
raise gr.Error("Metadata JSON must be an object.")
try:
with np.load(source, allow_pickle=False) as data:
if "hidden_states" not in data.files:
raise gr.Error("The NPZ file must contain a 'hidden_states' array.")
hidden_states = np.asarray(data["hidden_states"])
logits = np.asarray(data["logits"]) if "logits" in data.files else None
audit = audit_arrays(hidden_states, logits=logits, metadata=metadata)
except gr.Error:
raise
except Exception as exc:
raise gr.Error(f"Audit failed: {type(exc).__name__}: {exc}") from exc
output_dir = Path(tempfile.mkdtemp(prefix="limen-audit-"))
json_path = output_dir / "audit.json"
report_path = output_dir / "report.md"
json_path.write_text(json.dumps(audit, indent=2, ensure_ascii=False), encoding="utf-8")
report_path.write_text(_markdown_report(audit, source.name), encoding="utf-8")
return _summary_rows(audit), audit, str(json_path), str(report_path)
with gr.Blocks(title="LIMEN Runtime Audit") as demo:
gr.Markdown("""
# LIMEN Runtime Audit
Upload a `trajectory.npz` produced from an open transformer model. The file must
contain `hidden_states` with shape `[tokens, layers, hidden_dim]`; `logits`
with shape `[tokens, vocabulary]` is optional.
The tool reports descriptive measurements of how activations evolve across
tokens and layers. It does **not** prove where a function is located, why the
model answered, or whether a representation caused an output.
""")
with gr.Row():
trajectory = gr.File(
label="Trajectory file (.npz)",
file_types=[".npz"],
type="filepath",
)
metadata = gr.Textbox(
label="Optional metadata (JSON)",
value='{"model": "model-name", "prompt_id": "example-001"}',
lines=5,
)
gr.Markdown(
"**No trajectory yet?** Download the synthetic demonstration file below, "
"or click the example to load it automatically. It verifies the interface "
"but is not a scientific model result."
)
demo_download = gr.File(
value=str(DEMO_PATH),
label="Download demo_trajectory.npz",
interactive=False,
)
gr.Examples(
examples=[[str(DEMO_PATH), DEMO_METADATA]],
inputs=[trajectory, metadata],
label="Ready-to-use demonstration",
)
audit_button = gr.Button("Run descriptive audit", variant="primary")
summary = gr.Dataframe(
headers=["Metric", "Plain-language meaning", "N", "Median", "Q1", "Q3"],
datatype=["str", "str", "str", "str", "str", "str"],
interactive=False,
label="Summary",
)
raw_json = gr.JSON(label="Complete audit")
with gr.Row():
json_download = gr.File(label="Download audit.json")
report_download = gr.File(label="Download report.md")
audit_button.click(
fn=run_audit,
inputs=[trajectory, metadata],
outputs=[summary, raw_json, json_download, report_download],
show_progress="full",
)
# Register a real ZeroGPU endpoint without moving file-based NumPy auditing
# into the ephemeral GPU worker.
gpu_probe = gr.Button("ZeroGPU probe", visible=False)
gpu_probe_output = gr.Textbox(visible=False)
gpu_probe.click(fn=zerogpu_probe, outputs=gpu_probe_output)
gr.Markdown("""
### Responsible interpretation
Compare matched runs and keep the model revision, prompt, tokenization and
extraction protocol fixed. Decodability is not functional localization, and a
static geometric pattern is not automatically a dynamic mechanism.
""")
if __name__ == "__main__":
demo.launch()