# Project export: Spr0utS0urc4

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: GPU microarchitecture tuning took researcher weeks. Now AI agents do it autonomously running real GPU simulations and explaining every trade-off through an autonomous GPU Architecture Studio.
- Devpost: https://devpost.com/software/spr0uts0urc4
- GitHub: https://github.com/DevMewada1299/gpu-arch-studio
- Demo: https://asi1.ai/chat/3224a73e-3390-4a71-aca0-bd9bfc213ece
- Team: 3 GitHub contributor(s) — Dev Mewada (36 commits), Claude Opus 4.8 (28 commits), Skanda Gonur Nagaraj (1 commits)

## Devpost submission (written by the team)

### Inspiration

Picking the right GPU microarchitecture for a workload is a brutal search problem. With just the knobs we expose SM clusters, cores/cluster, memory channels, scheduler, shared-memory size, L1/L2 cache - the space is already $$4 \times 3 \times 4 \times 3 \times 3 \times 3 \times 4 \times 3 = 15{,}552 \text{ configurations,}$$ and each one means hand-editing a simulator config, queueing a cycle-accurate run, and squinting at a 3,000-line counter dump. Today that loop is done by a handful of specialists over weeks. We wanted to put that expert loop on autopilot and let anyone drive it by just describing a goal.

### What it does

GPU Architecture Studio is an autonomous design-space explorer. You give it a goal ("maximize IPC for the JPEG workload"); a team of AI agents then proposes a config, runs a real GPGPU-Sim simulation, diagnoses the bottleneck, proposes a better config, and repeats converging on a Pareto-optimal design and explaining every step. The agents decide what to run next it's not a UI that explains what you ran. You can drive it from a web studio or chat with it through ASI:One.

### How we built it

Simulation: a FastAPI backend drives GPGPU-Sim (GTX-480/Fermi) in Docker, generates configs, and parses the real counters. Agents: four Claude agents: Memory, Warp, and Bottleneck specialists (Haiku) plus an Orchestrator (Sonnet, adaptive thinking) that reasons over the full history. Memory: RedisVL as agent memory, each experiment is embedded and the Orchestrator semantically recalls relevant past runs before each decision. Reliability: Sentry traces every sim and agent call and captures failures with the offending config. Reach: Fetch.ai uAgents with the Agent Chat Protocol expose it on Agentverse / ASI:One - including a multi-agent bureau where the Orchestrator delegates to the specialist uAgents over Fetch messaging.

### Challenges we ran into

The simulator fought back.The simulator fought back. Scaling clusters segfaulted GPGPU-Sim until we realized the interconnect must be regenerated to match: k = n_clusters + n_mem × 2. We now emit both files together. A silent loop-killer. The Orchestrator returned empty proposals, adaptive thinking was consuming the entire token budget. Diagnosing it (not guessing) and raising max_tokens fixed it. Flaky infrastructure. A Redis drop mid-run once killed a successful experiment; we made persistence best-effort so a datastore blip can never lose a result. Dependency isolation for uAgents (older pinned deps) solved by running Fetch agents in a separate env that talks to the backend over HTTP.

### Accomplishments we're proud of

A loop that genuinely improves the design real simulations climbing IPC 315 → 459 → 490 with the Orchestrator justifying each move; agent analysis sharp enough to pass for a real architect; Redis used beyond caching as vector memory; and the whole system reachable from a chat, with reliability engineering that turns crashes into graceful, monitored failures.

### What we learned

