agharsallah commited on
Commit
52b556c
Β·
1 Parent(s): ed6315a

feat(about): add About tab with project overview, architecture, and links

Browse files
Files changed (3) hide show
  1. README.md +46 -9
  2. src/ui/fishbowl/about.py +193 -0
  3. src/ui/fishbowl/app.py +11 -0
README.md CHANGED
@@ -8,18 +8,45 @@ sdk_version: "6.16.0"
8
  python_version: "3.10"
9
  app_file: app.py
10
  pinned: true
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
  # Multi-Agent Land
14
 
15
- Hackathon project for the **Thousand Token Wood** trail: a small-model, multi-agent
16
- interactive story engine where the AI is load-bearing for the experience.
17
 
18
- > One tiny event-sourced engine can power many delightful worlds. A whimsical forest
19
- > theater, a mystery-solving blackboard swarm, and a tool-using oracle grove are not
20
- > three apps β€” they are three YAML configs of the *same* engine. Small specialist
21
- > agents write, judge, remember, and render strange interactive scenes, each on the
22
- > small model that fits its job.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  ---
25
 
@@ -67,13 +94,18 @@ A **tiny theater engine** powered by specialist small-model agents. Agents neve
67
  call each other directly β€” they post typed events to a shared append-only ledger,
68
  and every view (the stage, the memory, the UI) is a projection derived from that log.
69
 
 
 
 
 
 
70
  What makes it *super modular*:
71
  - **Config, not code.** Agents, scenarios, casts, model tiers, tool grants, and
72
  budgets are declarative YAML under `config/`, validated by a schema. Add a world
73
  by adding files β€” proven by `tests/test_modularity.py` (zero engine edits).
74
  - **A model per agent.** Each agent declares a logical profile (`tiny`/`fast`/
75
- `balanced`/`strong`); a `ModelRouter` binds it to a concrete small model. Mix a
76
- ≀4B worker with a ≀32B judge in one cast.
77
  - **Capability-checked tools.** Agents call tools only if their manifest grants
78
  them β€” the contract that fronts in-process tools today and MCP servers later.
79
  - **Built to run for hours.** The ledger is the checkpoint: `restore()` resumes a
@@ -254,3 +286,8 @@ uv run scripts/snapshot_progress.py
254
  # 4. Confirm nothing broke
255
  uv run pytest tests/ -q
256
  ```
 
 
 
 
 
 
8
  python_version: "3.10"
9
  app_file: app.py
10
  pinned: true
11
+ tags:
12
+ - agent-demo-track
13
+ - track:wood
14
+ - sponsor:openai
15
+ - sponsor:nvidia
16
+ - sponsor:modal
17
+ - sponsor:openbmb
18
+ - achievement:offbrand
19
+ - achievement:sharing
20
+ - achievement:fieldnotes
21
  ---
22
 
23
  # Multi-Agent Land
24
 
25
+ **Small models, one shared log, and a clear view of how agents behave in motion.**
 
26
 
27
+ Most multi-agent systems are hard to inspect: agents call each other directly and the
28
+ state gets messy. We wanted to *see* small agents in action β€” not isolated prompts, but
29
+ small models interacting over time: **debating, collaborating, playing games, and
30
+ pushing each other** in a shared environment.
31
+
32
+ So we built one. Every action β€” thoughts, tool calls, state updates β€” is appended to a
33
+ single **immutable log**. When one agent asks, another answers, a judge evaluates, and a
34
+ keeper tracks progress, *nothing is sent agent-to-agent* β€” every interaction flows
35
+ through that one shared ledger. So you can follow the whole run, step by step.
36
+
37
+ ---
38
+
39
+ ## Submission
40
+
41
+
42
+ - **🎬 Demo video:** https://youtu.be/v8-zR6eTbDM
43
+ - **πŸ“£ Social post:** https://www.linkedin.com/posts/gharsallah_huggingface-hackathon-buildsmall-activity-7472383877991501824-8vxO
44
+ - **πŸ’» GitHub link:** https://github.com/abducodez/multi-agent-land
45
+ - **πŸ‘₯ Team (Hugging Face usernames):** <!-- TODO: list every teammate's HF username; each must register + join the org separately -->
46
+ - `@agharsalah`
47
+
48
+ > Tags (track + badges) live in the YAML block at the top of this README β€” without
49
+ > them the project can't be placed in a category.
50
 
51
  ---
52
 
 
94
  call each other directly β€” they post typed events to a shared append-only ledger,
95
  and every view (the stage, the memory, the UI) is a projection derived from that log.
96
 
97
+ **The loop is simple:** define the environment and the agent roles, then launch them in
98
+ the multi-agent lab. Each scenario can run for a long time β€” agents debate, collaborate,
99
+ play games, and push each other β€” while a live telemetry view lets you follow the whole
100
+ run step by step.
101
+
102
  What makes it *super modular*:
103
  - **Config, not code.** Agents, scenarios, casts, model tiers, tool grants, and
104
  budgets are declarative YAML under `config/`, validated by a schema. Add a world
105
  by adding files β€” proven by `tests/test_modularity.py` (zero engine edits).
106
  - **A model per agent.** Each agent declares a logical profile (`tiny`/`fast`/
107
+ `balanced`/`strong`); a `ModelRouter` binds it to a concrete small model served on
108
+ Modal β€” **Nemotron, MiniCPM, Gemma**. Mix a ≀4B worker with a ≀32B judge in one cast.
109
  - **Capability-checked tools.** Agents call tools only if their manifest grants
110
  them β€” the contract that fronts in-process tools today and MCP servers later.
111
  - **Built to run for hours.** The ledger is the checkpoint: `restore()` resumes a
 
286
  # 4. Confirm nothing broke
287
  uv run pytest tests/ -q
288
  ```
