# Project export: Burn

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: TreeHacks 2026
- Tagline: Build, Tweak, Deploy.
- Devpost: https://devpost.com/software/burn-awl3rx
- GitHub: https://github.com/aryan-cs/burn
- Video: https://www.youtube.com/embed/ZQpftjOtru4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Raj Pandya (20 commits), Yax Patel (15 commits), aryan-cs (10 commits), ben (5 commits)

## Devpost submission (written by the team)

### Inspiration

At the age of nine years old, I taught myself to code. My school’s librarian introduced me to Scratch, an open-source platform that inevitably resulted in my journey in computer science. While platforms like Neetcode, Scratch, and Khan Academy teach the fundamentals of computer science, they don’t cover the details necessary to understand the workings of machine learning algorithms. We remedied this.

### What it does

Burn allows users to build, train, test, and deploy custom machine learning models regardless of their level of experience. With tooling intuitive to both beginners and experienced engineers, anyone can use Burn to create custom (convolutional) neural networks, transformer-based models, or regression & classification models. Burn simplifies the model development process using a 3-dimensional sandbox experience, allowing users to physically engage with the systems they design. With support for both local (Mac, PC, DGX Spark, Jetson Nano, etc) and cloud (Modal) services, we enable any user to work in machine learning. Additionally, we allow the user to immerse themself into the learning process using a Cosmos-based (Vision Language Model) Tutor to explain exactly what’s going on in the development process.

### How we built it

Connecting our Python (PyTorch, Scikit-learn, etc) backend with our React (TypeScript) frontend was Fast API. We served endpoints for the two to communicate, creating a seamless machine learning research & engineering experience with production-grade UI (inspired by Unity!).

### Challenges we ran into

One major issue we encountered was leveraging Edge AI. After deploying our workloads to the Jetson Orin Nano, we found that even with its integrated GPU, it did not perform as well as running the workloads on Apple Silicon. This resulted in significant time loss, and although we completed the workflow for the Jetson, it was never deployed to production. Instead, we focused our efforts on the DGX Spark, which is compatible with all the models on Burn.

### Accomplishments we're proud of

Initially, we planned to develop a completely different project. At first, our interested was in distributed inferring. However, we realized pretty early into the hackathon the underlying flaw in network-distributed learning as a whole and quickly pivoted to Burn. Our quick thinking allowed us to have enough time to still create an end-to-end, Tree Hacks-ready submission.

### What we learned

Since our group consists of members with vastly different levels of experience, each of us learned something different from this project. One member, who is relatively new to working in a team-based software environment, learned the importance of communication and proper version control. Two members, who have more experience in traditional software engineering, primarily learned about machine learning, how it is developed, and the technology behind it. The final member, a machine learning engineer, gained a deeper understanding of the connection between front-end and back-end systems, as well as Three.js rendering.

### What's next

We hope to continue Burn as a long-term open-source project to enable future generations to learn about artificial intelligence and machine learning, similar to platforms such as Khan Academy and Scratch. Authors Aryan Gupta Yax Patel Raj Pandya Benjamin Mujkic And a big shoutout to the organizers of Tree Hacks for having us for this great experience!

## README (from the GitHub repository)

# Burn

*Build, Tweak, Deploy.*

Burn enables people to create custom machine learning models using an intuitive sandbox UI. With visualizations to showcase concepts such as forward pass, gradient descent, backpropogation, and more in realtime, Burn helps users build an intuitive understanding of what's really happening under the hood. We currently support various models and algorithms such as Neural Networks (including AlexNet and an MNIST Database Classifier), Random Forest, Vision Language Models, and more.