Always test against real simulator output, never assumed formats. Tiered models (cheap specialists, smart orchestrator) keep cost ~$0.25/run. Vector recall turns the agent's growing history from a context-window problem into an advantage. And clean seams (injectable stores, HTTP-decoupled agents) let sponsors layer in without breaking the core. Sponsor Tracks We didn't reach for sponsor tech to check boxes each one solved a problem the autonomous loop genuinely had. The clean seams in our architecture (injectable stores, HTTP-decoupled agents) are exactly what let them slot in without bending the core. Anthropic (Claude) the reasoning itself. Claude is the loop, not a feature on top of it. The diagnosis "L1 miss traffic is low-reuse streaming; highest leverage is more clusters" is a Claude judgment over a 3,000-line counter dump, and the next config is a Claude decision. We leaned into the model lineup on purpose: cheap Haiku specialists (Memory, Warp, Bottleneck) do the per-run analysis, and a Sonnet Orchestrator with extended thinking reasons over the full experiment history to pick the next move. That tiering is what keeps a full exploration at ~$0.25/run instead of being cost-prohibitive. We also built the whole system with Claude Code. Redis Redis beyond caching, as the agent's memory. This was the integration we're most genuinely proud of, because it fixed a real failure mode. As the run grows, the Orchestrator can't fit every past experiment in its context window. Instead of truncating history, we embed each experiment and store it in RedisVL; before every decision the Orchestrator semantically recalls the most relevant prior runs (the "recalled N relevant prior runs" moment in the UI is this firing). That turns a growing history from a context-window liability into a compounding advantage and it's Redis as a vector database, not a key-value cache. Redis also backs the experiment store, made best-effort so a datastore blip can never lose a successful result. Sentry reliability for an unattended autonomous loop. A loop that runs cycle-accurate simulations and LLM calls without a human watching fails in ugly, silent ways and we hit several: GPGPU-Sim segfaulting on cluster scaling, the Orchestrator returning empty proposals when adaptive thinking ate the token budget. Sentry traces every simulation and every agent call and captures failures with the offending config attached, so each crash becomes a reproducible, monitored issue instead of a dead loop we have to guess at. Diagnosing the empty-proposal bug from a captured trace (rather than guessing) is precisely why this earned its place. Fetch.ai reach and real agent-to-agent collaboration. We wrapped the explorer as uAgents speaking the Agent Chat Protocol, so it's reachable on Agentverse / ASI:One you can drive a full design exploration just by chatting with it. Beyond a single entry point, we built a multi-agent bureau where the Orchestrator delegates to the specialist uAgents over Fetch messaging, which is genuine agent-to-agent collaboration rather than one process calling functions. Running the Fetch agents in their own environment that talks to the backend over HTTP also solved a real dependency-isolation problem. https://agentverse.ai/agents/details/agent1qv0wrqka6vhj6enxaurju53ky30hp4qz9hfvp0spuz5s7490lpjexzcv45n/profile https://asi1.ai/chat/3224a73e-3390-4a71-aca0-bd9bfc213ece

### What's next

A more fluid and pluggable system to connect more SIMS for end-to-end hardware analysis not just GPU, multi-container parallel exploration, a richer UI, deeper multi-agent collaboration.

## README (from the GitHub repository)

# 🖥️ GPU Architecture Studio

**AI agents that autonomously design better GPUs — and explain every decision.**

Tell it a goal in plain English ("maximize IPC for the JPEG workload"). A team of
Claude agents then proposes a GPU configuration, runs a **real** cycle-accurate
simulation, diagnoses the bottleneck, proposes a better config, and repeats —
converging on a Pareto-optimal chip design while narrating its reasoning. Drive
it from a web studio, or **just chat with it** through ASI:One.

---

## The problem (and why it matters)

A modern accelerator isn't one design — it's a **huge space of choices**: how many
SM clusters, cores per cluster, cache sizes, the warp scheduler, memory channels,
shared-memory budget. The *right* combination depends entirely on the workload,
and the search space is thousands of configs.

Today, finding the best config for a workload looks like this: a hardware
specialist hand-edits a simulator config, queues a cycle-accurate job on a
cluster (each run is minutes to hours), squints at a 3,000-line dump of
performance counters, forms a hypothesis about the bottleneck, tweaks one
parameter, and repeats — for **days or weeks**. The expertise to read those
counters and know *what to change and why* lives in a handful of PhD-level heads.

**That's the bottleneck we attack.** GPU Architecture Studio puts that expert
loop on autopilot: AI agents decide what to try, run real simulations, read the
real counters, reason about the bottleneck like a senior architect, and converge
on an optimal design — explaining each step. It turns a weeks-long, specialist-only
process into a conversation anyone can have.

> This is **not** a UI that explains what *you* ran. The agents drive the loop —
> they choose the next experiment. That autonomy is the product.

**Who it helps:** hardware/architecture engineers (explore a design space in
minutes, with rationale attached), ML-systems & performance engineers (fit a chip
to a workload without learning the simulator), and researchers/students (a
transparent, teachable loop that *shows* why a design is compute- vs memory-bound).

---

## How it works

```
 ASI:One / Agentverse chat                Web Studio (React)
        │  "best GPU config for JPEG?"          │ sliders + live charts
        ▼                                        ▼
  Fetch.ai uAgents  ──────── HTTP ────────►  FastAPI backend
  (Agent Chat Protocol)                          │
                                                 ▼   autonomous loop
   ┌─────────────────────────────────────────────────────────────────┐
   │  Orchestrator proposes config → run REAL GPGPU-Sim → 3 specialist  │
   │  agents analyze the counters → Orchestrator reasons over the FULL  │
   │  history (+ vector-recalled past runs) → proposes next → repeat    │
   │  until converged on a Pareto-optimal design                        │
   └─────────────────────────────────────────────────────────────────┘
        │                    │                          │
   GPGPU-Sim            Claude agents             RedisVL agent memory
   (Docker, ~8s/run)    Memory · Warp ·           (semantic recall of
                        Bottleneck · Orchestrator   relevant past runs)
```