289
+
290
+ ---
291
+
292
+ Small models, one shared log, and a clear view of how agents behave in motion.
293
+ **This is Multi-Agent Land.**
src/ui/fishbowl/about.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The About tab β€” a compact, self-contained tour of Multi-Agent Land.
2
+
3
+ What it is, how it works (an HTML/CSS recreation of the agent-ensemble architecture so
4
+ it scales crisply and lives in the CRT theme rather than a flat PNG), and the links a
5
+ judge or visitor needs: the submission video, the GitHub repo, LinkedIn, and the three
6
+ Hugging Face field-notes articles.
7
+
8
+ Pure HTML β€” no state, no callbacks. Everything is scoped under ``.fishbowl`` so the
9
+ theater stylesheet applies; the About-only rules live in the inline ``<style>`` block.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import gradio as gr
15
+
16
+ # ── links ────────────────────────────────────────────────────────────────────────
17
+ # Repo + articles are known; the video and LinkedIn URLs are filled in by the team
18
+ # (left as obvious placeholders so a missing link reads as TODO, never as broken chrome).
19
+ GITHUB_URL = "https://github.com/abducodez/multi-agent-land"
20
+ VIDEO_URL = "https://youtu.be/v8-zR6eTbDM"
21
+ LINKEDIN_URL = "https://www.linkedin.com/in/gharsallah/"
22
+ ARTICLES = [
23
+ ("The overview", "https://huggingface.co/blog/build-small-hackathon/multi-agents-land"),
24
+ (
25
+ "Different scenarios",
26
+ "https://huggingface.co/blog/build-small-hackathon/six-playable-woods-and-a-fishbowl-to-watch-them",
27
+ ),
28
+ ("Architecture", "https://huggingface.co/blog/build-small-hackathon/one-engine-three-costumes"),
29
+ ]
30
+
31
+
32
+ # ── architecture diagram (HTML/CSS recreation of agent_ensemble_architecture.png) ──
33
+
34
+
35
+ def _node(title: str, sub: str, *, tone: str, dashed: bool = False) -> str:
36
+ """One labelled box in the diagram. ``tone`` ∈ {engine, ledger, oversight}."""
37
+ cls = f"ab-node ab-{tone}" + (" ab-dashed" if dashed else "")
38
+ return f'<div class="{cls}"><div class="ab-node-title">{title}</div><div class="ab-node-sub">{sub}</div></div>'
39
+
40
+
41
+ def _architecture_html() -> str:
42
+ """Three columns β€” engine Β· ledger Β· oversight & UI β€” wired by arrow glyphs.
43
+
44
+ Mirrors the source diagram: the Conductor β†’ Context builder β†’ Worker agents β†’ Tools
45
+ pipeline on the left, the append-only Event ledger in the centre, and the Observer /
46
+ Judge oversight feeding the Client UI on the right."""
47
+ engine = (
48
+ _node("Conductor", "scheduler Β· budgets", tone="engine")
49
+ + '<div class="ab-arrow ab-down">↓</div>'
50
+ + _node("Context builder", "per-agent memory", tone="engine")
51
+ + '<div class="ab-arrow ab-down">↓</div>'
52
+ + _node("Worker agents", "small specialist models", tone="engine")
53
+ + '<div class="ab-arrow ab-down">↕</div>'
54
+ + _node("Tools (MCP)", "pluggable via registry", tone="engine")
55
+ + _node("+ register new module", "", tone="engine", dashed=True)
56
+ )
57
+ ledger = _node("Event ledger", "append-only event log", tone="ledger")
58
+ oversight = (
59
+ _node("Client UI", "renders the stream", tone="oversight")
60
+ + '<div class="ab-arrow ab-up">↑</div>'
61
+ + _node("Observer agent", "renders the stream", tone="oversight")
62
+ + _node("Judge / moderator", "promote Β· gate Β· decide", tone="oversight")
63
+ )
64
+ legend = (
65
+ '<div class="ab-legend">'
66
+ '<span class="ab-key ab-engine"></span>engine'
67
+ '<span class="ab-key ab-ledger"></span>ledger'
68
+ '<span class="ab-key ab-oversight"></span>oversight &amp; UI'
69
+ "</div>"
70
+ )
71
+ return (
72
+ '<div class="ab-arch">'
73
+ f'<div class="ab-col ab-col-engine">{engine}</div>'
74
+ '<div class="ab-wire ab-wire-l">β†’</div>'
75
+ f'<div class="ab-col ab-col-ledger">{ledger}</div>'
76
+ '<div class="ab-wire ab-wire-r">↔</div>'
77
+ f'<div class="ab-col ab-col-oversight">{oversight}</div>'
78
+ "</div>" + legend
79
+ )
80
+
81
+
82
+ # ── tab body ───────────────────────────────────────────────────────────────────────
83
+
84
+ _ABOUT_CSS = """
85
+ .fishbowl .ab-wrap { max-width: 1080px; margin: 0 auto; padding: 28px 22px 40px; line-height: 1.65; }
86
+ .fishbowl .ab-hero h1 { font-size: 30px; letter-spacing: 0.04em; color: var(--ink); }
87
+ .fishbowl .ab-hero .ab-tag { color: var(--cyan); font-style: italic; margin-top: 6px; }
88
+ .fishbowl .ab-lede { color: var(--ink-mid); margin: 16px 0 8px; font-size: 15px; }
89
+ .fishbowl .ab-lede b { color: var(--lime); font-weight: 600; }
90
+ .fishbowl .ab-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
91
+ gap: 12px; margin: 18px 0 6px; }
92
+ .fishbowl .ab-card { padding: 14px 16px; }
93
+ .fishbowl .ab-card h4 { color: var(--cyan); font-size: 13px; letter-spacing: 0.06em; margin-bottom: 6px; }
94
+ .fishbowl .ab-card p { color: var(--ink-mid); font-size: 13px; margin: 0; }
95
+
96
+ .fishbowl .ab-section-title { margin: 30px 0 14px; }
97
+
98
+ /* architecture diagram */
99
+ .fishbowl .ab-arch { display: grid; grid-template-columns: 1fr auto 1fr auto 1fr;
100
+ align-items: start; gap: 8px; margin: 6px 0 10px; }
101
+ .fishbowl .ab-col { display: flex; flex-direction: column; gap: 6px; }
102
+ .fishbowl .ab-col-ledger { justify-content: center; align-self: stretch; }
103
+ .fishbowl .ab-node { border: 1.5px solid var(--line); border-radius: var(--r-lg);
104
+ padding: 12px 14px; text-align: center; background: var(--panel); }
105
+ .fishbowl .ab-node-title { font-family: var(--font-display); font-weight: 700; font-size: 14px; }
106
+ .fishbowl .ab-node-sub { font-size: 11px; color: var(--ink-dim); margin-top: 3px; }
107
+ .fishbowl .ab-engine { border-color: var(--teal); background: rgba(43,196,180,0.10); }
108
+ .fishbowl .ab-engine .ab-node-title { color: var(--lime); }
109
+ .fishbowl .ab-ledger { border-color: var(--violet); background: rgba(155,140,255,0.12);
110
+ min-height: 100%; display: flex; flex-direction: column; justify-content: center; }
111
+ .fishbowl .ab-ledger .ab-node-title { color: var(--violet); font-size: 16px; }
112
+ .fishbowl .ab-oversight { border-color: var(--coral); background: rgba(255,143,125,0.10); }
113
+ .fishbowl .ab-oversight .ab-node-title { color: var(--coral); }
114
+ .fishbowl .ab-dashed { border-style: dashed; background: transparent; opacity: 0.7; }
115
+ .fishbowl .ab-dashed .ab-node-title { font-size: 12px; color: var(--ink-dim); }
116
+ .fishbowl .ab-arrow { text-align: center; color: var(--ink-faint); font-size: 16px; line-height: 1; }
117
+ .fishbowl .ab-wire { align-self: center; color: var(--ink-faint); font-size: 22px; padding-top: 40px; }
118
+ .fishbowl .ab-legend { display: flex; align-items: center; justify-content: center; gap: 8px;
119
+ flex-wrap: wrap; margin-top: 14px; color: var(--ink-dim); font-size: 12px; }
120
+ .fishbowl .ab-key { width: 13px; height: 13px; border-radius: 3px; display: inline-block;
121
+ margin-left: 14px; border: 1.5px solid; }
122
+ .fishbowl .ab-key.ab-engine { border-color: var(--teal); background: rgba(43,196,180,0.18); }
123
+ .fishbowl .ab-key.ab-ledger { border-color: var(--violet); background: rgba(155,140,255,0.2); }
124
+ .fishbowl .ab-key.ab-oversight { border-color: var(--coral); background: rgba(255,143,125,0.18); }
125
+
126
+ /* link row */
127
+ .fishbowl .ab-links { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0 4px; }
128
+ .fishbowl a.ab-link { text-decoration: none; }
129
+ .fishbowl .ab-articles { margin: 10px 0 0; padding: 0; list-style: none; }
130
+ .fishbowl .ab-articles li { margin: 6px 0; }
131
+ .fishbowl .ab-articles a { color: var(--cyan); text-decoration: none; }
132
+ .fishbowl .ab-articles a:hover { color: #fff; text-shadow: var(--glow); }
133
+
134
+ @media (max-width: 760px) {
135
+ .fishbowl .ab-arch { grid-template-columns: 1fr; }
136
+ .fishbowl .ab-wire { display: none; }
137
+ }
138
+ """
139
+
140
+
141
+ def _links_html() -> str:
142
+ articles = "".join(
143
+ f'<li>πŸ”— <a href="{url}" target="_blank" rel="noopener">{label}</a></li>' for label, url in ARTICLES
144
+ )
145
+ return f"""
146
+ <div class="fishbowl">
147
+ <div class="ab-links">
148
+ <a class="ab-link" href="{VIDEO_URL}" target="_blank" rel="noopener"><span class="btn primary">β–Ά Submission video</span></a>
149
+ <a class="ab-link" href="{GITHUB_URL}" target="_blank" rel="noopener"><span class="btn"> GitHub</span></a>
150
+ <a class="ab-link" href="{LINKEDIN_URL}" target="_blank" rel="noopener"><span class="btn">in LinkedIn</span></a>
151
+ </div>
152
+ <div class="ab-section-title eyebrow">Field notes &middot; articles</div>
153
+ <ul class="ab-articles">{articles}</ul>
154
+ </div>
155
+ """
156
+
157
+
158
+ def build_about() -> dict:
159
+ """Render the About tab. Pure HTML islands wrapped in the ``.fishbowl`` scope root."""
160
+ gr.HTML(f"<style>{_ABOUT_CSS}</style>")
161
+ gr.HTML(
162
+ """
163
+ <div class="fishbowl"><div class="ab-wrap">
164
+ <div class="ab-hero">
165
+ <h1>&#9673; Multi-Agent Land</h1>
166
+ <div class="ab-tag">small minds &middot; one ledger &middot; &le; 32B</div>
167
+ </div>
168
+ <p class="ab-lede">
169
+ A small-model, multi-agent interactive story engine built on an <b>append-only event
170
+ ledger</b>. Agents never call each other &mdash; they post typed events to a shared log,
171
+ and every view (stage, memory, UI) is a <b>projection</b> of that log. One tiny
172
+ event-sourced engine powers many delightful worlds: a whimsical forest theater, a
173
+ mystery-solving blackboard swarm, a tool-using oracle grove &mdash; not three apps, but
174
+ three YAML configs of the <b>same engine</b>.
175
+ </p>
176
+ <div class="ab-grid">
177
+ <div class="panel ab-card"><h4>Genuinely alive</h4><p>Watch small specialist agents
178
+ write, judge, remember, and render strange interactive scenes in real time.</p></div>
179
+ <div class="panel ab-card"><h4>AI is load-bearing</h4><p>The multi-agent drama is the
180
+ product &mdash; the magic comes from the models, not the scaffolding.</p></div>
181
+ <div class="panel ab-card"><h4>One engine, three costumes</h4><p>Agents bind to models by
182
+ declarative YAML profile; each runs on the small model that fits its job.</p></div>
183
+ </div>
184
+ </div></div>
185
+ """
186
+ )
187
+ gr.HTML(
188
+ f'<div class="fishbowl"><div class="ab-wrap" style="padding-top:0">'
189
+ f'<div class="ab-section-title eyebrow">How it works &middot; the agent ensemble</div>'
190
+ f"{_architecture_html()}</div></div>"
191
+ )
192
+ gr.HTML(f'<div class="ab-wrap" style="padding-top:0">{_links_html()}</div>')
193
+ return {}
src/ui/fishbowl/app.py CHANGED
@@ -49,6 +49,15 @@ except Exception: # pragma: no cover - degrade gracefully if the archive unit i
49
  return "β–Ά (run)"