See more on [Devpost](https://devpost.com/software/burn-awl3rx).

Latest: Burn is now [Gradient](https://github.com/learnwithgradient/gradient).


## Detected evidence (automated analysis)

Indexed codebase: 160 recognized source files, 1284 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- Hugging Face (technology) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- PyTorch (technology) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 173)

```
.gitignore
backend/.gitignore
backend/.python-version
backend/core/__init__.py
backend/core/compute_node_client.py
backend/core/deployment_registry.py
backend/core/graph_compiler.py
backend/core/job_registry.py
backend/core/job_storage.py
backend/core/ml_job_registry.py
backend/core/ml_training_engine.py
backend/core/rf_compiler.py
backend/core/rf_job_registry.py
backend/core/rf_shape_inference.py
backend/core/rf_training_engine.py
backend/core/shape_inference.py
backend/core/training_engine.py
backend/core/vlm_architecture.py
backend/core/vlm_registry.py
backend/core/vlm_runtime.py
backend/core/vlm_training_engine.py
backend/core/weight_extractor.py
backend/datasets/__init__.py
backend/datasets/loader.py
backend/datasets/ml_loader.py
backend/datasets/ml_registry.py
backend/datasets/registry.py
backend/datasets/rf_loader.py
backend/datasets/rf_registry.py
backend/main.py
backend/modal_sandbox_infer_server.py
backend/modal_worker.py
backend/models/__init__.py
backend/models/graph_schema.py
backend/models/ml_schema.py
backend/models/rf_graph_schema.py
backend/models/rf_training_config.py
backend/models/training_config.py
backend/pyproject.toml
backend/README.md
backend/routers/__init__.py
backend/routers/ai_coach.py
backend/routers/datasets.py
backend/routers/deploy.py
backend/routers/ml_model.py
backend/routers/ml_websocket.py
backend/routers/model.py
backend/routers/rf_datasets.py
backend/routers/rf_model.py
backend/routers/rf_websocket.py
backend/routers/vlm_model.py
backend/routers/vlm_websocket.py
backend/routers/websocket.py
backend/scripts/use_api.py
backend/scripts/watch_training_ws.py
backend/tests/__init__.py
backend/tests/conftest.py
backend/tests/test_ai_coach_audio.py
backend/tests/test_ai_coach_failover.py
backend/tests/test_api.py
backend/tests/test_datasets_loader.py
backend/tests/test_deploy_api.py
backend/tests/test_deployment_registry.py
backend/tests/test_graph_compiler.py
backend/tests/test_rf_api.py
backend/tests/test_rf_compiler.py
backend/tests/test_rf_shape_inference.py
backend/tests/test_rf_websocket.py
backend/tests/test_shape_inference.py
backend/tests/test_training_config.py
backend/tests/test_training_engine_modal.py
backend/tests/test_websocket.py
backend/uv.lock
compute_node/.env.example
compute_node/main.py
compute_node/pyproject.toml
compute_node/README.md
compute_node/vlm_runtime.py
frontend/.gitignore
frontend/deno.lock
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/README.md
frontend/src/App.tsx
frontend/src/canvas/animation/trainingPulse.ts
frontend/src/canvas/controls/CameraRig.tsx
frontend/src/canvas/controls/DragControls.tsx
frontend/src/canvas/edges/Connection.tsx
frontend/src/canvas/edges/ConnectionPreview.tsx
frontend/src/canvas/edges/WeightVisual.tsx
frontend/src/canvas/nodes/LayerNode.tsx
frontend/src/canvas/nodes/PortMesh.tsx
frontend/src/canvas/SceneManager.tsx
frontend/src/canvas/Viewport.tsx
frontend/src/deployments/deployments.css
frontend/src/deployments/DeploymentsPage.tsx
frontend/src/hooks/useConnectionDraw.ts
frontend/src/hooks/useDragToCanvas.ts
frontend/src/hooks/useMlWebSocket.ts
frontend/src/hooks/useWebSocket.ts
frontend/src/index.css
frontend/src/launch/landing.css
frontend/src/launch/LandingPage.tsx
frontend/src/launch/modelHub.css
frontend/src/launch/ModelHubPage.tsx
frontend/src/launch/WaveBackground.tsx
frontend/src/linreg/datasets.ts
frontend/src/linreg/LinearRegressionBootstrapPage.tsx
frontend/src/linreg/LinearRegressionPage.tsx
frontend/src/linreg/linreg.css
frontend/src/main.tsx
frontend/src/nn/NNBootstrapPage.tsx
frontend/src/rf/canvas/controls/RfDragControls.tsx
frontend/src/rf/canvas/edges/RfConnection.tsx
frontend/src/rf/canvas/nodes/RfNode3D.tsx
frontend/src/rf/canvas/nodes/RfPort.tsx
frontend/src/rf/canvas/RfSceneManager.tsx
frontend/src/rf/canvas/RfViewport.tsx
frontend/src/rf/components/RfChartsPanel.tsx
[53 more files omitted for size]
```

### Dependencies

- backend/pyproject.toml: fastapi@>=0.116.0, httpx@>=0.28.0, ipython@>=9.6.0, joblib@>=1.5.0, kaggle@>=2.0.0, matplotlib@>=3.10.7, modal@>=1.3.3, numpy@>=2.2.0, pandas@>=2.3.0, pillow@>=10.4.0, pydantic@>=2.11.0, pygame@>=2.6.1, python-dotenv@>=1.0.1, python-multipart@>=0.0.20, scikit-learn@>=1.7.0, tinygrad@>=0.11.0, torch@>=2.9.0, torchvision@>=0.24.0, transformers@>=4.57.0, uvicorn@>=0.35.0, websockets@>=16.0
- compute_node/pyproject.toml: fastapi@>=0.116.0, httpx@>=0.28.0, pillow@>=10.4.0, pydantic@>=2.11.0, torch@>=2.9.0, torchvision@>=0.24.0, transformers@>=4.57.0, uvicorn@>=0.35.0
- frontend/package.json: @eslint/js@^9.39.1, @react-three/drei@^10.7.7, @react-three/fiber@^9.5.0, @types/node@^24.10.1, @types/react@^19.2.7, @types/react-dom@^19.2.3, @types/three@^0.182.0, @vitejs/plugin-react@^5.1.1, eslint@^9.39.1, eslint-plugin-react-hooks@^7.0.1, eslint-plugin-react-refresh@^0.4.24, globals@^16.5.0, react@^19.2.0, react-dom@^19.2.0, three@^0.182.0, typescript@~5.9.3, typescript-eslint@^8.48.0, vite@^7.3.1, zustand@^5.0.11

### Recent commits (newest first)

- Update README.md
- treehacks is the goat
- record time
- Merge branch 'main' of https://github.com/aryan-cs/burn
- pls
- update pages
- Merge branch 'main' of https://github.com/aryan-cs/burn
- update landing
- fixing up
- Merge branch 'main' of https://github.com/aryan-cs/burn
- readying up
- Merge pull request #7 from aryan-cs/dgx-vlm
- opium
- optimize some stuff
- fix smthn i dunno
- Merge pull request #6 from aryan-cs/mash
- Merge branch 'main' into mash
- test on ssh
- cool nn viz
- fix sandbox

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

### compute_node/pyproject.toml

```
[project]
name = "compute-node"
version = "0.1.0"
description = "Remote VLM inference compute node"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "fastapi>=0.116.0",
    "httpx>=0.28.0",
    "pydantic>=2.11.0",
    "pillow>=10.4.0",
    "torch>=2.9.0",
    "torchvision>=0.24.0",
    "transformers>=4.57.0",
    "uvicorn>=0.35.0",
]

```

### backend/pyproject.toml

```
[project]
name = "backend"
version = "0.1.0"
description = "MLCanvas backend API and graph compiler"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "fastapi>=0.116.0",
    "httpx>=0.28.0",
    "ipython>=9.6.0",
    "joblib>=1.5.0",
    "kaggle>=2.0.0",
    "matplotlib>=3.10.7",
    "modal>=1.3.3",
    "numpy>=2.2.0",
    "pandas>=2.3.0",
    "pydantic>=2.11.0",
    "python-multipart>=0.0.20",
    "python-dotenv>=1.0.1",
    "pygame>=2.6.1",
    "scikit-learn>=1.7.0",
    "tinygrad>=0.11.0",
    "torch>=2.9.0",
    "torchvision>=0.24.0",
    "transformers>=4.57.0",
    "pillow>=10.4.0",
    "uvicorn>=0.35.0",
    "websockets>=16.0",
]

[dependency-groups]
dev = [
    "pytest>=8.4.0",
]

```

### 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": {
    "@react-three/drei": "^10.7.7",
    "@react-three/fiber": "^9.5.0",
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "three": "^0.182.0",
    "zustand": "^5.0.11"
  },
  "devDependencies": {
    "@eslint/js": "^9.39.1",
    "@types/node": "^24.10.1",
    "@types/react": "^19.2.7",
    "@types/react-dom": "^19.2.3",
    "@types/three": "^0.182.0",
    "@vitejs/plugin-react": "^5.1.1",
    "eslint": "^9.39.1",
    "eslint-plugin-react-hooks": "^7.0.1",
    "eslint-plugin-react-refresh": "^0.4.24",
    "globals": "^16.5.0",
    "typescript": "~5.9.3",
    "typescript-eslint": "^8.48.0",
    "vite": "^7.3.1"
  }
}