**The four agents** (Claude): **Memory** (cache/bandwidth/reuse), **Warp**
(occupancy/scheduling/latency-hiding), **Bottleneck** (synthesizes both into a
roofline classification + the highest-leverage change), and **Orchestrator**
(reads the full history, recalls relevant past experiments, proposes the next
config like a senior architect, finds the Pareto frontier).

A real run, verified: `IPC 315 → 459 → 490` as the agents scaled SM clusters then
tuned a second parameter — each step justified by the counters.

---

## Integrations

### Anthropic / Claude — the reasoning
The agents *are* Claude, tiered for cost: **Haiku 4.5** for the three specialists
(fast, focused, run 3× per iteration) and **Sonnet 4.6** with adaptive thinking
for the Orchestrator (the hard, history-spanning reasoning). The result is
analysis a GPU architect would actually write — *"occupancy is 32% yet IPC is 315,
so this kernel is latency-tolerant; chasing occupancy won't pay"* — not generic
filler. One full 8-experiment exploration costs **~$0.25**. Built with Claude Code.

### Redis — *agent memory*, not caching
The Orchestrator is only as good as what it remembers. As exploration grows,
stuffing the entire history into the prompt is expensive and noisy. So we use
**RedisVL** as the agent's **long-term memory**: every experiment (config + stats
+ bottleneck classification) is embedded with a local sentence-transformer into a
**vector index**, and before each decision the Orchestrator **semantically recalls
the most relevant past experiments** — RAG over the agent's *own experience*. It
reasons like an architect who remembers *"we've been in a shared-memory-bound
regime like this before; here's what worked."*

**How it helped us:** it turned a context-window problem into a feature. The
Orchestrator gets sharper as it accumulates experience, recall works **across
sessions**, and Redis Cloud durably stores every experiment. This is Redis used
*beyond caching* — vector search + context retrieval + agent memory.

### Sentry — reliability for a system *you don't drive*
When the **AI** decides what to run, you can't eyeball whether it stayed healthy.
We instrument **every simulation and every agent call as a Sentry transaction**,
tagged with the config and resulting IPC — a live, queryable map of what the
autonomous loop did and where time/cost went. And AI-proposed configs *can break
the simulator*: scaling SM clusters without resizing the interconnect **segfaults
GPGPU-Sim**. Sentry captures those crashes **with the offending config attached**,
and the runner returns an error result instead of dying.

**How it helped us:** mid-build, a flaky Redis connection dropped during a run and
killed a successful experiment. Sentry surfaced it immediately — so we made
persistence best-effort (a datastore blip can never lose a run) and captured the
blip instead. We literally used Sentry to **find and fix a real reliability bug.**

### Fetch.ai / ASI:One — a product anyone can talk to
A research tool shouldn't need a custom UI to be useful. We wrapped the agents as
**Fetch.ai uAgents** implementing the **Agent Chat Protocol**, registered on
**Agentverse** and reachable from **ASI:One** — chat *"find the best GPU config
for JPEG"* and the autonomous system runs and answers. And it's a **true
multi-agent system**: an **Orchestrator uAgent delegates to Memory/Warp/Bottleneck
specialist uAgents over Fetch messaging**, then synthesizes their verdicts.

**How it helped us:** it turned a backend tool into a conversational product
reachable by anyone, and satisfied "complete the workflow with no custom frontend."

---

## 🚀 Build & run

### Prerequisites
Python 3.9+ (backend). Docker with the GPGPU-Sim container for *real* sims
(optional — see DEMO_MODE). `ANTHROPIC_API_KEY` for live agents; optional
`REDIS_URL`, `SENTRY_DSN`.

```bash
python -m venv venv && source venv/bin/activate
pip install -r backend/requirements.txt
cp .env.example .env            # add ANTHROPIC_API_KEY (+ optional REDIS_URL, SENTRY_DSN)
uvicorn backend.main:app --port 8000
```
On startup it logs the store (`Redis`/in-memory) and Sentry (`enabled`/`disabled`).

### Run with NO Docker (DEMO_MODE — frontend dev & quick demos)
```bash
DEMO_MODE=1 DISABLE_REDIS=1 uvicorn backend.main:app --port 8000
```
Serves the **real API** with replayed simulator data — zero Docker/Redis. With
`ANTHROPIC_API_KEY` unset, agents return canned config-aware analysis so
`/explore` runs end-to-end offline; set the key for live Claude.

