File size: 6,866 Bytes
889dea9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
"""FATHOM Streamlit Demo β€” DEM-03.

2-panel layout:
  Left:   Recursion tree visualization (D3 or plotly sunburst)
  Right:  W&B training curves embed (live iframe)

Run: streamlit run space_demo/app.py
"""
from __future__ import annotations

import json
import os

import streamlit as st

st.set_page_config(
    page_title="FATHOM β€” RL-Trained Recursive Language Model",
    page_icon="🧠",
    layout="wide",
)

st.markdown(
    f"**Live env:** [Pratham-math/fathom-env]({os.environ.get('FATHOM_SPACE_URL','https://Pratham-math-fathom-env.hf.space')}) "
    f"Β· **Trained model:** [Pratham-math/fathom-1.5b-grpo](https://huggingface.co/Pratham-math/fathom-1.5b-grpo) "
    f"Β· **W&B:** [run sy1tqun0](https://wandb.ai/pratham-alwar05-indian-institute-of-information-technolo/huggingface/runs/sy1tqun0)"
)

# ---------------------------------------------------------------------------
# Header
# ---------------------------------------------------------------------------
st.markdown("## 🧠 FATHOM Demo")
st.markdown(
    "_First RL-Trained Recursive Language Model β€” Meta Γ— PyTorch Γ— HF Hackathon_"
)
st.divider()

# ---------------------------------------------------------------------------
# Reward composition badge (DEM-03 C.4: visible-to-judges reward overview)
# ---------------------------------------------------------------------------
with st.expander("Reward composition (4 components, deterministic verifier)", expanded=True):
    cb1, cb2 = st.columns([1, 2], gap="medium")
    with cb1:
        st.markdown("**Format gate** (multiplier)")
        st.success("`<answer>...</answer>` required \u2014 if missing, soft bonus + cap applies")
        st.caption("Source: `rewards/compose.py` \u2014 audited in REWARD_AUDIT.md")
    with cb2:
        try:
            import plotly.graph_objects as go  # type: ignore
            labels = ["correctness", "token_budget", "recursion_efficiency"]
            weights = [0.70, 0.15, 0.15]
            colors = ["#2ca02c", "#1f77b4", "#ff7f0e"]
            fig0 = go.Figure(go.Pie(
                labels=labels, values=weights, marker=dict(colors=colors),
                hole=0.4, textinfo="label+percent",
            ))
            fig0.update_layout(margin=dict(l=10, r=10, t=10, b=10), height=200, showlegend=False)
            st.plotly_chart(fig0, use_container_width=True)
        except ImportError:
            st.metric("correctness", 0.70)
            st.metric("token_budget", 0.15)
            st.metric("recursion_efficiency", 0.15)

st.divider()

# ---------------------------------------------------------------------------
# Sidebar: controls
# ---------------------------------------------------------------------------
with st.sidebar:
    st.header("Controls")
    env_url = st.text_input(
        "Env server URL",
        value=os.environ.get("FATHOM_SPACE_URL", "https://Pratham-math-fathom-env.hf.space"),
        key="env_url",
    )
    st.divider()
    st.caption("Source: [github.com/Pratham-math/fathom](https://github.com/Pratham-math/fathom)")

@st.cache_data(ttl=3600)
def fetch_trace(url: str) -> dict:
    import httpx
    try:
        # Provide a quick dummy trace by interacting with the env.
        # Note: the real recursive model is not loaded in Streamlit,
        # so this just grabs the reset observation and submits a dummy answer.
        r = httpx.post(f"{url}/reset", json={"seed": 42}, timeout=10)
        r.raise_for_status()
        obs = r.json()
        q = obs.get("question", "Question")
        
        s = httpx.post(f"{url}/step", json={"action_type": "answer", "answer": "simulated"}, timeout=10)
        s.raise_for_status()
        
        return {
            "name": f"Live init: {q[:30]}...",
            "children": [
                {"name": "REPL: simulated step"},
                {"name": "β†’ <answer>simulated</answer>"}
            ]
        }
    except Exception as e:
        return {
            "name": "[Example Trace] root: 200K doc",
            "children": [
                {
                    "name": "llm(chunk_0-50K)",
                    "children": [{"name": "REPL: grep β†’ 'azure'"}],
                },
                {"name": "REPL: count_tokens β†’ 200K"},
                {"name": "β†’ <answer>azure</answer>"},
            ],
        }

# ---------------------------------------------------------------------------
# 2 columns
# ---------------------------------------------------------------------------
col_tree, col_wb = st.columns([1.5, 1.5], gap="medium")

# ── Column 1: Recursion tree ────────────────────────────────────────────────
with col_tree:
    st.subheader("Recursion Tree")
    st.caption("Sample episode trace")

    sample_tree = fetch_trace(env_url)
    tree_json = json.dumps(sample_tree)
    d3_html = f"""
    <html>
    <head>
    <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
    <style>
      body {{ font-family: monospace; font-size: 12px; }}
      .node circle {{ fill: #6366f1; stroke: #312e81; stroke-width: 1.5px; }}
      .node text {{ fill: #1e1b4b; }}
      .link {{ fill: none; stroke: #a5b4fc; stroke-width: 1.5px; }}
    </style>
    </head>
    <body>
    <div id="tree"></div>
    <script>
    const data = {tree_json};
    const width = 400, height = 240;
    const svg = d3.select("#tree").append("svg").attr("width", width).attr("height", height);
    const g = svg.append("g").attr("transform", "translate(40,20)");
    const tree = d3.tree().size([height-40, width-120]);
    const root = d3.hierarchy(data);
    tree(root);
    g.selectAll(".link").data(root.links()).enter().append("path")
      .attr("class","link")
      .attr("d", d3.linkHorizontal().x(d=>d.y).y(d=>d.x));
    const node = g.selectAll(".node").data(root.descendants()).enter()
      .append("g").attr("class","node")
      .attr("transform", d=>`translate(${{d.y}},${{d.x}})`);
    node.append("circle").attr("r", 5);
    node.append("text").attr("dy","0.35em").attr("x", d=>d.children?-8:8)
      .attr("text-anchor", d=>d.children?"end":"start")
      .text(d=>d.data.name.slice(0,35));
    </script>
    </body></html>
    """
    st.components.v1.html(d3_html, height=280)

# ── Column 3: W&B training curves ────────────────────────────────────────────
with col_wb:
    st.subheader("Training Curves")
    wb_url = os.environ.get("WANDB_RUN_URL", "https://wandb.ai/pratham-alwar05-indian-institute-of-information-technolo/huggingface/runs/sy1tqun0")
    if wb_url:
        st.components.v1.iframe(wb_url, height=260, scrolling=True)
    else:
        st.info("Set `WANDB_RUN_URL` env var to embed live training curves.")

st.divider()
st.caption("FATHOM Demo Space")