```

### backend/main.py

```python
from __future__ import annotations

import logging
import os
from contextlib import asynccontextmanager
from pathlib import Path

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv

BACKEND_DIR = Path(__file__).resolve().parent
REPO_ROOT = BACKEND_DIR.parent
load_dotenv(BACKEND_DIR / ".env", override=False)
load_dotenv(REPO_ROOT / ".env", override=False)

from routers.deploy import router as deploy_router
from routers.datasets import router as datasets_router
from routers.model import router as model_router
from routers.rf_datasets import router as rf_datasets_router
from routers.rf_model import router as rf_model_router
from routers.rf_websocket import router as rf_websocket_router
from routers.vlm_model import router as vlm_model_router
from routers.vlm_websocket import router as vlm_websocket_router
from routers.websocket import router as websocket_router
from routers.ml_model import router as ml_model_router
from routers.ml_websocket import router as ml_ws_router
from routers.ai_coach import router as ai_coach_router, warn_if_ai_provider_keys_missing

logger = logging.getLogger(__name__)
if not logging.getLogger().handlers:
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    )


@asynccontextmanager
async def lifespan(_: FastAPI):
    artifacts_dir = Path(__file__).resolve().parent / "artifacts"
    artifacts_dir.mkdir(parents=True, exist_ok=True)
    warn_if_ai_provider_keys_missing()
    compute_node_url = os.getenv("VLM_COMPUTE_NODE_URL", "").strip()
    if compute_node_url:
        logger.info("VLM compute node configured at %s", compute_node_url)
    else:
        logger.info("VLM compute node is not configured; local VLM runtime will be used")
    yield