### Try the autonomous loop
```bash
curl -N -X POST localhost:8000/explore -H 'Content-Type: application/json' \
  -d '{"goal":"maximize IPC for the JPEG workload","constraints":{"max_n_clusters":30}}'
# → {session_id}; then: curl -N localhost:8000/explo

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 73 recognized source files, 346 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Docker (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers

## Codebase structure (from repository index)

### Files (108 of 108)

```
.DS_Store
.gitignore
agents/bottleneck_agent.md
agents/memory_agent.md
agents/orchestrator.md
agents/warp_agent.md
backend/__init__.py
backend/agent_engine.py
backend/agent_memory.py
backend/config_generator.py
backend/docker_manager.py
backend/explore.py
backend/fetch_agents.py
backend/fetch_bureau.py
backend/main.py
backend/models.py
backend/monitoring.py
backend/redis_store.py
backend/report_parser.py
backend/requirements.txt
backend/run_test.py
backend/runner.py
backend/stats_parser.py
backend/store.py
backend/templates/config_fermi_islip.icnt
backend/templates/gpgpusim.config
CLAUDE.md
docs/AGENT_CORE_PLAN.md
docs/API_FOR_FRONTEND.md
docs/BACKEND_PLAN.md
docs/FRONTEND_PLAN.md
docs/MASTER_PLAN.md
docs/sample_report.json
frontend/.gitignore
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/postcss.config.js
frontend/README.md
frontend/src/App.css
frontend/src/App.tsx
frontend/src/components/AgentCard.tsx
frontend/src/components/AgentPanel.tsx
frontend/src/components/CompareModal.tsx
frontend/src/components/ConfigPanel.tsx
frontend/src/components/ContainerSelector.tsx
frontend/src/components/DeepDive.tsx
frontend/src/components/ExperimentHistory.tsx
frontend/src/components/PerformanceDashboard.tsx
frontend/src/components/Segmented.tsx
frontend/src/components/SegmentedWithOther.tsx
frontend/src/constants.ts
frontend/src/index.css
frontend/src/lib/api.ts
frontend/src/lib/exploreStream.ts
frontend/src/main.tsx
frontend/src/mocks.ts
frontend/src/sampleReport.json
frontend/src/types.ts
frontend/tailwind.config.js
frontend/tests/driver-agents-final.mjs
frontend/tests/driver-agents.mjs
frontend/tests/driver-compare.mjs
frontend/tests/driver-compare2.mjs
frontend/tests/driver-containers.mjs
frontend/tests/driver-deepdive.mjs
frontend/tests/driver-drawer.mjs
frontend/tests/driver-explore-live.mjs
frontend/tests/driver-header-full.mjs
frontend/tests/driver-history-live.mjs
frontend/tests/driver-home-explore.mjs
frontend/tests/driver-homelayout.mjs
frontend/tests/driver-integration.mjs
frontend/tests/driver-logic-check.mjs
frontend/tests/driver-logo.mjs
frontend/tests/driver-orch.mjs
frontend/tests/driver-otherfields.mjs
frontend/tests/driver-redesign.mjs
frontend/tests/driver-regressions.mjs
frontend/tests/driver-rerun.mjs
frontend/tests/driver-resize.mjs
frontend/tests/driver.mjs
frontend/tsconfig.app.json
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/vite.config.ts
README.md
sample/out.txt
tests/agent_engine/test_analyze.py
tests/agent_memory/test_agent_memory.py
tests/api/test_api_e2e.py
tests/api/test_explore_api.py
tests/config_generator/test_generate_config.py
tests/conftest.py
tests/docker_manager/test_exec_basic.py
tests/docker_manager/test_get_containers.py
tests/docker_manager/test_run_benchmark.py
tests/explore/debug_orchestrator.py
tests/explore/test_explore_live.py
tests/explore/test_explore.py
tests/fetch/test_bureau_local.py
tests/monitoring/test_failure_capture.py
tests/monitoring/test_sentry_smoke.py
tests/README.md
tests/redis_store/test_redis_store.py
tests/report_parser/test_parse_report.py
tests/runner/test_run_experiment.py
tests/stats_parser/test_parse_stats.py
```

### Dependencies

- backend/requirements.txt: anthropic@>=0.40, docker@>=7.0, fastapi@>=0.110, hiredis@>=2.0, python-dotenv@>=1.0, redis@>=5.0, redisvl@>=0.3, sentence-transformers@>=2.2, sentry-sdk[fastapi]@>=2.0, uvicorn[standard]@>=0.29
- frontend/package.json: @eslint/js@^10.0.1, @types/node@^24.12.3, @types/react@^19.2.14, @types/react-dom@^19.2.3, @vitejs/plugin-react@^6.0.1, autoprefixer@^10.5.0, eslint@^10.3.0, eslint-plugin-react-hooks@^7.1.1, eslint-plugin-react-refresh@^0.5.2, globals@^17.6.0, lucide-react@^1.21.0, postcss@^8.5.15, react@^19.2.6, react-dom@^19.2.6, recharts@^3.8.1, tailwindcss@^3.4.19, typescript@~6.0.2, typescript-eslint@^8.59.2, vite@^8.0.12

### Recent commits (newest first)

- adding AgentPanel
- changing configs
- erge branch 'frontend' of https://github.com/DevMewada1299/gpu-arch-studio into frontend
- merge: demo polish (clean agent text + realistic DEMO_MODE IPC)
- merge: demo polish (clean agent text + realistic DEMO_MODE IPC)
- demo polish: clean agent text (strip markdown) + realistic DEMO_MODE IPC scaling
- Merge Redis read-path resilience
- redis: make ALL store ops resilient (no 500s on a flaky connection)
- Merge team frontend + CORS dev fix
- frontend: integrate team's React studio + allow Vite port in CORS
- feat(frontend): live backend integration (run + explore SSE, real data)
- feat(frontend): live backend integration (run + explore SSE, real data)
- Merge stronger README
- docs: stronger README — problem story + sponsor narratives + Fetch submission
- Merge verified live bureau + Sentry startup log
- fetch.ai: live multi-agent bureau (verified) + Sentry startup log
- Merge remote-tracking branch 'origin/main' into frontend
- Merge multi-agent Fetch bureau (bonus) into main
- fetch.ai: multi-agent Bureau (BONUS — agent-to-agent collaboration)
- feat(frontend): two-hero Home layout (Deep Dive + Agents) + left config drawer

## Key source files (fetched from GitHub, selected and truncated for size)

### CLAUDE.md

```markdown
# GPU Architecture Studio