50
 
51
 
 
 
 
 
 
 
 
 
 
52
  try: # hall of fame: the competitive high-score board (Workstream 6.2 + 6.3)
53
  from src.ui.fishbowl.hall_of_fame import build_hall_of_fame, wire_sessions_render
54
  except Exception: # pragma: no cover - degrade gracefully if the unit is absent
@@ -731,6 +740,8 @@ def build_app() -> gr.Blocks:
731
  build_telemetry()
732
  with gr.Tab("πŸ† Hall of Fame", id="hall"):
733
  hall_handles = build_hall_of_fame()
 
 
734
 
735
  # CRT foreground layers (scanlines + vignette, above content, click-through).
736
  gr.HTML(_CRT_FG_HTML)
 
49
  return "β–Ά (run)"
50
 
51
 
52
+ try: # about: a compact project tour β€” what it is, the architecture, the submission links
53
+ from src.ui.fishbowl.about import build_about
54
+ except Exception: # pragma: no cover - degrade gracefully if the unit is absent
55
+
56
+ def build_about() -> dict:
57
+ gr.HTML('<div class="fishbowl"><div class="fishbowl-placeholder">About β€” coming soon.</div></div>')
58
+ return {}
59
+
60
+
61
  try: # hall of fame: the competitive high-score board (Workstream 6.2 + 6.3)
62
  from src.ui.fishbowl.hall_of_fame import build_hall_of_fame, wire_sessions_render
63
  except Exception: # pragma: no cover - degrade gracefully if the unit is absent
 
740
  build_telemetry()
741
  with gr.Tab("πŸ† Hall of Fame", id="hall"):
742
  hall_handles = build_hall_of_fame()
743
+ with gr.Tab("About", id="about"):
744
+ build_about()
745
 
746
  # CRT foreground layers (scanlines + vignette, above content, click-through).
747
  gr.HTML(_CRT_FG_HTML)