app = FastAPI(title="MLCanvas Backend", version="0.1.0", lifespan=lifespan)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(model_router)
app.include_router(deploy_router)
app.include_router(datasets_router)
app.include_router(websocket_router)
app.include_router(ml_model_router)
app.include_router(ml_ws_router)
app.include_router(ai_coach_router)
app.include_router(rf_model_router)
app.include_router(rf_datasets_router)
app.include_router(rf_websocket_router)
app.include_router(vlm_model_router)
app.include_router(vlm_websocket_router)


@app.get("/health")
async def health() -> dict[str, str]:
    return {"status": "ok"}


if __name__ == "__main__":
    import uvicorn

    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=False)

```

### compute_node/main.py

```python
from __future__ import annotations

import base64
import io
import json
import logging
from contextlib import asynccontextmanager
from typing import Any

from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from PIL import Image
from pydantic import BaseModel, field_validator

from vlm_runtime import DEFAULT_VLM_MODEL_ID, vlm_runtime

logger = logging.getLogger("compute_node.vlm")
if not logging.getLogger().handlers:
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    )


class VLMInferRequest(BaseModel):
    image_base64: str
    model_id: str = DEFAULT_VLM_MODEL_ID
    score_threshold: float = 0.45
    max_detections: int = 25

    @field_validator("model_id")
    @classmethod
    def normalize_model_id(cls, value: str) -> str:
        text = value.strip()
        if text == "":
            return DEFAULT_VLM_MODEL_ID
        return text

    @field_validator("score_threshold")
    @classmethod
    def score_in_range(cls, value: float) -> float:
        if value < 0 or value > 1:
            raise ValueError("score_threshold must be between 0 and 1")
        return value

    @field_validator("max_detections")
    @classmethod
    def positive_limit(cls, value: int) -> int:
        if value <= 0:
            raise ValueError("max_detections must be > 0")
        return value