> Shared context for the whole team. Claude Code reads this automatically.
> Keep the "What's Working" section updated as you build.

## What This Is

An **agentic GPU microarchitecture design space exploration tool**.

GPGPU-Sim runs inside Docker containers. Claude agents autonomously run
simulation experiments, analyze the performance results, and propose better
GPU configurations. The user watches the agents iterate toward an optimal
design for a given workload — and can intervene, add constraints, or override.

**The core differentiator:** this is NOT a UI wrapper that explains what the
user ran. The agents decide what to run next. They propose a config, run it,
read the results, reason about the bottleneck, and propose the next config —
converging on a Pareto-optimal design across experiments.

## The One-Sentence Pitch

"GPU architecture exploration used to take a PhD student weeks of manual
config editing and cluster queueing. We built an interface where AI agents
do the exploration autonomously and explain every decision."

## Stack

| Layer | Technology |
|-------|-----------|
| Frontend | React + TypeScript + Tailwind + Recharts |
| Backend | Python FastAPI |
| Simulation | GPGPU-Sim inside Docker (already set up) |
| Docker control | docker-py (Python Docker SDK) |
| Agents | Anthropic Claude API (streaming) |
| Storage | Redis (experiment results + agent memory) |
| Delivery | Web app — open in browser at localhost:3000 |

> NOTE: We are using the WEB version (browser), not Electron. Backend runs
> on localhost:8000, frontend on localhost:3000. Simpler, faster to build.

## Tracks We're Targeting

- **Primary:** Ddoski's Lab Track (hardware, engineering, scientific tools)
- **Anthropic prize:** built with Claude Code, tackles a meaningful technical problem
- **Sponsor prizes:** Redis (experiment store + agent memory), Sentry
  (simulation run reliability monitoring), The Token Company (depth of research)

## Docker Setup Conventions

- GPGPU-Sim containers are labeled: `gpgpu-sim=true`
- Shared volume: `./experiments` on host → `/experiments` inside each container
- Each experiment writes config to `/experiments/{exp_id}/gpgpusim.config`
- Simulation output goes to `/experiments/{exp_id}/output.log`
- Container discovery: list running containers filtered by the label
- Multiple containers = parallel experiments (one experiment per container)

## GPU Config Parameters (REAL — from our actual working config)

Baseline GPU: **GTX 480, Fermi, compute capability 2.0**. The benchmark runs
in ~8 seconds, so live simulation during the demo is viable (no fake demo mode
strictly required, though keep it as backup).

These are the ACTUAL tunable lines in our `gpgpusim.config`:

| UI param | config key | baseline | slider values |
|----------|-----------|----------|---------------|
| SM Clusters | `-gpgpu_n_clusters` | 15 | 8, 15, 30, 60 |
| Cores/cluster | `-gpgpu_n_cores_per_cluster` | 1 | 1, 2, 4 |
| Memory Controll
[truncated — 8643 more characters]
```

### agents/memory_agent.md

```markdown
You are the **Memory Agent** in an autonomous GPU microarchitecture exploration
system. You analyze the memory hierarchy of a GTX-480-class (Fermi) GPU running
the **DCT8x8 / JPEG** workload in GPGPU-Sim.

You receive the current config and one experiment's stats:
- l1_hit_rate, l2_hit_rate, l1i_hit_rate (fractions 0-1)
- dram_stalls (gpu_stall_dramfull cycles), l2_bw (GB/s)
- ipc, occupancy, and the config (clusters, cores, n_mem, shmem_size, L1/L2 sets)

Reason about the memory system specifically:
- **Working set vs cache.** What does the L1/L2 hit rate imply about whether the
  DCT 8x8-block working set fits in cache? DCT is shared-memory heavy, so global
  L1 traffic is often low-reuse streaming — more L1 may NOT help.
