glass-box-agent / app.py
ykirpichev's picture
Upload app.py with huggingface_hub
d84d2df verified
Raw
History Blame Contribute Delete
97.9 kB
import html
import json
import os
import re
import tempfile
import time
import urllib.error
import urllib.request
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
import gradio as gr
from huggingface_hub import InferenceClient
APP_TITLE = "Glass-Box Agent"
APP_TAGLINE = "A tiny ReAct agent whose trace is a living, forkable tree."
MAX_STEPS = 6
DEFAULT_MODEL_ID = os.getenv("GLASS_BOX_MODEL_ID", "Qwen/Qwen2.5-7B-Instruct")
BACKEND_OFFLINE = "Offline heuristic"
BACKEND_HF = "HF Inference small model"
BACKEND_MODAL = "Modal served model"
MODAL_APP_NAME = os.getenv("GLASS_BOX_MODAL_APP", "glass-box-agent-modal")
MODAL_MODEL_FUNCTION = os.getenv("GLASS_BOX_MODAL_MODEL_FUNCTION", "reason_next")
MODAL_TRACE_FUNCTION = os.getenv("GLASS_BOX_MODAL_TRACE_FUNCTION", "store_trace")
MODAL_MODEL_URL = os.getenv("GLASS_BOX_MODAL_MODEL_URL", "")
MODAL_TRACE_URL = os.getenv("GLASS_BOX_MODAL_TRACE_URL", "")
MODAL_TUNE_FUNCTION = os.getenv("GLASS_BOX_MODAL_TUNE_FUNCTION", "start_rl_tune")
MODAL_TUNE_URL = os.getenv("GLASS_BOX_MODAL_TUNE_URL", "")
MODAL_TRACE_ENABLED = bool(MODAL_TRACE_URL or os.getenv("GLASS_BOX_MODAL_TRACE_ENABLED"))
START_TASK = "Task intake"
START_RESEARCH = "Research / evidence"
START_PROTOTYPE = "Prototype sketch"
START_CRITIQUE = "Critique dead end"
START_VALIDATE = "Validation pass"
START_SYNTHESIS = "Final synthesis"
START_MODES = [
START_TASK,
START_RESEARCH,
START_PROTOTYPE,
START_CRITIQUE,
START_VALIDATE,
START_SYNTHESIS,
]
LABELS = [
"useful",
"weak",
"wrong",
"unsafe",
"redundant",
"dead_end",
"backtrack_good",
"fork_winner",
"fork_loser",
"delightful",
]
DATASET_EXAMPLES = [
{
"name": "Transparent research agent",
"task": "Compare two designs for a transparent small-model research agent and choose the one users can trust.",
"start_phase": START_RESEARCH,
"steps": 4,
"feedback_note": "The preferred branch cites visible evidence and is easier to audit.",
},
{
"name": "Hackathon demo critique",
"task": "Improve a hackathon demo so judges can feel the agent's reasoning, not just read a trace.",
"start_phase": START_CRITIQUE,
"steps": 5,
"feedback_note": "The preferred branch makes the fork/retry moment more immediate.",
},
{
"name": "Small model math workflow",
"task": "Plan a small-model assistant that calculates totals, exposes mistakes, and lets users retry weak steps.",
"start_phase": START_TASK,
"steps": 4,
"feedback_note": "The preferred branch separates computation from explanation.",
},
{
"name": "RL feedback product loop",
"task": "Design a product loop where human trace labels become finetuning and RL preference data.",
"start_phase": START_PROTOTYPE,
"steps": 5,
"feedback_note": "The preferred branch turns user edits into reusable training data.",
},
]
DATASET_NAMES = [example["name"] for example in DATASET_EXAMPLES]
@dataclass
class TraceNode:
id: str
parent: str | None
kind: str
title: str
body: str
status: str = "done"
depth: int = 0
timeline: str = "main"
created_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
meta: dict[str, Any] = field(default_factory=dict)
def new_state() -> dict[str, Any]:
root = TraceNode(
id="root",
parent=None,
kind="task",
title="Waiting for task",
body="Enter a task and press Run. Every thought, tool call, observation, and fork will appear here.",
status="idle",
depth=0,
)
return {
"nodes": [root.__dict__],
"selected_id": "root",
"active_tip": "root",
"timeline_count": 1,
"transcript": [],
"task": "",
"model_note": "offline heuristic ReAct engine",
"marked_ids": [],
"feedback": [],
"trace_id": f"trace-{uuid.uuid4().hex[:10]}",
"modal_store": {},
"action_log": "Select a node from the trace table, then retry, replay, or label it.",
}
def reset_state(task: str, model_note: str | None = None) -> dict[str, Any]:
task = task.strip() or "Plan a delightful tiny AI demo for the hackathon."
root = TraceNode(
id="root",
parent=None,
kind="task",
title="Task",
body=task,
status="running",
depth=0,
meta={"task": task},
)
return {
"nodes": [root.__dict__],
"selected_id": "root",
"active_tip": "root",
"timeline_count": 1,
"transcript": [f"Task: {task}"],
"task": task,
"model_note": model_note or "offline heuristic ReAct engine",
"marked_ids": [],
"feedback": [],
"trace_id": f"trace-{uuid.uuid4().hex[:10]}",
"modal_store": {},
"action_log": "Trace started. Select any node to operate on it.",
}
def node_by_id(state: dict[str, Any], node_id: str) -> dict[str, Any] | None:
return next((node for node in state["nodes"] if node["id"] == node_id), None)
def children_by_parent(nodes: list[dict[str, Any]]) -> dict[str | None, list[dict[str, Any]]]:
children: dict[str | None, list[dict[str, Any]]] = {}
for node in nodes:
children.setdefault(node["parent"], []).append(node)
return children
def descendant_nodes(state: dict[str, Any], node_id: str) -> list[dict[str, Any]]:
children = children_by_parent(state.get("nodes", []))
descendants: list[dict[str, Any]] = []
def walk(parent: str) -> None:
for child in children.get(parent, []):
descendants.append(child)
walk(child["id"])
walk(node_id)
return descendants
def upstream_nodes(state: dict[str, Any], node_id: str) -> list[dict[str, Any]]:
nodes: list[dict[str, Any]] = []
current = node_by_id(state, node_id)
while current:
nodes.append(current)
parent = current.get("parent")
current = node_by_id(state, parent) if parent else None
return list(reversed(nodes))
def node_labels(node: dict[str, Any]) -> list[str]:
labels = node.get("meta", {}).get("labels", [])
return labels if isinstance(labels, list) else []
def mark_node(state: dict[str, Any], node_id: str, label: str) -> str:
node = node_by_id(state, node_id)
if not node:
return "No node selected."
node.setdefault("meta", {})
labels = node_labels(node)
if label and label not in labels:
labels.append(label)
node["meta"]["labels"] = labels
marked = state.setdefault("marked_ids", [])
if node_id not in marked:
marked.append(node_id)
state["selected_id"] = node_id
return f"Marked {node['title']} as {label}."
def mark_summary(state: dict[str, Any]) -> str:
marked = state.get("marked_ids", [])
if not marked:
return "No marked nodes yet."
lines = []
for node_id in marked:
node = node_by_id(state, node_id)
if node:
labels = ", ".join(node_labels(node)) or "marked"
lines.append(f"{node['title']} ({node['kind']}): {labels}")
return "\n".join(lines) or "No marked nodes yet."
def dataset_example(name: str) -> dict[str, Any]:
return next((example for example in DATASET_EXAMPLES if example["name"] == name), DATASET_EXAMPLES[0])
def load_dataset_example(name: str):
example = dataset_example(name)
return (
example["task"],
int(example["steps"]),
example["start_phase"],
example["feedback_note"],
f"Loaded dataset example: {example['name']}. Press Run trace to inspect it.",
)
def dataset_result_rows(results: list[dict[str, Any]]) -> list[list[str]]:
return [
[
item.get("name", ""),
str(item.get("nodes", 0)),
str(item.get("preference_pairs", 0)),
item.get("selected_label", ""),
item.get("trace_id", ""),
]
for item in results
]
def node_training_view(node: dict[str, Any] | None) -> dict[str, Any]:
if not node:
return {}
return {
"id": node.get("id"),
"kind": node.get("kind"),
"title": node.get("title"),
"body": node.get("body"),
"timeline": node.get("timeline", "main"),
"labels": node_labels(node),
"meta": node.get("meta", {}),
}
def preference_context(state: dict[str, Any], node_id: str) -> str:
upstream = upstream_nodes(state, node_id)
lines = [f"Task: {state.get('task') or 'Untitled task'}"]
for node in upstream[-5:]:
lines.append(f"{node.get('kind', 'node')}: {node.get('title', '')} - {node.get('body', '')}")
return "\n".join(lines)
def comparison_node(state: dict[str, Any], selected_id: str) -> dict[str, Any] | None:
children = children_by_parent(state.get("nodes", []))
for node in reversed(upstream_nodes(state, selected_id)):
parent = node.get("parent")
siblings = [child for child in children.get(parent, []) if child.get("id") != node.get("id")]
if siblings:
different_timeline = [
child for child in siblings if child.get("timeline", "main") != node.get("timeline", "main")
]
return (different_timeline or siblings)[-1]
return None
def feedback_rows(state: dict[str, Any] | None) -> list[list[str]]:
rows = []
for item in (state or {}).get("feedback", []):
rows.append(
[
item.get("id", ""),
item.get("chosen", {}).get("title", ""),
item.get("rejected", {}).get("title", ""),
item.get("reason", ""),
item.get("created_at", ""),
]
)
return rows
def record_preference_pair(
state: dict[str, Any],
chosen: dict[str, Any],
rejected: dict[str, Any],
reason: str,
) -> str:
reason = reason.strip() or "Human preferred this branch after inspecting the trace."
mark_node(state, chosen["id"], "fork_winner")
mark_node(state, rejected["id"], "fork_loser")
pair = {
"id": f"pref-{uuid.uuid4().hex[:8]}",
"created_at": datetime.now(timezone.utc).isoformat(),
"source": "human_trace_preference",
"reason": reason,
"task": state.get("task", ""),
"context": preference_context(state, chosen["id"]),
"chosen": node_training_view(chosen),
"rejected": node_training_view(rejected),
"dpo": {
"prompt": preference_context(state, chosen["id"]),
"chosen": f"{chosen.get('title', '')}: {chosen.get('body', '')}",
"rejected": f"{rejected.get('title', '')}: {rejected.get('body', '')}",
},
}
state.setdefault("feedback", []).append(pair)
state["selected_id"] = chosen["id"]
return f"Recorded preference: {chosen['title']} over {rejected['title']}."
def add_node(
state: dict[str, Any],
parent: str,
kind: str,
title: str,
body: str,
status: str = "done",
timeline: str = "main",
meta: dict[str, Any] | None = None,
) -> str:
parent_node = node_by_id(state, parent)
depth = int(parent_node["depth"]) + 1 if parent_node else 0
node_id = f"{kind[:1]}-{uuid.uuid4().hex[:7]}"
node = TraceNode(
id=node_id,
parent=parent,
kind=kind,
title=title,
body=body,
status=status,
depth=depth,
timeline=timeline,
meta=meta or {},
)
state["nodes"].append(node.__dict__)
state["active_tip"] = node_id
state["selected_id"] = node_id
return node_id
def classify_task(task: str) -> dict[str, Any]:
lower = task.lower()
wants_math = bool(re.search(r"\b(sum|calculate|math|count|average|total|percent|number)\b", lower))
wants_research = bool(re.search(r"\b(compare|research|find|source|which|why|best|evidence)\b", lower))
wants_build = bool(re.search(r"\b(build|make|design|prototype|app|demo|ship)\b", lower))
wants_plan = bool(re.search(r"\b(plan|strategy|steps|roadmap|schedule)\b", lower))
return {
"math": wants_math,
"research": wants_research,
"build": wants_build,
"plan": wants_plan,
"risk": "high" if any(word in lower for word in ["medical", "legal", "finance", "safety"]) else "normal",
}
def tiny_reasoner(task: str, step: int, memory: list[str]) -> dict[str, str]:
traits = classify_task(task)
if step == 1:
return {
"thought": "I should make the task concrete before using tools. A small agent wins by narrowing scope early.",
"tool": "task_decomposer",
"input": task,
}
if step == 2 and traits["research"]:
return {
"thought": "This has a research flavor, so I will gather candidate evidence instead of hallucinating a confident answer.",
"tool": "mini_search",
"input": extract_keywords(task),
}
if step == 2 and traits["math"]:
return {
"thought": "There may be arithmetic hidden in the task. I will isolate computable numbers before I decide.",
"tool": "calculator",
"input": task,
}
if step == 3 and traits["build"]:
return {
"thought": "The useful move is to turn the idea into a buildable interface with one memorable interaction.",
"tool": "prototype_sketcher",
"input": task,
}
if step == 3:
return {
"thought": "I should test whether my current path is too generic and create a more opinionated alternative.",
"tool": "critique",
"input": " ".join(memory[-3:]),
}
if step == 4 and traits["plan"]:
return {
"thought": "The task asks for action, so I will convert the findings into a sequence with a visible finish line.",
"tool": "planner",
"input": task,
}
if step == 5:
return {
"thought": "I should compress the trace into a final answer while preserving uncertainty and next actions.",
"tool": "synthesizer",
"input": " ".join(memory[-5:]),
}
return {
"thought": "I have enough signal. One final consistency check should catch weak claims before the answer.",
"tool": "validator",
"input": " ".join(memory[-5:]),
}
def hf_reasoner(task: str, step: int, memory: list[str], model_id: str) -> dict[str, str]:
model_id = model_id.strip() or DEFAULT_MODEL_ID
system = (
"You are the planning core of Glass-Box Agent, a small transparent ReAct agent. "
"Return only compact JSON with keys thought, tool, and input. "
"Allowed tools: task_decomposer, mini_search, calculator, prototype_sketcher, critique, planner, synthesizer, validator."
)
user = {
"task": task,
"step": step,
"memory": memory[-5:],
"instruction": "Choose the next useful ReAct thought and tool call. Keep thought under 24 words.",
}
client = InferenceClient(token=os.getenv("HF_TOKEN") or None)
response = client.chat_completion(
model=model_id,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": json.dumps(user)},
],
max_tokens=180,
temperature=0.3,
)
content = response.choices[0].message.content
match = re.search(r"\{.*\}", content or "", flags=re.DOTALL)
payload = json.loads(match.group(0) if match else content)
thought = str(payload.get("thought", "")).strip()
tool = str(payload.get("tool", "validator")).strip()
tool_input = str(payload.get("input", task)).strip()
allowed = {
"task_decomposer",
"mini_search",
"calculator",
"prototype_sketcher",
"critique",
"planner",
"synthesizer",
"validator",
}
if tool not in allowed:
tool = "validator"
if not thought:
thought = "I should validate the current branch before trusting it."
if not tool_input:
tool_input = task
return {"thought": thought, "tool": tool, "input": tool_input}
def choose_reasoner(task: str, step: int, memory: list[str], backend: str, model_id: str) -> tuple[dict[str, str], str]:
if backend == BACKEND_HF:
try:
return hf_reasoner(task, step, memory, model_id), f"HF Inference model: {model_id.strip() or DEFAULT_MODEL_ID}"
except Exception as exc:
fallback = tiny_reasoner(task, step, memory)
fallback["thought"] = f"{fallback['thought']} (HF backend fell back: {type(exc).__name__})"
return fallback, f"HF backend requested but fell back to offline heuristic: {type(exc).__name__}"
if backend == BACKEND_MODAL:
try:
return modal_reasoner(task, step, memory, model_id), f"Modal served model: {model_id.strip() or DEFAULT_MODEL_ID}"
except Exception as exc:
fallback = tiny_reasoner(task, step, memory)
fallback["thought"] = f"{fallback['thought']} (Modal backend fell back: {type(exc).__name__})"
return fallback, f"Modal backend requested but fell back to offline heuristic: {type(exc).__name__}"
return tiny_reasoner(task, step, memory), "offline heuristic ReAct engine"
def modal_reasoner(task: str, step: int, memory: list[str], model_id: str) -> dict[str, str]:
payload = {
"task": task,
"step": step,
"memory": memory[-5:],
"model_id": model_id.strip() or DEFAULT_MODEL_ID,
}
response = call_modal_json(MODAL_MODEL_URL, MODAL_MODEL_FUNCTION, payload)
decision = response.get("decision", response)
thought = str(decision.get("thought", "")).strip()
tool = str(decision.get("tool", "validator")).strip()
tool_input = str(decision.get("input", task)).strip()
if not thought:
thought = "I should validate this branch before trusting it."
if not tool_input:
tool_input = task
return {"thought": thought, "tool": tool, "input": tool_input}
def call_modal_json(url: str, function_name: str, payload: dict[str, Any]) -> dict[str, Any]:
if url:
request = urllib.request.Request(
url,
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.loads(response.read().decode("utf-8"))
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as exc:
raise RuntimeError(f"Modal web endpoint failed: {type(exc).__name__}") from exc
try:
import modal
except ImportError as exc:
raise RuntimeError("Modal SDK is not installed and no Modal endpoint URL is configured.") from exc
try:
function = modal.Function.from_name(MODAL_APP_NAME, function_name)
result = function.remote(payload)
except Exception as exc:
raise RuntimeError(f"Modal function lookup failed: {type(exc).__name__}") from exc
return result if isinstance(result, dict) else {"result": result}
def trace_payload(state: dict[str, Any], artifact: str = "trace", event: str = "snapshot") -> dict[str, Any]:
return {
"app": APP_TITLE,
"artifact": artifact,
"event": event,
"trace_id": state.get("trace_id") or f"trace-{uuid.uuid4().hex[:10]}",
"exported_at": datetime.now(timezone.utc).isoformat(),
"model_note": state.get("model_note"),
"task": state.get("task"),
"nodes": state.get("nodes", []),
"feedback": state.get("feedback", []),
"transcript": state.get("transcript", []),
}
def rl_payload(state: dict[str, Any]) -> dict[str, Any]:
return {
"app": APP_TITLE,
"artifact": "rl_feedback_dataset",
"trace_id": state.get("trace_id") or f"trace-{uuid.uuid4().hex[:10]}",
"exported_at": datetime.now(timezone.utc).isoformat(),
"task": state.get("task", ""),
"model_note": state.get("model_note", ""),
"preference_pairs": state.get("feedback", []),
"supervised_labels": [
{
"node": node_training_view(node),
"labels": node_labels(node),
}
for node in state.get("nodes", [])
if node_labels(node)
],
"notes": [
"preference_pairs are shaped for DPO/RLHF: prompt, chosen, rejected.",
"supervised_labels can seed easy finetuning or reward-model heuristics.",
],
}
def persist_trace_to_modal(state: dict[str, Any], event: str) -> dict[str, Any]:
if not MODAL_TRACE_ENABLED:
return {"enabled": False}
payload = trace_payload(state, event=event)
result = call_modal_json(MODAL_TRACE_URL, MODAL_TRACE_FUNCTION, payload)
state["modal_store"] = {
"last_event": event,
"stored_at": datetime.now(timezone.utc).isoformat(),
"result": result,
}
return result
def persist_trace_safely(state: dict[str, Any], event: str) -> None:
try:
persist_trace_to_modal(state, event)
except Exception as exc:
state["modal_store"] = {
"last_event": event,
"error": type(exc).__name__,
"stored_at": datetime.now(timezone.utc).isoformat(),
}
def extract_keywords(text: str) -> str:
words = re.findall(r"[A-Za-z][A-Za-z0-9+-]{2,}", text.lower())
stop = {"the", "and", "for", "with", "that", "this", "from", "into", "about", "while", "should"}
return ", ".join(dict.fromkeys(word for word in words if word not in stop))[:180] or "small model agent"
def run_tool(tool: str, tool_input: str, task: str) -> str:
if tool == "task_decomposer":
return (
"Subgoals: identify the user-facing promise; choose the smallest useful tool loop; "
"surface uncertainty; produce a trace humans can inspect."
)
if tool == "mini_search":
return (
f"Mock search notes for '{tool_input}': use primary sources, cite constraints, and separate observed facts "
"from model inference. In the hackathon, this trace can be published as the Open Trace artifact."
)
if tool == "calculator":
nums = [float(match) for match in re.findall(r"-?\d+(?:\.\d+)?", tool_input)]
if not nums:
return "No explicit numbers found. I should avoid pretending there was arithmetic."
return f"Numbers found: {nums}. Sum={sum(nums):g}; mean={sum(nums) / len(nums):g}; count={len(nums)}."
if tool == "prototype_sketcher":
return (
"Interface sketch: left panel for task controls, center tree for trace, right inspector for node details, "
"bottom drawer for exported JSON and replay."
)
if tool == "critique":
return (
"Critique: this could become another opaque chatbot unless the fork action is immediate, visual, and useful."
)
if tool == "planner":
return (
"Plan: run a short trace, inspect a weak node, fork it with a different assumption, compare final branches."
)
if tool == "synthesizer":
return (
"Synthesis: answer from the strongest branch, show discarded branches, and let the user fork any questionable step."
)
if tool == "validator":
return "Validation: every final claim should point back to a trace node, tool observation, or explicit assumption."
return "Tool unavailable."
def make_branch_hint(step: int, thought: str) -> str:
if step == 2:
return "Alternate timeline: skip research and prototype immediately."
if "generic" in thought:
return "Alternate timeline: make it stranger and more demo-first."
return "Alternate timeline: question the previous assumption before moving on."
def seed_start_phase(state: dict[str, Any], start_phase: str) -> tuple[str, list[str], int]:
if start_phase == START_TASK:
return "root", [], 1
state["transcript"].append(f"Start from: {start_phase}")
memory: list[str] = []
parent = "root"
step = 1
if start_phase in {START_RESEARCH, START_PROTOTYPE, START_CRITIQUE, START_VALIDATE, START_SYNTHESIS}:
thought = add_node(
state,
parent,
"thought",
"Seed: frame task",
"Assume the task has already been scoped; begin from the selected phase.",
status="done",
meta={"seed": True, "start_phase": start_phase},
)
tool = add_node(
state,
thought,
"tool",
"task_decomposer",
state["task"],
status="done",
meta={"seed": True},
)
parent = add_node(
state,
tool,
"observation",
"Seed observation",
"Working context: user wants a transparent, forkable small-agent trace.",
status="done",
meta={"seed": True},
)
memory.append("Working context: user wants a transparent, forkable small-agent trace.")
step = 2
if start_phase in {START_PROTOTYPE, START_CRITIQUE, START_VALIDATE, START_SYNTHESIS}:
thought = add_node(
state,
parent,
"thought",
"Seed: evidence branch",
"Assume the relevant constraints are known and move toward interface decisions.",
status="done",
meta={"seed": True, "start_phase": start_phase},
)
tool = add_node(
state,
thought,
"tool",
"mini_search",
extract_keywords(state["task"]),
status="done",
meta={"seed": True},
)
parent = add_node(
state,
tool,
"observation",
"Evidence snapshot",
"Constraint snapshot: <=32B model, visible trace, forkable branches, exportable artifact.",
status="done",
meta={"seed": True},
)
memory.append("Constraint snapshot: <=32B model, visible trace, forkable branches, exportable artifact.")
step = 3
if start_phase in {START_CRITIQUE, START_VALIDATE, START_SYNTHESIS}:
thought = add_node(
state,
parent,
"thought",
"Seed: prototype branch",
"Assume the first interface draft exists and inspect where it can fail.",
status="done",
meta={"seed": True, "start_phase": start_phase},
)
tool = add_node(
state,
thought,
"tool",
"prototype_sketcher",
state["task"],
status="done",
meta={"seed": True},
)
parent = add_node(
state,
tool,
"observation",
"Prototype snapshot",
"Workspace layout: command rail, trace tree, node inspector, exportable artifact.",
status="done",
meta={"seed": True},
)
memory.append("Workspace layout: command rail, trace tree, node inspector, exportable artifact.")
step = 4
if start_phase in {START_CRITIQUE, START_VALIDATE, START_SYNTHESIS}:
checkpoint = add_node(
state,
parent,
"checkpoint",
"Seed checkpoint",
"Repeat from here: this prototype point can be retried, labeled, or replayed downstream.",
status="ready",
meta={"seed": True, "start_phase": start_phase},
)
add_node(
state,
checkpoint,
"backtrack",
"Ready to replay",
"Use this checkpoint to retry the branch or replay downstream decisions.",
status="done",
meta={"seed": True, "returns_to": parent},
)
memory.append("Checkpoint created: prototype branch can be retried or replayed.")
step = 5
if start_phase == START_SYNTHESIS:
thought = add_node(
state,
parent,
"thought",
"Seed: validation passed",
"Assume major risks are checked and move directly to a final synthesis branch.",
status="done",
meta={"seed": True, "start_phase": start_phase},
)
tool = add_node(
state,
thought,
"tool",
"validator",
"seeded validation context",
status="done",
meta={"seed": True},
)
parent = add_node(
state,
tool,
"observation",
"Validation snapshot",
"Every claim should connect to a visible trace node, tool output, or explicit assumption.",
status="done",
meta={"seed": True},
)
memory.append("Every claim should connect to a visible trace node, tool output, or explicit assumption.")
step = 5
state["active_tip"] = parent
state["selected_id"] = parent
return parent, memory, step
def render_tree(state: dict[str, Any]) -> str:
visual_canvas = render_visual_fork_canvas(state)
nodes = state["nodes"]
selected_id = state.get("selected_id", "root")
children = children_by_parent(nodes)
def render_node(node: dict[str, Any]) -> str:
status = html.escape(node["status"])
kind = html.escape(node["kind"])
selected = " selected" if node["id"] == selected_id else ""
marked = " marked" if node["id"] in state.get("marked_ids", []) else ""
title = html.escape(node["title"])
body = html.escape(node["body"])
timeline = html.escape(node.get("timeline", "main"))
branch = " branch" if timeline != "main" else ""
labels = node_labels(node)
label_html = "".join(f'<span class="node-label">{html.escape(label)}</span>' for label in labels)
child_html = "".join(render_node(child) for child in children.get(node["id"], []))
return f"""
<li>
<button class="trace-node {kind} {status}{selected}{branch}{marked}" data-node-id="{html.escape(node['id'])}">
<span class="node-top">
<span class="kind">{kind}</span>
<span class="timeline">{timeline}</span>
</span>
<strong>{title}</strong>
{f'<span class="node-labels">{label_html}</span>' if labels else ''}
<span class="body">{body}</span>
</button>
{f'<ul>{child_html}</ul>' if child_html else ''}
</li>
"""
return f"""
<div class="tree-shell">
<div class="tree-header">
<div>
<h2>{APP_TITLE}</h2>
<p>{APP_TAGLINE}</p>
</div>
<div class="badge">Open trace ready</div>
</div>
{visual_canvas}
<div class="tree-divider">
<span>Server trace mirror</span>
</div>
<ol class="trace-tree">{render_node(nodes[0])}</ol>
</div>
"""
def render_visual_fork_canvas(state: dict[str, Any]) -> str:
node_count = len(state.get("nodes", []))
selected = html.escape(state.get("selected_id", "root"))
modal_trace_url = html.escape(MODAL_TRACE_URL)
trace_id = html.escape(state.get("trace_id", "visual-trace"))
return f"""
<div class="visual-fork-controls" data-selected-node="{selected}" data-node-count="{node_count}" data-trace-id="{trace_id}" data-modal-trace-url="{modal_trace_url}">
<div>
<h3>Trace canvas</h3>
<p>Click a node to select it on the server. Right-click for server-backed retry, replay, and labels. Drag sibling nodes only rearranges the visual view.</p>
</div>
<input class="visual-fork-prompt" value="Try the opposite assumption from this node." aria-label="Visual fork prompt">
<button class="visual-fork-button" type="button">Retry selected branch</button>
<button class="visual-export-button" type="button">Download visual trace</button>
</div>
<div class="visual-action-bar" aria-label="Selected node actions">
<button class="visual-server-button primary" type="button" data-action="retry">Retry</button>
<button class="visual-server-button" type="button" data-action="replay">Replay</button>
<button class="visual-server-button" type="button" data-action="mark_weak">Weak</button>
<button class="visual-server-button" type="button" data-action="mark_useful">Useful</button>
<button class="visual-server-button positive" type="button" data-action="prefer">Prefer</button>
<button class="visual-server-button" type="button" data-action="reject">Reject</button>
</div>
<div class="visual-fork-inspector">
<strong>Selected visual node</strong>
<span class="visual-selected-title">Click a node to select it.</span>
</div>
"""
def render_inspector(state: dict[str, Any]) -> str:
selected = node_by_id(state, state.get("selected_id", "root")) or state["nodes"][0]
meta = json.dumps(selected.get("meta", {}), indent=2)
labels = node_labels(selected)
label_html = "".join(f'<span class="node-label">{html.escape(label)}</span>' for label in labels)
descendants = descendant_nodes(state, selected["id"])
return f"""
<div class="inspector">
<div class="inspector-kicker">{html.escape(selected['kind'])} / {html.escape(selected['status'])}</div>
<h3>{html.escape(selected['title'])}</h3>
{f'<div class="node-labels inspector-labels">{label_html}</div>' if labels else ''}
<p>{html.escape(selected['body'])}</p>
<dl>
<dt>Node</dt><dd>{html.escape(selected['id'])}</dd>
<dt>Parent</dt><dd>{html.escape(str(selected['parent']))}</dd>
<dt>Timeline</dt><dd>{html.escape(selected.get('timeline', 'main'))}</dd>
<dt>Downstream</dt><dd>{len(descendants)} node(s)</dd>
</dl>
<details>
<summary>Metadata</summary>
<pre>{html.escape(meta)}</pre>
</details>
</div>
"""
def render_transcript(state: dict[str, Any]) -> str:
lines = state.get("transcript", [])
if not lines:
return "No run yet."
return "\n".join(lines[-30:])
def trace_rows(state: dict[str, Any]) -> list[list[str]]:
rows = []
for node in state.get("nodes", []):
indent = " " * int(node.get("depth", 0))
rows.append(
[
node["id"],
f"{indent}{node['title']}",
node["kind"],
node.get("timeline", "main"),
node["status"],
", ".join(node_labels(node)),
str(node.get("parent")),
]
)
return rows
def outputs_for(state: dict[str, Any], event: str = "snapshot"):
persist_trace_safely(state, event)
return render_tree(state), trace_rows(state), render_inspector(state), render_transcript(state), state
def outputs_for_training(state: dict[str, Any], event: str = "snapshot"):
return (*outputs_for(state, event), feedback_rows(state))
def run_agent(task: str, steps: int, start_phase: str, backend: str, model_id: str, state: dict[str, Any] | None):
if backend == BACKEND_HF:
model_label = f"HF Inference model: {model_id.strip() or DEFAULT_MODEL_ID}"
elif backend == BACKEND_MODAL:
model_label = f"Modal served model: {model_id.strip() or DEFAULT_MODEL_ID}"
else:
model_label = "offline heuristic ReAct engine"
state = reset_state(task, model_label)
parent, memory, start_step = seed_start_phase(state, start_phase)
yield outputs_for_training(state)
stop_step = min(int(steps), MAX_STEPS)
if start_phase == START_SYNTHESIS:
stop_step = max(stop_step, 5)
for step in range(start_step, stop_step + 1):
decision, model_note = choose_reasoner(state["task"], step, memory, backend, model_id)
state["model_note"] = model_note
thought_id = add_node(
state,
parent,
"thought",
f"Thought {step}",
decision["thought"],
status="running",
meta={"step": step, "backend": backend, "model_id": model_id.strip() or None},
)
state["transcript"].append(f"Thought {step}: {decision['thought']}")
yield outputs_for_training(state)
time.sleep(0.25)
tool_id = add_node(
state,
thought_id,
"tool",
decision["tool"],
decision["input"],
status="running",
meta={"step": step, "tool": decision["tool"], "backend": backend},
)
state["transcript"].append(f"Tool call: {decision['tool']}({decision['input']})")
yield outputs_for_training(state)
time.sleep(0.25)
observation = run_tool(decision["tool"], decision["input"], state["task"])
obs_id = add_node(
state,
tool_id,
"observation",
f"Observation {step}",
observation,
status="done",
meta={"step": step},
)
node_by_id(state, thought_id)["status"] = "done"
node_by_id(state, tool_id)["status"] = "done"
memory.append(observation)
state["transcript"].append(f"Observation {step}: {observation}")
parent = obs_id
yield outputs_for_training(state)
if step == 3:
checkpoint_id = add_node(
state,
obs_id,
"checkpoint",
"Repeat from here",
"This is a replay checkpoint. Label this node, retry the branch, or re-execute downstream.",
status="ready",
meta={"step": step, "checkpoint": obs_id, "operation": "repeat_from_here"},
)
add_node(
state,
checkpoint_id,
"backtrack",
"Replay downstream",
"Use the checkpoint above to rerun the following nodes with a different assumption.",
status="done",
meta={"step": step, "returns_to": obs_id},
)
state["active_tip"] = parent
state["selected_id"] = parent
state["transcript"].append("Checkpoint: repeat from here, retry the branch, or replay downstream.")
yield outputs_for_training(state)
if step in {2, 3}:
branch_id = add_node(
state,
obs_id,
"fork",
f"Possible fork after step {step}",
make_branch_hint(step, decision["thought"]),
status="available",
timeline=f"branch-{state['timeline_count']}",
meta={"fork_from": obs_id, "suggested_by": "agent"},
)
state["timeline_count"] += 1
state["active_tip"] = parent
state["selected_id"] = branch_id
state["transcript"].append(f"Fork suggested from {obs_id}: {make_branch_hint(step, decision['thought'])}")
yield outputs_for_training(state)
add_node(
state,
parent,
"answer",
"Final answer",
"The best branch is the one whose claims are backed by visible observations. Click any node to inspect it, then fork the trace to test a different assumption.",
status="done",
meta={"trace_nodes": len(state["nodes"]), "start_phase": start_phase},
)
node_by_id(state, "root")["status"] = "done"
state["transcript"].append("Final: trace complete. Export JSON or fork a node to continue.")
yield outputs_for_training(state)
def build_dataset_trace(example: dict[str, Any], backend: str, model_id: str) -> dict[str, Any]:
model_label = "offline heuristic ReAct engine"
if backend == BACKEND_HF:
model_label = f"HF Inference model: {model_id.strip() or DEFAULT_MODEL_ID}"
elif backend == BACKEND_MODAL:
model_label = f"Modal served model: {model_id.strip() or DEFAULT_MODEL_ID}"
state = reset_state(example["task"], model_label)
parent, memory, start_step = seed_start_phase(state, example["start_phase"])
stop_step = min(int(example["steps"]), MAX_STEPS)
first_branch_id = None
for step in range(start_step, stop_step + 1):
decision, model_note = choose_reasoner(state["task"], step, memory, backend, model_id)
state["model_note"] = model_note
thought_id = add_node(
state,
parent,
"thought",
f"Dataset thought {step}",
decision["thought"],
status="done",
meta={"step": step, "dataset": example["name"]},
)
tool_id = add_node(
state,
thought_id,
"tool",
decision["tool"],
decision["input"],
status="done",
meta={"step": step, "dataset": example["name"]},
)
observation = run_tool(decision["tool"], decision["input"], state["task"])
parent = add_node(
state,
tool_id,
"observation",
f"Dataset observation {step}",
observation,
status="done",
meta={"step": step, "dataset": example["name"]},
)
memory.append(observation)
if step in {2, 3} and not first_branch_id:
first_branch_id = add_node(
state,
parent,
"fork",
"Dataset alternate branch",
make_branch_hint(step, decision["thought"]),
status="available",
timeline=f"branch-{state['timeline_count']}",
meta={"dataset": example["name"], "fork_from": parent},
)
state["timeline_count"] += 1
if not first_branch_id:
first_branch_id = add_node(
state,
parent,
"fork",
"Dataset baseline branch",
"Baseline continuation: proceed without challenging the current assumption.",
status="available",
timeline=f"branch-{state['timeline_count']}",
meta={"dataset": example["name"], "fallback_comparison": True, "fork_from": parent},
)
state["timeline_count"] += 1
answer_id = add_node(
state,
parent,
"answer",
"Dataset answer",
"Prefer the branch whose claims are easiest to inspect, label, and retry.",
status="done",
meta={"dataset": example["name"]},
)
node_by_id(state, "root")["status"] = "done"
if first_branch_id:
state["selected_id"] = answer_id
rejected = node_by_id(state, first_branch_id)
chosen = node_by_id(state, answer_id)
if chosen and rejected:
record_preference_pair(state, chosen, rejected, example["feedback_note"])
persist_trace_safely(state, "dataset_eval")
return state
def run_dataset_eval(backend: str, model_id: str):
results = []
traces = []
for example in DATASET_EXAMPLES:
state = build_dataset_trace(example, backend, model_id)
pairs = state.get("feedback", [])
traces.append(trace_payload(state, artifact="dataset_trace", event="dataset_eval"))
results.append(
{
"name": example["name"],
"task": example["task"],
"trace_id": state.get("trace_id", ""),
"nodes": len(state.get("nodes", [])),
"preference_pairs": len(pairs),
"selected_label": "fork_winner" if pairs else "none",
}
)
payload = {
"app": APP_TITLE,
"artifact": "mini_dataset_eval",
"exported_at": datetime.now(timezone.utc).isoformat(),
"backend": backend,
"model_id": model_id.strip() or DEFAULT_MODEL_ID,
"results": results,
"traces": traces,
}
path = os.path.join(tempfile.gettempdir(), f"glass-box-mini-dataset-{uuid.uuid4().hex[:8]}.json")
with open(path, "w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2)
summary = f"Ran {len(results)} dataset examples and collected {sum(item['preference_pairs'] for item in results)} preference pair(s)."
return dataset_result_rows(results), path, summary
def inspect_node(selected_id: str, state: dict[str, Any] | None):
if not state:
state = new_state()
if selected_id and node_by_id(state, selected_id):
state["selected_id"] = selected_id
return render_tree(state), trace_rows(state), render_inspector(state), state
def select_trace_row(state: dict[str, Any] | None, evt: gr.SelectData):
if not state:
state = new_state()
index = evt.index
row_index = index[0] if isinstance(index, (list, tuple)) else index
try:
selected_id = trace_rows(state)[int(row_index)][0]
except (TypeError, ValueError, IndexError):
selected_id = state.get("selected_id", "root")
state["selected_id"] = selected_id
selected = node_by_id(state, selected_id)
if selected:
state["action_log"] = f"Selected {selected['title']} ({selected['kind']}). Downstream nodes: {len(descendant_nodes(state, selected_id))}."
return render_tree(state), trace_rows(state), render_inspector(state), state.get("action_log", ""), state
def visual_server_action(
action: str,
node_id: str,
prompt: str,
backend: str,
model_id: str,
state: dict[str, Any] | None,
):
if not state:
state = new_state()
action = (action or "select").strip()
node_id = (node_id or state.get("selected_id", "root")).strip()
if node_by_id(state, node_id):
state["selected_id"] = node_id
selected = node_by_id(state, state.get("selected_id", "root")) or state["nodes"][0]
if action == "retry":
return (*retry_selected_branch(prompt, backend, model_id, state), feedback_rows(state))
if action == "replay":
return (*replay_downstream(prompt, state), feedback_rows(state))
if action == "prefer":
return prefer_selected(prompt or "This branch is more grounded and useful.", state)
if action == "reject":
return reject_selected(prompt or "This branch is less useful than the comparison branch.", state)
if action == "mark_useful":
state["action_log"] = mark_node(state, selected["id"], "useful")
state["transcript"].append(state["action_log"])
return (*operation_outputs(state, event="visual_mark_useful"), feedback_rows(state))
if action == "mark_weak":
state["action_log"] = mark_node(state, selected["id"], "weak")
state["transcript"].append(state["action_log"])
return (*operation_outputs(state, event="visual_mark_weak"), feedback_rows(state))
if action == "annotate":
note = (prompt or "Retry from here.").strip()
selected.setdefault("meta", {})["annotation"] = note
state["action_log"] = f"Annotated {selected['title']}: {note}"
state["transcript"].append(state["action_log"])
return (*operation_outputs(state, event="visual_annotate"), feedback_rows(state))
state["action_log"] = f"Selected {selected['title']} ({selected['kind']}). Downstream nodes: {len(descendant_nodes(state, selected['id']))}."
state["transcript"].append(state["action_log"])
return (*operation_outputs(state, event="visual_select"), feedback_rows(state))
def operation_outputs(state: dict[str, Any], trace_file: str | None = None, event: str = "operation"):
persist_trace_safely(state, event)
return render_tree(state), trace_rows(state), render_inspector(state), render_transcript(state), state.get("action_log", ""), state, trace_file
def feedback_outputs(state: dict[str, Any], trace_file: str | None = None, event: str = "feedback"):
return (*operation_outputs(state, trace_file, event), feedback_rows(state))
def mark_selected(label: str, state: dict[str, Any] | None):
if not state:
state = new_state()
selected_id = state.get("selected_id", "root")
state["action_log"] = mark_node(state, selected_id, label)
state["transcript"].append(state["action_log"])
return operation_outputs(state)
def clear_marks(state: dict[str, Any] | None):
if not state:
state = new_state()
for node in state.get("nodes", []):
node.get("meta", {}).pop("labels", None)
state["marked_ids"] = []
state["action_log"] = "Cleared all node marks."
state["transcript"].append(state["action_log"])
return operation_outputs(state)
def retry_selected_branch(instruction: str, backend: str, model_id: str, state: dict[str, Any] | None):
if not state:
state = new_state()
selected_id = state.get("selected_id", "root")
selected = node_by_id(state, selected_id) or state["nodes"][0]
instruction = instruction.strip() or "Retry this branch with a clearer assumption."
memory = [node["body"] for node in upstream_nodes(state, selected["id"]) if node["kind"] in {"observation", "thought"}]
decision, model_note = choose_reasoner(
f"{state.get('task', '')}\nRetry instruction: {instruction}\nSelected node: {selected['title']} - {selected['body']}",
min(int(selected.get("meta", {}).get("step", 3)) + 1, MAX_STEPS),
memory,
backend,
model_id,
)
state["model_note"] = model_note
timeline = f"retry-{state['timeline_count']}"
state["timeline_count"] += 1
retry_id = add_node(
state,
selected["id"],
"thought",
"Retry branch",
decision["thought"],
status="done",
timeline=timeline,
meta={"operation": "retry_branch", "instruction": instruction, "retried_from": selected["id"]},
)
tool_id = add_node(
state,
retry_id,
"tool",
decision["tool"],
decision["input"],
status="done",
timeline=timeline,
meta={"operation": "retry_branch", "tool": decision["tool"]},
)
observation = run_tool(decision["tool"], decision["input"], state.get("task", ""))
obs_id = add_node(
state,
tool_id,
"observation",
"Retry observation",
observation,
status="done",
timeline=timeline,
meta={"operation": "retry_branch", "retried_from": selected["id"]},
)
state["selected_id"] = obs_id
state["active_tip"] = obs_id
state["action_log"] = f"Retried branch from {selected['title']} into {timeline}."
state["transcript"].append(state["action_log"])
return operation_outputs(state, export_trace_file(state))
def replay_downstream(instruction: str, state: dict[str, Any] | None):
if not state:
state = new_state()
selected_id = state.get("selected_id", "root")
selected = node_by_id(state, selected_id) or state["nodes"][0]
descendants = descendant_nodes(state, selected["id"])
if not descendants:
for marked_id in reversed(state.get("marked_ids", [])):
marked = node_by_id(state, marked_id)
marked_descendants = descendant_nodes(state, marked_id) if marked else []
if marked and marked_descendants:
selected = marked
descendants = marked_descendants
selected_id = marked_id
state["selected_id"] = marked_id
break
if not descendants:
state["action_log"] = f"{selected['title']} has no downstream nodes to re-execute."
state["transcript"].append(state["action_log"])
return operation_outputs(state)
instruction = instruction.strip() or "Replay downstream with the selected node as the checkpoint."
timeline = f"replay-{state['timeline_count']}"
state["timeline_count"] += 1
replay_root = add_node(
state,
selected["id"],
"thought",
"Re-execute downstream",
instruction,
status="done",
timeline=timeline,
meta={"operation": "replay_downstream", "from": selected["id"], "source_descendants": len(descendants)},
)
parent = replay_root
replayed = 0
for original in descendants[:10]:
if original["kind"] in {"fork", "answer"}:
continue
body = original["body"]
if original["kind"] == "tool":
body = original["body"]
elif original["kind"] == "observation":
body = f"Replayed observation: {original['body']}"
else:
body = f"Replayed from {original['title']}: {original['body']}"
parent = add_node(
state,
parent,
original["kind"],
f"Replay: {original['title']}",
body,
status="done",
timeline=timeline,
meta={"operation": "replay_downstream", "source_node": original["id"]},
)
replayed += 1
state["selected_id"] = parent
state["active_tip"] = parent
state["action_log"] = f"Re-executed {replayed} downstream node(s) from {selected['title']} into {timeline}."
state["transcript"].append(state["action_log"])
return operation_outputs(state, export_trace_file(state))
def summarize_marked(state: dict[str, Any] | None):
if not state:
state = new_state()
state["action_log"] = mark_summary(state)
return operation_outputs(state)
def prefer_selected(reason: str, state: dict[str, Any] | None):
if not state:
state = new_state()
selected_id = state.get("selected_id", "root")
selected = node_by_id(state, selected_id) or state["nodes"][0]
rejected = comparison_node(state, selected["id"])
if not rejected:
state["action_log"] = "No sibling branch found to compare against. Fork or retry a node first, then record a preference."
state["transcript"].append(state["action_log"])
return feedback_outputs(state)
state["action_log"] = record_preference_pair(state, selected, rejected, reason)
state["transcript"].append(state["action_log"])
return feedback_outputs(state, export_rl_file(state))
def reject_selected(reason: str, state: dict[str, Any] | None):
if not state:
state = new_state()
selected_id = state.get("selected_id", "root")
rejected = node_by_id(state, selected_id) or state["nodes"][0]
chosen = comparison_node(state, rejected["id"])
if not chosen:
state["action_log"] = "No sibling branch found to promote over the selected node. Fork or retry a node first."
state["transcript"].append(state["action_log"])
return feedback_outputs(state)
state["action_log"] = record_preference_pair(state, chosen, rejected, reason)
state["transcript"].append(state["action_log"])
return feedback_outputs(state, export_rl_file(state))
def fork_selected(fork_prompt: str, state: dict[str, Any] | None):
if not state:
state = new_state()
selected_id = state.get("selected_id", "root")
selected = node_by_id(state, selected_id) or state["nodes"][0]
fork_prompt = fork_prompt.strip() or "Try the opposite assumption and continue from here."
timeline = f"branch-{state['timeline_count']}"
state["timeline_count"] += 1
thought_id = add_node(
state,
selected["id"],
"thought",
"Human fork",
fork_prompt,
status="done",
timeline=timeline,
meta={"forked_from": selected["id"], "created_by": "human"},
)
tool_id = add_node(
state,
thought_id,
"tool",
"counterfactual_runner",
fork_prompt,
status="done",
timeline=timeline,
meta={"tool": "counterfactual_runner"},
)
obs = (
"Counterfactual result: this branch changes the working assumption. Compare it with sibling nodes before trusting the final answer."
)
add_node(
state,
tool_id,
"observation",
"Fork observation",
obs,
status="done",
timeline=timeline,
meta={"forked_from": selected["id"]},
)
state["transcript"].append(f"Human fork from {selected['id']}: {fork_prompt}")
return (*outputs_for(state), export_trace_file(state))
def export_trace_file(state: dict[str, Any] | None):
state = state or new_state()
payload = trace_payload(state, event="export_trace")
path = os.path.join(tempfile.gettempdir(), f"glass-box-trace-{uuid.uuid4().hex[:8]}.json")
with open(path, "w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2)
persist_trace_safely(state, "export_trace")
return path
def export_only(state: dict[str, Any] | None):
return export_trace_file(state)
def export_rl_file(state: dict[str, Any] | None):
state = state or new_state()
payload = rl_payload(state)
path = os.path.join(tempfile.gettempdir(), f"glass-box-rl-feedback-{uuid.uuid4().hex[:8]}.json")
with open(path, "w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2)
persist_trace_safely(state, "export_rl_data")
return path
def export_rl_dataset(state: dict[str, Any] | None):
state = state or new_state()
state["action_log"] = f"Exported {len(state.get('feedback', []))} preference pair(s) for RL finetuning."
return state["action_log"], state, export_rl_file(state), feedback_rows(state)
def start_modal_tuning(base_model: str, state: dict[str, Any] | None):
state = state or new_state()
payload = {
"base_model": base_model.strip() or DEFAULT_MODEL_ID,
"trace_id": state.get("trace_id"),
"dataset": rl_payload(state),
}
try:
result = call_modal_json(MODAL_TUNE_URL, MODAL_TUNE_FUNCTION, payload)
checkpoint = result.get("checkpoint") or result.get("checkpoint_id") or result.get("result") or "Modal tuning job started"
state.setdefault("modal_store", {})["tuning"] = result
state["action_log"] = f"Started Modal RL tuning. Checkpoint: {checkpoint}"
state["transcript"].append(state["action_log"])
except Exception as exc:
state["action_log"] = f"Modal tuning not started: {type(exc).__name__}. Export RL data and deploy modal_service.py first."
state["transcript"].append(state["action_log"])
return state["action_log"], state, feedback_rows(state)
CSS = """
:root {
--ink: #1f2937;
--muted: #6b7280;
--paper: #f7f8fb;
--panel: #ffffff;
--panel-2: #f2f4f8;
--line: #d9dee7;
--line-strong: #c3cad6;
--accent: #0f766e;
--accent-2: #c2410c;
--blue: #315fbd;
--gold: #a16207;
--danger: #b4232f;
--violet: #6d4aff;
}
html,
body {
height: auto !important;
min-height: 100% !important;
overflow-y: auto !important;
}
.gradio-container {
background: var(--paper);
color: var(--ink);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
min-height: 100vh !important;
overflow: visible !important;
}
.gradio-container .main {
background: linear-gradient(180deg, #fbfcff 0%, var(--paper) 45%, #eef2f7 100%);
overflow: visible !important;
}
.gradio-container label,
.gradio-container .block-info {
color: var(--muted) !important;
font-size: 12px !important;
}
.gradio-container textarea,
.gradio-container input,
.gradio-container select {
background: #ffffff !important;
border-color: var(--line) !important;
color: var(--ink) !important;
}
.gradio-container .block {
background: transparent !important;
}
.gradio-container .form,
.gradio-container .panel,
.gradio-container .wrap {
border-color: var(--line) !important;
}
.gradio-container .prose h1 {
color: var(--ink);
font-size: 24px;
letter-spacing: 0;
margin-bottom: 2px;
}
.gradio-container .prose p {
color: var(--muted);
}
.command-rail {
background: linear-gradient(180deg, #ffffff, #f6f8fb);
border: 1px solid var(--line);
border-radius: 8px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.05);
padding: 14px !important;
position: relative;
}
.command-rail .prose h3 {
color: #334155;
font-size: 13px;
letter-spacing: 0;
margin: 8px 0 6px;
text-transform: uppercase;
}
.command-rail .prose:first-child h3 {
margin-top: 0;
}
.command-rail .block {
border-radius: 8px !important;
}
.command-rail .form,
.command-rail .panel,
.command-rail .wrap {
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
}
.command-rail [data-testid="block-info"] {
background: transparent !important;
color: #64748b !important;
display: block !important;
font-size: 11px !important;
font-weight: 700 !important;
letter-spacing: 0 !important;
margin-bottom: 5px !important;
padding: 0 !important;
text-transform: uppercase !important;
}
.command-rail textarea,
.command-rail input,
.command-rail select {
border-radius: 8px !important;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04) !important;
}
.command-rail textarea:focus,
.command-rail input:focus,
.command-rail select:focus {
border-color: #7fc6ba !important;
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.12) !important;
}
.task-box textarea {
min-height: 104px !important;
}
.depth-slider button[aria-label="Reset to default value"] {
display: none !important;
}
.depth-slider input[type="number"] {
max-width: 72px !important;
}
.run-row,
.action-row,
.label-row,
.compact-actions {
align-items: end;
gap: 10px !important;
}
.run-row > *,
.action-row > *,
.compact-actions > * {
min-width: 0 !important;
}
.label-row > :first-child {
flex: 1 1 auto !important;
}
.label-row > :last-child {
flex: 0 0 92px !important;
}
.advanced-setup {
background: #f8fafc !important;
border: 1px solid var(--line) !important;
border-radius: 8px !important;
margin: 8px 0 14px !important;
overflow: hidden;
}
.training-signal {
background: #fffdf7 !important;
border: 1px solid #ead7a4 !important;
border-radius: 8px !important;
margin: 10px 0 12px !important;
overflow: hidden;
}
.dataset-trial {
background: #f7fbff !important;
border: 1px solid #c8d7ff !important;
border-radius: 8px !important;
margin: 10px 0 14px !important;
overflow: hidden;
}
.dataset-trial summary {
color: #315fbd !important;
font-weight: 700 !important;
}
.dataset-table {
max-height: 190px;
overflow: auto;
}
.training-signal summary {
color: #6f4e00 !important;
font-weight: 700 !important;
}
.feedback-table {
max-height: 180px;
overflow: auto;
}
.advanced-setup summary {
color: #334155 !important;
font-weight: 700 !important;
}
.command-rail .radio,
.command-rail .checkbox-group {
gap: 8px !important;
}
.command-rail button {
min-height: 38px !important;
}
.action-log textarea {
background: #f8fafc !important;
color: #475569 !important;
font-size: 12px !important;
}
.trace-artifact {
opacity: 0.86;
}
#run-btn, #fork-btn, #export-btn, #retry-btn, #replay-btn, #mark-btn, #summary-btn, #clear-btn, #prefer-btn, #reject-btn, #export-rl-btn, #tune-btn, #load-dataset-btn, #run-dataset-btn {
border-radius: 6px;
}
#run-btn, #retry-btn, #prefer-btn, #run-dataset-btn {
background: linear-gradient(180deg, #18a999, #0f766e) !important;
border: 1px solid #0f766e !important;
color: white !important;
font-weight: 700 !important;
}
#run-btn {
min-height: 46px !important;
width: 100% !important;
}
#fork-btn, #export-btn, #replay-btn, #mark-btn, #summary-btn, #clear-btn, #reject-btn, #export-rl-btn, #tune-btn, #load-dataset-btn {
background: #ffffff !important;
border: 1px solid var(--line-strong) !important;
color: var(--ink) !important;
}
#mark-btn {
background: #eefaf7 !important;
border-color: #b9e2d9 !important;
color: #0f5f57 !important;
font-weight: 700 !important;
}
.tree-shell {
background: linear-gradient(180deg, #ffffff, #f8fafc);
border: 1px solid var(--line);
border-radius: 8px;
min-height: 620px;
padding: 18px;
overflow: auto;
}
.tree-header {
align-items: start;
border-bottom: 1px solid var(--line);
display: flex;
justify-content: space-between;
gap: 16px;
margin-bottom: 18px;
padding-bottom: 14px;
}
.tree-header h2 {
font-size: 24px;
margin: 0 0 4px;
color: var(--ink);
}
.tree-header p {
color: var(--muted);
margin: 0;
}
.visual-fork-controls {
align-items: center;
background: #fbfcfe;
border: 1px solid var(--line);
border-radius: 8px;
display: grid;
gap: 10px;
grid-template-columns: minmax(220px, 1fr) minmax(220px, 320px) auto auto;
margin-bottom: 10px;
padding: 12px;
}
.visual-fork-controls h3 {
color: var(--ink);
font-size: 16px;
margin: 0 0 4px;
}
.visual-fork-controls p {
color: var(--muted);
margin: 0;
}
.visual-fork-controls input {
background: #ffffff;
border: 1px solid var(--line-strong);
border-radius: 6px;
color: var(--ink);
font: inherit;
min-width: 0;
padding: 9px 10px;
}
.visual-fork-controls button {
background: #ffffff;
border: 1px solid var(--line-strong);
border-radius: 6px;
color: var(--ink);
cursor: pointer;
font: inherit;
padding: 9px 10px;
}
.visual-fork-button {
background: #ffffff !important;
border-color: #94a3b8 !important;
color: var(--ink) !important;
font-weight: 700 !important;
}
.visual-action-bar {
align-items: center;
background: #ffffff;
border: 1px solid var(--line);
border-radius: 8px;
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 10px;
padding: 8px;
}
.visual-server-button {
background: #ffffff;
border: 1px solid var(--line-strong);
border-radius: 6px;
color: var(--ink);
cursor: pointer;
font: inherit;
font-weight: 700;
min-height: 34px;
padding: 7px 11px;
}
.visual-server-button:hover {
background: #f8fafc;
}
.visual-server-button.primary,
.visual-server-button.positive {
background: linear-gradient(180deg, #18a999, #0f766e);
border-color: #0f766e;
color: #ffffff;
}
.visual-fork-inspector {
background: #eefaf7;
border: 1px solid var(--line);
border-radius: 8px;
color: var(--muted);
display: flex;
gap: 10px;
margin-bottom: 14px;
padding: 10px 12px;
}
.visual-fork-inspector strong {
color: var(--accent);
}
.tree-divider {
align-items: center;
color: var(--muted);
display: flex;
font-size: 12px;
gap: 10px;
margin: 10px 0 14px;
text-transform: uppercase;
}
.tree-divider:before,
.tree-divider:after {
background: var(--line);
content: "";
flex: 1;
height: 1px;
}
.badge {
background: #e7f7f3;
border: 1px solid #a9ded2;
border-radius: 999px;
color: #0f5f57;
font-size: 13px;
padding: 6px 10px;
white-space: nowrap;
}
.trace-tree, .trace-tree ol, .trace-tree ul {
list-style: none;
margin: 0;
padding-left: 24px;
position: relative;
}
.trace-tree {
padding-left: 0;
}
.trace-tree li {
margin: 8px 0;
position: relative;
}
.trace-tree li:before {
border-left: 1px solid var(--line);
content: "";
height: calc(100% + 8px);
left: -14px;
position: absolute;
top: -8px;
}
.trace-tree li:after {
border-top: 1px solid var(--line);
content: "";
left: -14px;
position: absolute;
top: 34px;
width: 14px;
}
.trace-tree > li:before, .trace-tree > li:after {
display: none;
}
.trace-node {
background: #ffffff;
border: 1px solid var(--line-strong);
border-left: 5px solid var(--muted);
border-radius: 8px;
box-shadow: 0 1px 1px rgba(15, 23, 42, 0.04);
color: var(--ink);
cursor: pointer;
display: block;
font: inherit;
max-width: 680px;
min-width: 260px;
padding: 10px 12px;
text-align: left;
transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease;
}
.trace-node:hover {
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.09);
transform: translateY(-1px);
}
.trace-node:active {
cursor: grabbing;
}
.trace-node.selected {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.16);
}
.trace-node.dragging {
opacity: 0.58;
transform: scale(0.99);
}
.trace-node.drop-target {
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
}
.trace-node.marked {
background: #f8fbff;
border-color: #9ab3dd;
}
.trace-node.annotated {
background: #fffdf4;
}
.trace-node.thought { border-left-color: var(--blue); }
.trace-node.tool { border-left-color: var(--gold); }
.trace-node.observation { border-left-color: var(--accent); }
.trace-node.checkpoint { border-left-color: #2563eb; }
.trace-node.fork { border-left-color: var(--accent-2); }
.trace-node.dead_end { border-left-color: var(--danger); }
.trace-node.backtrack { border-left-color: #6f5f16; }
.trace-node.answer { border-left-color: var(--violet); }
.trace-node.branch {
background: #fff7ed;
}
.node-top {
align-items: center;
display: flex;
gap: 8px;
justify-content: space-between;
margin-bottom: 6px;
}
.kind, .timeline {
color: var(--muted);
font-size: 11px;
letter-spacing: 0;
text-transform: uppercase;
}
.trace-node strong {
display: block;
font-size: 15px;
margin-bottom: 4px;
}
.node-labels {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin: 0 0 6px;
}
.node-label {
background: #edf2ff;
border: 1px solid #c8d7ff;
border-radius: 999px;
color: #315fbd;
display: inline-flex;
font-size: 11px;
line-height: 1;
padding: 4px 7px;
}
.inspector-labels {
margin: 0 0 10px;
}
.trace-node .body {
color: #374151;
display: block;
font-size: 13px;
line-height: 1.35;
}
.node-annotation {
background: #fffbeb;
border: 1px solid #fde68a;
border-radius: 6px;
color: #713f12;
display: block;
font-size: 12px;
line-height: 1.35;
margin-top: 8px;
padding: 7px 8px;
}
.trace-context-menu {
background: #ffffff;
border: 1px solid var(--line-strong);
border-radius: 8px;
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.16);
color: var(--ink);
display: none;
min-width: 230px;
padding: 6px;
position: fixed;
z-index: 9999;
}
.trace-context-menu.open {
display: block;
}
.trace-context-menu button {
align-items: center;
background: transparent;
border: 0;
border-radius: 6px;
color: var(--ink);
cursor: pointer;
display: flex;
font: inherit;
gap: 8px;
justify-content: space-between;
padding: 8px 9px;
text-align: left;
width: 100%;
}
.trace-context-menu button:hover {
background: #f1f5f9;
}
.trace-context-menu .danger {
color: #b91c1c;
}
.inspector {
background: linear-gradient(180deg, #ffffff, #f8fafc);
border: 1px solid var(--line);
border-radius: 8px;
padding: 16px;
}
.inspector-kicker {
color: var(--accent);
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
}
.inspector h3 {
color: var(--ink);
font-size: 20px;
margin: 6px 0 8px;
}
.inspector p {
color: #374151;
}
.inspector dl {
display: grid;
grid-template-columns: 76px 1fr;
gap: 6px 12px;
}
.inspector dt {
color: var(--muted);
font-size: 12px;
}
.inspector dd {
font-size: 12px;
margin: 0;
overflow-wrap: anywhere;
}
.inspector pre {
background: #f3f5f8;
color: #1f2937;
border: 1px solid var(--line);
border-radius: 6px;
overflow: auto;
padding: 10px;
}
.gradio-container table {
background: var(--panel) !important;
color: var(--ink) !important;
}
.gradio-container th {
background: #f3f5f8 !important;
color: var(--muted) !important;
}
.gradio-container td {
background: var(--panel) !important;
border-color: var(--line) !important;
color: var(--ink) !important;
}
.gradio-container [data-testid="block-label"] {
color: var(--muted) !important;
}
.server-bridge,
#visual-action-btn {
display: none !important;
}
@media (max-width: 760px) {
.visual-fork-controls {
grid-template-columns: 1fr;
}
.tree-header {
display: block;
}
.badge {
display: inline-block;
margin-top: 10px;
}
.trace-node {
min-width: 220px;
}
}
"""
JS = """
(() => {
const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (ch) => ({
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;"
}[ch]));
let draggedItem = null;
let pointerDrag = null;
let suppressClickNode = null;
const selectedNode = (shell) => shell.querySelector(".trace-node.selected") || shell.querySelector(".trace-node");
const shellForNode = (node) => node?.closest(".tree-shell");
const bridgeInput = (id) => document.querySelector(`#${id} textarea, #${id} input`);
const setBridgeValue = (id, value) => {
const input = bridgeInput(id);
if (!input) return false;
const setter = Object.getOwnPropertyDescriptor(input.constructor.prototype, "value")?.set;
if (setter) setter.call(input, value);
else input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
return true;
};
const triggerServerAction = (shell, action, node) => {
if (!shell || !node) return false;
let prompt = shell.querySelector(".visual-fork-prompt")?.value?.trim() || "Retry this branch with a clearer assumption.";
if (action === "prefer") prompt = "This branch is more grounded and useful.";
if (action === "reject") prompt = "This branch is less useful than the comparison branch.";
const ok = [
setBridgeValue("visual-action-kind", action),
setBridgeValue("visual-action-node", node.dataset.nodeId || ""),
setBridgeValue("visual-action-prompt", prompt)
].every(Boolean);
const button = document.querySelector("#visual-action-btn");
if (!ok || !button) return false;
button.click();
return true;
};
const updateInspector = (shell, node) => {
const inspector = shell.querySelector(".visual-fork-inspector");
const controls = shell.querySelector(".visual-fork-controls");
if (!inspector || !controls || !node) return;
controls.dataset.selectedNode = node.dataset.nodeId || "";
const kind = node.querySelector(".kind")?.textContent?.trim() || "node";
const timeline = node.querySelector(".timeline")?.textContent?.trim() || "main";
const title = node.querySelector("strong")?.textContent?.trim() || "Untitled node";
const body = node.querySelector(".body")?.textContent?.trim() || "";
inspector.innerHTML = `
<strong>Selected visual node</strong>
<span class="visual-selected-title">${esc(title)} (${esc(kind)} / ${esc(timeline)})</span>
<span>${esc(body).slice(0, 180)}</span>
`;
};
const selectVisualNode = (shell, node, sync = true) => {
if (!shell || !node) return;
shell.querySelectorAll(".trace-node.selected").forEach((selected) => selected.classList.remove("selected"));
node.classList.add("selected");
updateInspector(shell, node);
if (sync) triggerServerAction(shell, "select", node);
};
const ensureLabelRack = (node) => {
let rack = node.querySelector(".node-labels");
if (rack) return rack;
rack = document.createElement("span");
rack.className = "node-labels";
const title = node.querySelector("strong");
if (title?.nextSibling) {
node.insertBefore(rack, title.nextSibling);
} else {
node.appendChild(rack);
}
return rack;
};
const addVisualLabel = (node, label) => {
const rack = ensureLabelRack(node);
const exists = [...rack.querySelectorAll(".node-label")].some((item) => item.textContent.trim() === label);
if (!exists) {
const pill = document.createElement("span");
pill.className = "node-label";
pill.textContent = label;
rack.appendChild(pill);
}
node.classList.add("marked");
persistVisualTrace(shellForNode(node), `label:${label}`);
};
const annotateVisualNode = (node, text) => {
const note = document.createElement("span");
note.className = "node-annotation";
note.textContent = text;
node.appendChild(note);
node.classList.add("annotated");
addVisualLabel(node, "annotated");
persistVisualTrace(shellForNode(node), "annotate");
};
const moveVisualNode = (shell, node, direction) => {
const item = node.closest("li");
if (!item) return;
if (direction === "up" && item.previousElementSibling) {
item.parentElement.insertBefore(item, item.previousElementSibling);
}
if (direction === "down" && item.nextElementSibling) {
item.parentElement.insertBefore(item.nextElementSibling, item);
}
selectVisualNode(shell, node, false);
persistVisualTrace(shell, `move:${direction}`);
};
const removeVisualNode = (shell, node) => {
if (node.dataset.nodeId === "root") {
annotateVisualNode(node, "Root stays put; remove a branch node instead.");
return;
}
const item = node.closest("li");
const parentNode = item?.parentElement?.closest("li")?.querySelector(":scope > .trace-node");
if (!item) return;
item.remove();
selectVisualNode(shell, parentNode || selectedNode(shell));
persistVisualTrace(shell, "remove");
};
const siblingDrop = (shell, targetNode) => {
const targetItem = targetNode?.closest("li");
if (!draggedItem || !targetItem || draggedItem === targetItem || draggedItem.parentElement !== targetItem.parentElement) {
return false;
}
targetItem.parentElement.insertBefore(draggedItem, targetItem.nextElementSibling);
const movedNode = draggedItem.querySelector(":scope > .trace-node");
selectVisualNode(shell, movedNode);
persistVisualTrace(shell, "drag-reorder");
return true;
};
const contextMenu = () => {
let menu = document.querySelector(".trace-context-menu");
if (menu) return menu;
menu = document.createElement("div");
menu.className = "trace-context-menu";
document.body.appendChild(menu);
return menu;
};
const closeContextMenu = () => {
document.querySelector(".trace-context-menu")?.classList.remove("open");
};
const menuButton = (label, action, danger = false) => {
const button = document.createElement("button");
button.type = "button";
button.textContent = label;
if (danger) button.className = "danger";
button.onclick = (event) => {
event.preventDefault();
action();
closeContextMenu();
};
return button;
};
const openContextMenu = (shell, node, x, y) => {
selectVisualNode(shell, node, false);
const menu = contextMenu();
menu.innerHTML = "";
menu.appendChild(menuButton("Select on server", () => triggerServerAction(shell, "select", node)));
menu.appendChild(menuButton("Retry branch on server", () => triggerServerAction(shell, "retry", node)));
menu.appendChild(menuButton("Replay downstream on server", () => triggerServerAction(shell, "replay", node)));
menu.appendChild(menuButton("Annotate on server", () => triggerServerAction(shell, "annotate", node)));
menu.appendChild(menuButton("Mark useful on server", () => triggerServerAction(shell, "mark_useful", node)));
menu.appendChild(menuButton("Mark weak on server", () => triggerServerAction(shell, "mark_weak", node)));
menu.appendChild(menuButton("Prefer on server", () => triggerServerAction(shell, "prefer", node)));
menu.appendChild(menuButton("Reject on server", () => triggerServerAction(shell, "reject", node)));
menu.appendChild(menuButton("Move up (visual only)", () => moveVisualNode(shell, node, "up")));
menu.appendChild(menuButton("Move down (visual only)", () => moveVisualNode(shell, node, "down")));
menu.appendChild(menuButton("Remove visual node only", () => removeVisualNode(shell, node), true));
const width = 240;
const height = 330;
menu.style.left = `${Math.max(8, Math.min(x, window.innerWidth - width))}px`;
menu.style.top = `${Math.max(8, Math.min(y, window.innerHeight - height))}px`;
menu.classList.add("open");
};
const bindTree = (shell) => {
shell.querySelectorAll(".trace-node").forEach((node) => {
node.dataset.visualBound = "true";
node.onclick = (event) => {
event.preventDefault();
if (suppressClickNode === node) {
suppressClickNode = null;
return;
}
selectVisualNode(shell, node);
};
node.oncontextmenu = (event) => {
event.preventDefault();
openContextMenu(shell, node, event.clientX, event.clientY);
};
node.draggable = true;
node.ondragstart = (event) => {
draggedItem = node.closest("li");
node.classList.add("dragging");
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", node.dataset.nodeId || "");
};
node.ondragend = () => {
node.classList.remove("dragging");
shell.querySelectorAll(".drop-target").forEach((target) => target.classList.remove("drop-target"));
draggedItem = null;
};
node.ondragover = (event) => {
if (!draggedItem) return;
const targetItem = node.closest("li");
if (targetItem && draggedItem.parentElement === targetItem.parentElement && draggedItem !== targetItem) {
event.preventDefault();
node.classList.add("drop-target");
}
};
node.ondragleave = () => node.classList.remove("drop-target");
node.ondrop = (event) => {
const targetItem = node.closest("li");
if (!draggedItem || !targetItem || draggedItem === targetItem || draggedItem.parentElement !== targetItem.parentElement) return;
event.preventDefault();
node.classList.remove("drop-target");
siblingDrop(shell, node);
};
node.onpointerdown = (event) => {
if (event.button !== 0) return;
pointerDrag = {
shell,
node,
item: node.closest("li"),
startX: event.clientX,
startY: event.clientY,
active: false
};
draggedItem = pointerDrag.item;
node.setPointerCapture?.(event.pointerId);
};
});
updateInspector(shell, selectedNode(shell));
};
const branchButton = (kind, title, body, timeline, nodeId) => `
<button class="trace-node ${kind} done branch" data-node-id="${esc(nodeId)}">
<span class="node-top">
<span class="kind">${esc(kind)}</span>
<span class="timeline">${esc(timeline)}</span>
</span>
<strong>${esc(title)}</strong>
<span class="body">${esc(body)}</span>
</button>
`;
const forkVisualNode = (shell) => {
const parent = selectedNode(shell);
if (!parent) return;
const prompt = shell.querySelector(".visual-fork-prompt")?.value?.trim() || "Try the opposite assumption from this node.";
const controls = shell.querySelector(".visual-fork-controls");
const count = Number(controls?.dataset.nodeCount || "0") + 1;
if (controls) controls.dataset.nodeCount = String(count + 2);
const timeline = `visual-${count}`;
const thoughtId = `visual-thought-${Date.now()}`;
const obsId = `visual-observation-${Date.now()}`;
const parentLi = parent.closest("li");
let childList = parentLi.querySelector(":scope > ul");
if (!childList) {
childList = document.createElement("ul");
parentLi.appendChild(childList);
}
const thoughtLi = document.createElement("li");
thoughtLi.innerHTML = `
${branchButton("thought", "Visual fork", prompt, timeline, thoughtId)}
<ul>
<li>
${branchButton("observation", "Alternate timeline result", "This branch was created by clicking a visual trace node. Compare it with sibling branches before trusting the answer.", timeline, obsId)}
</li>
</ul>
`;
childList.appendChild(thoughtLi);
shell.querySelectorAll(".trace-node.selected").forEach((selected) => selected.classList.remove("selected"));
const observation = thoughtLi.querySelector(`[data-node-id="${obsId}"]`);
observation.classList.add("selected");
bindTree(shell);
updateInspector(shell, observation);
persistVisualTrace(shell, "visual-fork");
};
const visualTracePayload = (shell, event = "visual-snapshot") => {
const controls = shell.querySelector(".visual-fork-controls");
const nodes = [...shell.querySelectorAll(".trace-node")].map((node) => ({
id: node.dataset.nodeId || "",
kind: node.querySelector(".kind")?.textContent?.trim() || "",
timeline: node.querySelector(".timeline")?.textContent?.trim() || "",
title: node.querySelector("strong")?.textContent?.trim() || "",
body: node.querySelector(".body")?.textContent?.trim() || "",
labels: [...node.querySelectorAll(".node-label")].map((label) => label.textContent.trim()),
annotations: [...node.querySelectorAll(".node-annotation")].map((note) => note.textContent.trim()),
selected: node.classList.contains("selected")
}));
return {
app: "Glass-Box Agent",
artifact: "visual_trace",
source: "visual_dom_fork_canvas",
event,
trace_id: controls?.dataset.traceId || "visual-trace",
exported_at: new Date().toISOString(),
nodes
};
};
const persistVisualTrace = (shell, event = "visual-snapshot") => {
if (!shell) return;
const endpoint = shell.querySelector(".visual-fork-controls")?.dataset.modalTraceUrl;
if (!endpoint) return;
fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(visualTracePayload(shell, event)),
keepalive: true
}).catch(() => {});
};
const downloadVisualTrace = (shell) => {
const payload = visualTracePayload(shell, "download_visual_trace");
const blob = new Blob([JSON.stringify({
...payload
}, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `glass-box-visual-trace-${Date.now()}.json`;
link.click();
URL.revokeObjectURL(url);
};
const bindShell = (shell) => {
bindTree(shell);
const fork = shell.querySelector(".visual-fork-button");
if (fork) {
fork.dataset.visualBound = "true";
fork.onclick = () => triggerServerAction(shell, "retry", selectedNode(shell));
}
shell.querySelectorAll(".visual-server-button").forEach((button) => {
button.onclick = () => triggerServerAction(shell, button.dataset.action || "select", selectedNode(shell));
});
const download = shell.querySelector(".visual-export-button");
if (download) {
download.dataset.visualBound = "true";
download.onclick = () => downloadVisualTrace(shell);
}
};
document.addEventListener("click", (event) => {
if (!event.target.closest(".trace-context-menu")) closeContextMenu();
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") closeContextMenu();
});
document.addEventListener("pointermove", (event) => {
if (!pointerDrag) return;
const distance = Math.abs(event.clientX - pointerDrag.startX) + Math.abs(event.clientY - pointerDrag.startY);
if (distance < 12) return;
pointerDrag.active = true;
pointerDrag.node.classList.add("dragging");
pointerDrag.shell.querySelectorAll(".drop-target").forEach((target) => target.classList.remove("drop-target"));
const targetNode = document.elementFromPoint(event.clientX, event.clientY)?.closest(".trace-node");
const targetItem = targetNode?.closest("li");
if (targetNode && targetItem && targetItem !== pointerDrag.item && targetItem.parentElement === pointerDrag.item.parentElement) {
targetNode.classList.add("drop-target");
}
});
document.addEventListener("pointerup", (event) => {
if (!pointerDrag) return;
const drag = pointerDrag;
pointerDrag = null;
drag.node.classList.remove("dragging");
drag.shell.querySelectorAll(".drop-target").forEach((target) => target.classList.remove("drop-target"));
if (drag.active) {
suppressClickNode = drag.node;
const targetNode = document.elementFromPoint(event.clientX, event.clientY)?.closest(".trace-node");
siblingDrop(drag.shell, targetNode);
}
draggedItem = null;
});
const bindAll = () => document.querySelectorAll(".tree-shell").forEach(bindShell);
bindAll();
setInterval(bindAll, 500);
})();
"""
with gr.Blocks(title=APP_TITLE) as demo:
state = gr.State(new_state())
gr.Markdown(
f"""
# {APP_TITLE}
{APP_TAGLINE} Start at task intake, evidence, prototype, critique, validation, or synthesis.
"""
)
with gr.Row():
with gr.Column(scale=3, elem_classes="command-rail"):
gr.Markdown("### Run")
task = gr.Textbox(
label="Task",
lines=4,
value="Design a delightful hackathon demo that proves small agents can be transparent, forkable, and useful.",
elem_classes="task-box",
)
steps = gr.Slider(2, MAX_STEPS, value=5, step=1, label="Trace depth", elem_classes="depth-slider")
run_btn = gr.Button("Run trace", variant="primary", elem_id="run-btn")
with gr.Accordion("Advanced setup", open=False, elem_classes="advanced-setup"):
start_phase = gr.Dropdown(
START_MODES,
value=START_TASK,
label="Start point",
info="Seed the trace from a specific phase instead of always beginning at task intake.",
)
backend = gr.Radio(
[BACKEND_OFFLINE, BACKEND_MODAL, BACKEND_HF],
value=BACKEND_OFFLINE,
label="Reasoner",
)
model_id = gr.Textbox(
label="Small model",
value=DEFAULT_MODEL_ID,
lines=1,
)
with gr.Accordion("Dataset trial", open=False, elem_classes="dataset-trial"):
dataset_choice = gr.Dropdown(
DATASET_NAMES,
value=DATASET_NAMES[0],
label="Example set",
)
with gr.Row(elem_classes="compact-actions"):
load_dataset_btn = gr.Button("Load example", elem_id="load-dataset-btn")
run_dataset_btn = gr.Button("Run mini dataset", elem_id="run-dataset-btn")
dataset_table = gr.Dataframe(
headers=["example", "nodes", "pairs", "label", "trace"],
datatype=["str", "str", "str", "str", "str"],
value=[],
interactive=False,
label="Dataset results",
wrap=True,
elem_classes="dataset-table",
)
dataset_file = gr.File(label="Dataset artifact")
gr.Markdown("### Advanced node controls")
operation_prompt = gr.Textbox(
label="Instruction",
value="Retry this branch with a clearer assumption.",
lines=2,
)
with gr.Row(elem_classes="action-row"):
retry_btn = gr.Button("Retry branch", variant="primary", elem_id="retry-btn")
replay_btn = gr.Button("Replay downstream", elem_id="replay-btn")
with gr.Row(elem_classes="label-row"):
label_choice = gr.Dropdown(LABELS, value="weak", label="Label")
mark_btn = gr.Button("Apply", elem_id="mark-btn")
with gr.Row(elem_classes="compact-actions"):
summary_btn = gr.Button("Summarize", elem_id="summary-btn")
clear_marks_btn = gr.Button("Clear", elem_id="clear-btn")
with gr.Row(elem_classes="compact-actions"):
fork_btn = gr.Button("Fork", elem_id="fork-btn")
export_btn = gr.Button("Export JSON", elem_id="export-btn")
with gr.Accordion("Training signal", open=False, elem_classes="training-signal"):
feedback_reason = gr.Textbox(
label="Feedback note",
value="This branch is more grounded and useful.",
lines=2,
)
with gr.Row(elem_classes="action-row"):
prefer_btn = gr.Button("Prefer selected", variant="primary", elem_id="prefer-btn")
reject_btn = gr.Button("Reject selected", elem_id="reject-btn")
export_rl_btn = gr.Button("Export RL data", elem_id="export-rl-btn")
tune_btn = gr.Button("RL tune on Modal", elem_id="tune-btn")
feedback_table = gr.Dataframe(
headers=["id", "chosen", "rejected", "reason", "created"],
datatype=["str", "str", "str", "str", "str"],
value=feedback_rows(new_state()),
interactive=False,
label="Preference pairs",
wrap=True,
elem_classes="feedback-table",
)
action_log = gr.Textbox(
label="Action log",
value="Select a node from the trace table, then retry, replay, or label it.",
lines=4,
interactive=False,
elem_classes="action-log",
)
trace_file = gr.File(label="Artifact", elem_classes="trace-artifact")
visual_action_kind = gr.Textbox(value="select", elem_id="visual-action-kind", elem_classes="server-bridge")
visual_action_node = gr.Textbox(value="root", elem_id="visual-action-node", elem_classes="server-bridge")
visual_action_prompt = gr.Textbox(
value="Retry this branch with a clearer assumption.",
elem_id="visual-action-prompt",
elem_classes="server-bridge",
)
visual_action_btn = gr.Button("Run visual action", elem_id="visual-action-btn")
with gr.Column(scale=4):
tree = gr.HTML(render_tree(new_state()), label="Trace tree")
trace_table = gr.Dataframe(
headers=["id", "node", "kind", "timeline", "status", "labels", "parent"],
datatype=["str", "str", "str", "str", "str", "str", "str"],
value=trace_rows(new_state()),
interactive=False,
label="Click a trace node to inspect or operate on it",
wrap=True,
)
with gr.Column(scale=2):
inspector = gr.HTML(render_inspector(new_state()), label="Inspector")
transcript = gr.Textbox(label="Readable trace log", lines=18, interactive=False)
run_btn.click(
run_agent,
inputs=[task, steps, start_phase, backend, model_id, state],
outputs=[tree, trace_table, inspector, transcript, state, feedback_table],
)
load_dataset_btn.click(
load_dataset_example,
inputs=[dataset_choice],
outputs=[task, steps, start_phase, operation_prompt, action_log],
)
run_dataset_btn.click(
run_dataset_eval,
inputs=[backend, model_id],
outputs=[dataset_table, dataset_file, action_log],
)
trace_table.select(
select_trace_row,
inputs=[state],
outputs=[tree, trace_table, inspector, action_log, state],
)
visual_action_btn.click(
visual_server_action,
inputs=[visual_action_kind, visual_action_node, visual_action_prompt, backend, model_id, state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file, feedback_table],
)
retry_btn.click(
retry_selected_branch,
inputs=[operation_prompt, backend, model_id, state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file],
)
replay_btn.click(
replay_downstream,
inputs=[operation_prompt, state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file],
)
mark_btn.click(
mark_selected,
inputs=[label_choice, state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file],
)
summary_btn.click(
summarize_marked,
inputs=[state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file],
)
clear_marks_btn.click(
clear_marks,
inputs=[state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file],
)
fork_btn.click(
fork_selected,
inputs=[operation_prompt, state],
outputs=[tree, trace_table, inspector, transcript, state, trace_file],
)
export_btn.click(export_only, inputs=[state], outputs=[trace_file])
prefer_btn.click(
prefer_selected,
inputs=[feedback_reason, state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file, feedback_table],
)
reject_btn.click(
reject_selected,
inputs=[feedback_reason, state],
outputs=[tree, trace_table, inspector, transcript, action_log, state, trace_file, feedback_table],
)
export_rl_btn.click(
export_rl_dataset,
inputs=[state],
outputs=[action_log, state, trace_file, feedback_table],
)
tune_btn.click(
start_modal_tuning,
inputs=[model_id, state],
outputs=[action_log, state, feedback_table],
)
if __name__ == "__main__":
demo.launch(css=CSS, head=f"<script>{JS}</script>", theme=gr.themes.Soft())