def _decode_data_url_image(value: str) -> Image.Image:
    raw = value.strip()
    if raw == "":
        raise HTTPException(status_code=400, detail={"message": "image_base64 cannot be empty"})
    if "," in raw and raw.startswith("data:"):
        _, encoded = raw.split(",", maxsplit=1)
    else:
        encoded = raw
    try:
        image_bytes = base64.b64decode(encoded, validate=True)
    except Exception as exc:
        raise HTTPException(status_code=400, detail={"message": f"Invalid base64 image payload: {exc}"}) from exc
    try:
        return Image.open(io.BytesIO(image_bytes)).convert("RGB")
    except Exception as exc:
        raise HTTPException(status_code=400, detail={"message": f"Could not decode image payload: {exc}"}) from exc


def _run_infer(payload: VLMInferRequest, *, source: str) -> dict[str, Any]:
    image = _decode_data_url_image(payload.image_base64)
    result = vlm_runtime.detect(
        image,
        model_id=payload.model_id,
        score_threshold=payload.score_threshold,
        max_detections=payload.max_detections,
    )
    logger.info(
        "Compute node infer served: source=%s model_id=%s backend=%s device=%s detections=%d",
        source,
        result.get("runtime_model_id"),
        result.get("runtime_backend"),
        result.get("runtime_device"),
        len(result.get("detections", [])),
    )
    return result


@asynccontextmanager
async def lifespan(_: FastAPI):
    # Warm up once at startup so the first request is less likely to stall.
    warmup = vlm_runtime.warmup(DEFAULT_VLM_MODEL_ID)
    accel = vlm_runtime.acceleration_profile
    logger.info(
        "Compute node startup complete: default_model=%s backend=%s device=%s autocast=%s fp16_model=%s",
        warmup.model_id,
        warmup.backend,
        warmup.device,
        accel["autocast"],
        accel["fp16_model"],
    )
    yield


app = FastAPI(title="VLM Compute Node", version="0.1.0", lifespan=lifespan)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/health")
async def health() -> dict[str, Any]:
    return {"status": "ok", **vlm_runtime.acceleration_profile}


@app.post("/api/v1/vlm/infer")
async def vlm_infer(payload: VLMInferRequest) -> dict[str, Any]:
    return _run_infer(payload, source="http")


@app.websocket("/ws/v1/vlm/infer")
async def vlm_infer_ws(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            raw_message = await websocket.receive_text()
            try:
                payload = VLMInferRequest.model_validate(json.loads(raw_message))
            except Exception as exc:
                await websocket.send_json({"type": "infer_error", "message": f"Invalid payload: {exc}"})
                continue

            try:
                result = _run_infer(payload, source="ws")
                await websocket.send_json({"type": "infer_result", **result})
            except HTTPException as exc:
                detail = exc.detail if isinstance(exc.detail, dict) else {"message": str(exc.detail)}
                await websocket.send_json({"type": "infer_error", **detail})
            except Exception as exc:
                await websocket.send_json({"type": "infer_error", "message": f"Inference failed: {exc}"})
    except WebSocketDisconnect:
        return


if __name__ == "__main__":
    import uvicorn

    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    )
    uvicorn.run("main:app", host="0.0.0.0", port=8100, reload=False)

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import DeploymentsPage from './deployments/DeploymentsPage'
import LandingPage from './launch/LandingPage'
import ModelHubPage from './launch/ModelHubPage'
import LinearRegressionBootstrapPage from './linreg/LinearRegressionBootstrapPage'
import NNBootstrapPage from './nn/NNBootstrapPage'
import RFBootstrapPage from './rf/RFBootstrapPage'
import VLMBootstrapPage from './vlm/VLMBootstrapPage'