- **Bandwidth pressure.** Are DRAM stalls high relative to total cycles? Is L2
  bandwidth saturated? If stalls are low, bandwidth is not the limiter — say so.
- **What would actually help.** More L2 sets? More memory controllers (n_mem)?
  Or is reuse already captured and more cache is wasted area?

RULES:
- Be SPECIFIC. Quote the actual numbers and what they imply. Never generic.
- Bad: "L2 hit rate is decent." Good: "L2 hit 51% with only 532 DRAM-full stall
  cycles means bandwidth isn't the bottleneck — adding memory channels won't move
  IPC; the L1 miss traffic is low-reuse streaming, so larger L1 is wasted area."
- 2-3 sentences of analysis. Then a final line EXACTLY: `STATUS: GREEN` (memory
  system healthy / not the limiter), `STATUS: AMBER` (some pressure, watch it),
  or `STATUS: RED` (memory is the dominant bottleneck).

```

### backend/requirements.txt

```
# Backend dependencies
docker>=7.0
fastapi>=0.110
uvicorn[standard]>=0.29
redis>=5.0
hiredis>=2.0
python-dotenv>=1.0

# Agents
anthropic>=0.40

# RedisVL agent memory (the Redis "beyond caching" prize path).
# sentence-transformers downloads a ~90MB model on first use.
redisvl>=0.3
sentence-transformers>=2.2

# Optional / sponsor integrations (safe to install; code degrades without config)
sentry-sdk[fastapi]>=2.0

# Fetch.ai uAgent wrapper (backend/fetch_agents.py). Runs as a SEPARATE process
# and talks to the backend over HTTP, so its pinned deps (older pydantic) don't
# affect the backend. Ideally install in a dedicated venv.
#   pip install uagents

```

### frontend/package.json

```
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "lucide-react": "^1.21.0",
    "react": "^19.2.6",
    "react-dom": "^19.2.6",
    "recharts": "^3.8.1"
  },
  "devDependencies": {
    "@eslint/js": "^10.0.1",
    "@types/node": "^24.12.3",
    "@types/react": "^19.2.14",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^6.0.1",
    "autoprefixer": "^10.5.0",
    "eslint": "^10.3.0",
    "eslint-plugin-react-hooks": "^7.1.1",
    "eslint-plugin-react-refresh": "^0.5.2",
    "globals": "^17.6.0",
    "postcss": "^8.5.15",
    "tailwindcss": "^3.4.19",
    "typescript": "~6.0.2",
    "typescript-eslint": "^8.59.2",
    "vite": "^8.0.12"
  }
}

```

### backend/main.py

```python
"""FastAPI app — exposes the simulation pipeline over the API contract.

Endpoints (see CLAUDE.md):
    GET  /containers
    POST /experiments/run            -> {exp_id}; runs in the background
    GET  /experiments/{id}/stream    -> SSE: {type:output,line} ... {type:complete,stats}
    GET  /experiments/history
    GET  /experiments/{id}
    POST /explore                    -> 501 until the agent core lands

Storage is chosen at startup: RedisExperimentStore if REDIS_URL is set and
reachable, else the in-memory store (real data, not persisted). Sentry is
initialized if SENTRY_DSN is set (no-op otherwise).

Run it:  uvicorn backend.main:app --reload --port 8000
"""

import asyncio
import json
import os
from typing import Dict, List, Optional

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel

from . import docker_manager, monitoring
from .explore import explore
from .models import GPUConfig
from .runner import BENCHMARKS, DEFAULT_BENCHMARK, run_experiment
from .store import InMemoryExperimentStore

# Load .env (REDIS_URL, SENTRY_DSN, ...) so `uvicorn backend.main:app` just works
# without manually exporting. Harmless if python-dotenv isn't installed.
try:
    from dotenv import load_dotenv

    load_dotenv()
except ImportError:
    pass

_sentry_on = monitoring.init_sentry()  # no-op unless SENTRY_DSN is set
print(f"[sentry] {'enabled — sim + agent calls traced in Performance' if _sentry_on else 'disabled (set SENTRY_DSN to enable)'}")


def _make_store():
    if os.environ.get("DISABLE_REDIS") == "1":
        print("[store] DISABLE_REDIS=1 -> in-memory store")
        return InMemoryExperimentStore()
    url = os.environ.get("REDIS_URL")
    if url:
        try:
            from .redis_store import RedisExperimentStore

            store = RedisExperimentStore(url)
            store.ping()
            print("[store] using Redis")
            return store
        except Exception as exc:  # noqa: BLE001
            print(f"[store] Redis unavailable ({exc}); falling back to in-memory")
    print("[store] using in-memory store")
    return InMemoryExperimentStore()


STORE = _make_store()

from .agent_memory import make_agent_memory

AGENT_MEMORY = make_agent_memory()

app = FastAPI(title="GPU Architecture Studio API")
app.add_middleware(
    CORSMiddleware,
    # Allow any localhost port for dev (Vite uses 5173, others use 3000).
    allow_origin_regex=r"http://(localhost|127\.0\.0\.1):\d+",
    allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
    allow_methods=["*"],
    allow_headers=["*"],
)


# --- request models -------------------------------------------------------

class GPUConfigIn(BaseModel):
    n_clusters: int = 15
    cores_per_cluster: int = 1
    n_mem: int = 6
    shmem_size: int = 49152
    scheduler: str = "gto"
    num_sched_per_core: int = 2
    l1_sets: int = 32
    l2_sets: int = 64


class RunRequest(BaseModel):
    config: GPUConfigIn
    benchmark: str = DEFAULT_BENCHMARK
    container_id: Optional[str] = None


# --- in-process run registry (for SSE) ------------------------------------

class RunHandle:
    """Buffers a run's SSE events so late subscribers still see everything."""

    def __init__(self, container_id: Optional[str]):
        self.events: List[dict] = []
        self.done = False
        self.container_id = container_id


RUNS: Dict[str, RunHandle] = {}


async def _run_job(exp_id: str, config: GPUConfig, benchmark: str, container_id):
    handle = RUNS[exp_id]
    loop = asyncio.get_running_loop()

    def on_line(line: str):
        # called from the worker thread -> hop back to the loop thread to append
        loop.call_soon_threadsafe(
            handle.events.append, {"type": "output", "line": line}
        )

    try:
        exp = await asyncio.to_thread(
            run_experiment,
            config,
            benchmark,
            container_id,
            STORE,
            True,        # save_artifacts
            exp_id,      # exp_id
            on_line,     # on_line
        )
        handle.events.append(
            {
                "type": "complete",
                "exp_id": exp_id,
                "status": exp.status,
                "error": exp.error,
                "config": exp.config.to_dict(),
                "stats": exp.stats.to_dict(),
            }
        )
    except Exception as exc:  # noqa: BLE001
        monitoring.capture_exception(exc, exp_id=exp_id)
        handle.events.append({"type": "error", "message": str(exc)})
    finally:
        handle.done = True


# --- endpoints ------------------------------------------------------------

@app.get("/health")
def health():
    return {"ok": True, "benchmarks": list(BENCHMARKS)}


@app.get("/containers")
def containers():
    busy = {h.container_id for h in RUNS.values() if not h.done and h.container_id}
    out = []
    for c in docker_manager.get_containers():
        out.append({**c, "busy": c["name"] in busy or c["id"] in busy})
    return out


@app.post("/experiments/run")
async def experiments_run(req: RunRequest):
    if req.benchmark not in BENCHMARKS:
        raise HTTPException(400, f"unknown benchmark {req.benchmark!r}")
    exp_id = os.urandom(4).hex()
    config = GPUConfig.from_dict(req.config.model_dump())
    RUNS[exp_id] = RunHandle(req.container_id)
    asyncio.create_task(_run_job(exp_id, config, req.benchmark, req.container_id))
    return {"exp_id": exp_id}


def _sse_response(handle: "RunHandle") -> StreamingResponse:
    """SSE from a buffered handle — replays all events then tails new ones.
    Late subscribers still see everything (events are buffered, not consumed)."""

    async def gen():
        i = 0
        while True:
            while i < len(handle.events):
                yield f"data: {json.dumps(handle.events[i])}\n\n"
                i += 1
            if handle.done:
           
[truncated — 5664 more characters]
```

### frontend/src/main.tsx

```typescript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

### frontend/src/App.tsx