const path = window.location.pathname.toLowerCase()

function resolveTitle(currentPath: string): string {
  if (currentPath.startsWith('/builders')) return 'Burn | Builders'
  if (currentPath.startsWith('/deployments')) return 'Burn | Deployments'
  if (currentPath.startsWith('/rf')) return 'Burn | Random Forest Builder'
  if (currentPath.startsWith('/nn')) return 'Burn | Neural Network Builder'
  if (currentPath.startsWith('/vlm')) return 'Burn | VLM Builder'
  if (currentPath.startsWith('/svm')) return 'Burn | SVM Builder'
  if (currentPath.startsWith('/pca')) return 'Burn | PCA Builder'
  if (currentPath.startsWith('/linreg')) return 'Burn | Linear Regression Builder'
  if (currentPath.startsWith('/logreg')) return 'Burn | Logistic Regression Builder'
  return 'Burn | Home'
}

function resolveEntry() {
  if (path.startsWith('/builders')) return <ModelHubPage />
  if (path.startsWith('/deployments')) return <DeploymentsPage />
  if (path.startsWith('/vlm')) return <VLMBootstrapPage />
  if (path.startsWith('/rf')) return <RFBootstrapPage />
  if (path.startsWith('/nn')) return <NNBootstrapPage />
  if (path.startsWith('/linreg')) return <LinearRegressionBootstrapPage />
  return <LandingPage />
}

document.title = resolveTitle(path)

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    {resolveEntry()}
  </StrictMode>,
)

```

### frontend/vite.config.ts

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

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': 'http://localhost:8000',
      '/ws': {
        target: 'ws://localhost:8000',
        ws: true,
      },
    },
  },
})

```

### frontend/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/logo.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap');
    </style>
    <title>Burn</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### frontend/eslint.config.js

```javascript
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
      reactHooks.configs.flat.recommended,
      reactRefresh.configs.vite,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
])

```

### backend/modal_sandbox_infer_server.py

```python
from __future__ import annotations

import os
from typing import Any

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

from modal_worker import infer_deployment_payload


app = FastAPI(title="Burn Modal Sandbox Inference Server")
DEPLOYMENT_ID = os.getenv("BURN_MODAL_DEPLOYMENT_ID", "").strip()


class InferRequest(BaseModel):
    inputs: Any
    return_probabilities: bool = True
    deployment_id: str | None = None


@app.get("/health")
def health() -> dict[str, str]:
    if not DEPLOYMENT_ID:
        raise HTTPException(status_code=500, detail="Missing BURN_MODAL_DEPLOYMENT_ID")
    return {"status": "ok", "deployment_id": DEPLOYMENT_ID}


@app.post("/infer")
def infer(payload: InferRequest) -> dict[str, Any]:
    if not DEPLOYMENT_ID:
        raise HTTPException(status_code=500, detail="Missing BURN_MODAL_DEPLOYMENT_ID")
    if payload.deployment_id and payload.deployment_id != DEPLOYMENT_ID:
        raise HTTPException(status_code=400, detail="deployment_id does not match sandbox deployment")
    try:
        return infer_deployment_payload(
            {
                "deployment_id": DEPLOYMENT_ID,
                "inputs": payload.inputs,
                "return_probabilities": payload.return_probabilities,
            }
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"Sandbox inference failed: {exc}") from exc

```

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