```typescript
import { useEffect, useRef, useState } from 'react'
import { Play, Sparkles, History, X, SlidersHorizontal, Microscope, Loader2, CheckCircle2, AlertTriangle } from 'lucide-react'
import './App.css'
import ConfigPanel from './components/ConfigPanel'
import PerformanceDashboard from './components/PerformanceDashboard'
import AgentPanel from './components/AgentPanel'
import ExperimentHistory from './components/ExperimentHistory'
import ContainerSelector from './components/ContainerSelector'
import { DeepDiveContent } from './components/DeepDive'
import { api } from './lib/api'
import type { GPUConfig, SimStats, SimReport, Experiment, Container } from './types'
import { baselineConfig, mockContainers, mockHistory, mockReport } from './mocks'

type RunStatus = 'idle' | 'running' | 'done' | 'error'

export default function App() {
  const [config, setConfig] = useState<GPUConfig>(baselineConfig)
  const [benchmark, setBenchmark] = useState<string>("dct8x8")
  const [benchmarks, setBenchmarks] = useState<string[]>(["dct8x8"])
  const [goal, setGoal] = useState("Maximize IPC")
  const [exploreRunId, setExploreRunId] = useState(0)

  // Live backend data (with graceful fallback to mocks when offline).
  const [containerList, setContainerList] = useState<Container[]>(mockContainers)
  const [containers, setContainers] = useState<string[]>([])
  const [history, setHistory] = useState<Experiment[]>([])

  // Current experiment driving the dashboard + Home deep-dive.
  const [currentStats, setCurrentStats] = useState<SimStats | null>(null)
  const [homeReport, setHomeReport] = useState<SimReport | null>(null)

  // Manual-run streaming state.
  const [runStatus, setRunStatus] = useState<RunStatus>('idle')
  const [runLine, setRunLine] = useState("")
  const [runError, setRunError] = useState<string | null>(null)
  const [runIpc, setRunIpc] = useState<number | null>(null)
  const runHandle = useRef<{ cancel: () => void } | null>(null)

  // Presentational drawers.
  const [historyOpen, setHistoryOpen] = useState(false)
  const [configOpen, setConfigOpen] = useState(false)

  const refreshHistory = () => {
    api.history().then(setHistory).catch(() => {})
  }

  const loadDetails = (expId: string) => {
    api.details(expId).then(setHomeReport).catch(() => {})
  }

  // ── initial load: health (benchmarks), containers, history ──────────────
  // All setState happens inside async callbacks (allowed in effects).
  useEffect(() => {
    api.health()
      .then((h) => h.benchmarks?.length && setBenchmarks(h.benchmarks))
      .catch(() => {})
    const applyContainers = (list: Container[]) => {
      const cs = list.length ? list : mockContainers
      setContainerList(cs)
      setContainers(cs.filter((c) => !c.busy).map((c) => c.id))
    }
    api.containers()
      .then(applyContainers)
      .catch(() => applyContainers([])) // /containers 500s without Docker → mock fallback
    api.history().then(setHistory).catch(() => {})
  }, [])

  // Auto-dismiss the run toast after it settles.
  useEffect(() => {
    if (runStatus === 'done' || runStatus === 'error') {
      const id = setTimeout(() => setRunStatus('idle'), 5000)
      return () => clearTimeout(id)
    }
  }, [runStatus])

  // ── manual run: POST /experiments/run → EventSource stream ──────────────
  const handleRunExperiment = () => {
    setConfigOpen(false)
    setRunStatus('running')
    setRunLine("starting simulation…")
    setRunError(null)
    setRunIpc(null)
    runHandle.current?.cancel()

    api.run({ config, benchmark })
      .then(({ exp_id }) => {
        runHandle.current = api.streamRun(exp_id, (e) => {
          if (e.type === 'output') {
            setRunLine(e.line)
          } else if (e.type === 'complete') {
            if (e.status === 'success') {
              setCurrentStats(e.stats)
              setRunIpc(e.stats.ipc)
              setRunStatus('done')
              loadDetails(e.exp_id)
            } else {
              setRunStatus('error')
              setRunError(e.error ?? 'simulation failed')
            }
            refreshHistory()
          } else if (e.type === 'error') {
            setRunStatus('error')
            setRunError(e.message)
          }
        })
      })
      .catch((err: unknown) => {
        setRunStatus('error')
        setRunError(err instanceof Error ? err.message : String(err))
      })
  }

  // ── autonomous exploration ──────────────────────────────────────────────
  const handleExplore = () => setExploreRunId((id) => id + 1)

  // During exploration, each finished experiment updates the dashboard + deep-dive live.
  const handleExploreExperiment = (expId: string, stats: SimStats) => {
    setCurrentStats(stats)
    loadDetails(expId)
  }

  // ── derived display data ────────────────────────────────────────────────
  const successStats = history.filter((e) => e.status === 'success').map((e) => e.stats)
  const dashboardHistory = successStats.length ? successStats : undefined
  const historyForDrawer = history.length ? history : mockHistory
  const reportForDeepDive = homeReport ?? mockReport

  return (
    <div className="h-screen flex flex-col bg-neutral-50 text-neutral-900 overflow-hidden">

      {/* ── Header ─────────────────────────────────────────────────────── */}
      <header className="flex-none flex items-center justify-between px-6 h-16 border-b border-neutral-200/80 bg-white/80 backdrop-blur-sm">
        <div className="flex items-center gap-3">
          {/* Logo mark — inline gradient + inline SVG so it always renders. */}
          <div
            className="w-9 h-9 rounded-xl flex items-center justify-center shadow-sm"
            style={{
              background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)",
              boxShadow: "0 2px 8px rgba(99,102,241,0.30)",
            }}
          >
            <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="#ffffff" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" 
[truncated — 9846 more characters]
```

### frontend/postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### frontend/vite.config.ts

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### tests/conftest.py

```python
"""Pytest bootstrap: put the repo root on sys.path so `import backend...` works."""
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))

```

[61 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]