Skip to content

Commit f9386c3

Browse files
abrichrclaude
andcommitted
feat: initial openadapt-viewer package creation
Create standalone viewer package for training and benchmark visualization. Core architecture: - core/types.py: ViewerData, StepData, ComparisonData type definitions - core/data_loader.py: Load training logs, captures, benchmark results - core/html_builder.py: HTML generation utilities with Jinja2 templates - cli.py: Command-line interface for generating and serving viewers Templates: - templates/base.html: Base HTML template with shared styles - templates/components/: Reusable UI components (header, navigation) Viewers: - viewers/benchmark/: Benchmark result viewer (data loading + generation) Configuration: - pyproject.toml with hatchling build system - ARCHITECTURE.md: Technical architecture documentation - README.md: Usage and installation guide - LICENSE: MIT license Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
0 parents  commit f9386c3

19 files changed

Lines changed: 2323 additions & 0 deletions

File tree

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
__pycache__/
2+
*.pyc
3+
*.pyo
4+
.venv/
5+
venv/
6+
.DS_Store
7+
dist/
8+
build/
9+
*.egg-info/
10+
*.log

ARCHITECTURE.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# OpenAdapt Viewer Architecture
2+
3+
This document describes the architecture of the openadapt-viewer package for LLM assistants and developers.
4+
5+
## Overview
6+
7+
openadapt-viewer generates standalone HTML files for visualizing ML training results, benchmark evaluations, and capture recordings. The architecture prioritizes:
8+
9+
1. **Maintainability** - Small, focused files that fit in LLM context windows
10+
2. **Separation of concerns** - Data loading, processing, and presentation are separate
11+
3. **Standalone capability** - Generated HTML files work offline without a server
12+
4. **No build step** - CDN-loaded libraries, no webpack/vite required
13+
14+
## Technology Stack
15+
16+
| Layer | Technology | Rationale |
17+
|-------|------------|-----------|
18+
| Data Processing | Pure Python + Pydantic | Type-safe, testable |
19+
| HTML Structure | Jinja2 templates | Industry standard, well-understood |
20+
| Visualization | Plotly | Best standalone export support |
21+
| Styling | Tailwind CSS (CDN) | Utility-first, no build step |
22+
| Interactivity | Alpine.js (CDN) | Lightweight (~15KB), declarative |
23+
24+
## Directory Structure
25+
26+
```
27+
openadapt-viewer/
28+
├── pyproject.toml
29+
├── README.md
30+
├── ARCHITECTURE.md # This file
31+
├── src/
32+
│ └── openadapt_viewer/
33+
│ ├── __init__.py # Package exports
34+
│ ├── cli.py # CLI entry point
35+
│ │
36+
│ ├── core/ # Shared utilities
37+
│ │ ├── __init__.py
38+
│ │ ├── types.py # Pydantic models, type definitions
39+
│ │ ├── data_loader.py # Common data loading utilities
40+
│ │ └── html_builder.py # Jinja2 environment setup
41+
│ │
42+
│ ├── templates/ # Jinja2 templates
43+
│ │ ├── base.html # Base template with CDN imports
44+
│ │ └── components/ # Reusable HTML components
45+
│ │ ├── header.html
46+
│ │ └── navigation.html
47+
│ │
48+
│ └── viewers/ # Vertical slices by viewer type
49+
│ ├── __init__.py
50+
│ └── benchmark/ # Benchmark viewer
51+
│ ├── __init__.py
52+
│ ├── data.py # Data models and loading
53+
│ └── generator.py # HTML generation logic
54+
```
55+
56+
## Key Design Patterns
57+
58+
### 1. Vertical Slice Architecture
59+
60+
Each viewer type (benchmark, training, recording) is a self-contained module with:
61+
- **data.py** - Pydantic models and data loading functions
62+
- **generator.py** - HTML generation logic
63+
- **templates/** (optional) - Viewer-specific templates
64+
65+
This allows LLMs to understand and modify one viewer without loading the entire codebase.
66+
67+
### 2. Template Inheritance
68+
69+
All templates inherit from `base.html`, which provides:
70+
- CDN imports for Tailwind CSS, Alpine.js, and Plotly
71+
- Common header and navigation components
72+
- Dark mode support
73+
- Responsive layout
74+
75+
```html
76+
{% extends "base.html" %}
77+
{% block title %}My Page{% endblock %}
78+
{% block content %}
79+
<h1>Page content here</h1>
80+
{% endblock %}
81+
```
82+
83+
### 3. Data/Presentation Separation
84+
85+
Data loading and HTML generation are strictly separated:
86+
87+
```python
88+
# data.py - Pure data operations
89+
from pydantic import BaseModel
90+
91+
class BenchmarkTask(BaseModel):
92+
task_id: str
93+
status: str
94+
metrics: dict
95+
96+
def load_benchmark_data(path: str) -> list[BenchmarkTask]:
97+
# Load and validate data, no HTML concerns
98+
...
99+
```
100+
101+
```python
102+
# generator.py - HTML generation only
103+
from .data import load_benchmark_data
104+
105+
def generate_benchmark_html(data_path: str, output_path: str) -> None:
106+
tasks = load_benchmark_data(data_path)
107+
# Render templates with data
108+
...
109+
```
110+
111+
### 4. Standalone HTML Generation
112+
113+
Generated HTML files are fully self-contained:
114+
- Plotly.js can be embedded or loaded from CDN
115+
- Data is embedded as inline JSON
116+
- No external dependencies required for viewing
117+
118+
```python
119+
# CDN mode (smaller file, requires internet)
120+
fig.to_html(include_plotlyjs='cdn')
121+
122+
# Standalone mode (larger file, works offline)
123+
fig.to_html(include_plotlyjs=True)
124+
```
125+
126+
## File Size Guidelines
127+
128+
To maintain LLM-friendliness:
129+
- Keep files under **500 lines**
130+
- One responsibility per file
131+
- Use type hints throughout
132+
- Add docstrings explaining intent, not mechanics
133+
134+
## Adding a New Viewer
135+
136+
1. Create a new directory under `viewers/`:
137+
```
138+
viewers/myviewer/
139+
├── __init__.py
140+
├── data.py
141+
└── generator.py
142+
```
143+
144+
2. Define Pydantic models in `data.py`
145+
146+
3. Implement generation logic in `generator.py`
147+
148+
4. Add CLI command in `cli.py`
149+
150+
5. Update this document
151+
152+
## CDN Resources
153+
154+
The following CDN resources are loaded in `base.html`:
155+
156+
| Library | CDN URL | Version | Size |
157+
|---------|---------|---------|------|
158+
| Tailwind CSS | cdn.tailwindcss.com | 3.x | ~100KB (JIT) |
159+
| Alpine.js | cdn.jsdelivr.net/npm/alpinejs | 3.x | ~15KB |
160+
| Plotly.js | cdn.plot.ly/plotly-2.32.0.min.js | 2.32.0 | ~3.5MB |
161+
162+
For offline/standalone mode, Plotly.js is embedded directly in the HTML.
163+
164+
## Testing
165+
166+
Run tests with:
167+
```bash
168+
uv run pytest
169+
```
170+
171+
Test files should mirror the source structure:
172+
```
173+
tests/
174+
├── test_core/
175+
│ ├── test_types.py
176+
│ ├── test_data_loader.py
177+
│ └── test_html_builder.py
178+
└── test_viewers/
179+
└── test_benchmark/
180+
├── test_data.py
181+
└── test_generator.py
182+
```
183+
184+
## Related Projects
185+
186+
- **openadapt-ml** - ML training pipeline (source of training data)
187+
- **openadapt-evals** - Benchmark evaluation infrastructure
188+
- **openadapt-capture** - Recording capture tool

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 MLDSAI Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# openadapt-viewer
2+
3+
Standalone HTML viewer generation for OpenAdapt ML dashboards and benchmarks.
4+
5+
## Overview
6+
7+
openadapt-viewer generates self-contained HTML files for visualizing:
8+
- Benchmark evaluation results (WAA, WebArena, OSWorld)
9+
- ML training metrics and loss curves
10+
- Capture recordings with action predictions
11+
12+
Generated files work offline without requiring a server.
13+
14+
## Installation
15+
16+
```bash
17+
pip install openadapt-viewer
18+
```
19+
20+
Or with uv:
21+
```bash
22+
uv add openadapt-viewer
23+
```
24+
25+
## Quick Start
26+
27+
### Generate a Benchmark Viewer
28+
29+
```python
30+
from openadapt_viewer.viewers.benchmark import generate_benchmark_html
31+
32+
# From a benchmark results directory
33+
generate_benchmark_html(
34+
data_path="benchmark_results/run_001/",
35+
output_path="benchmark_viewer.html",
36+
standalone=True # Embed all resources for offline viewing
37+
)
38+
```
39+
40+
### CLI Usage
41+
42+
```bash
43+
# Generate benchmark viewer
44+
openadapt-viewer benchmark --data benchmark_results/run_001/ --output viewer.html
45+
46+
# Generate with embedded resources (standalone)
47+
openadapt-viewer benchmark --data benchmark_results/run_001/ --output viewer.html --standalone
48+
```
49+
50+
## Architecture
51+
52+
The package uses:
53+
- **Jinja2** for HTML templating with inheritance
54+
- **Plotly** for interactive visualizations
55+
- **Tailwind CSS** (CDN) for styling
56+
- **Alpine.js** (CDN) for lightweight interactivity
57+
58+
See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed design documentation.
59+
60+
## Development
61+
62+
```bash
63+
# Clone the repo
64+
git clone https://github.com/OpenAdaptAI/openadapt-viewer.git
65+
cd openadapt-viewer
66+
67+
# Install with dev dependencies
68+
uv sync --all-extras
69+
70+
# Run tests
71+
uv run pytest
72+
73+
# Run linter
74+
uv run ruff check .
75+
```
76+
77+
## Project Structure
78+
79+
```
80+
src/openadapt_viewer/
81+
├── cli.py # CLI entry point
82+
├── core/ # Shared utilities
83+
│ ├── types.py # Pydantic models
84+
│ ├── data_loader.py # Data loading utilities
85+
│ └── html_builder.py # Jinja2 environment setup
86+
├── templates/ # Jinja2 templates
87+
│ ├── base.html # Base template with CDN imports
88+
│ └── components/ # Reusable components
89+
└── viewers/ # Viewer implementations
90+
└── benchmark/ # Benchmark viewer
91+
```
92+
93+
## License
94+
95+
MIT License - see LICENSE file for details.

pyproject.toml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "openadapt-viewer"
7+
version = "0.1.0"
8+
description = "Standalone HTML viewer generation for OpenAdapt ML dashboards and benchmarks"
9+
readme = "README.md"
10+
requires-python = ">=3.10"
11+
license = "MIT"
12+
authors = [
13+
{name = "Richard Abrich", email = "richard@openadapt.ai"}
14+
]
15+
keywords = ["viewer", "dashboard", "visualization", "ml", "benchmarks", "html"]
16+
classifiers = [
17+
"Development Status :: 3 - Alpha",
18+
"Intended Audience :: Developers",
19+
"License :: OSI Approved :: MIT License",
20+
"Programming Language :: Python :: 3",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
24+
"Topic :: Scientific/Engineering :: Visualization",
25+
"Topic :: Software Development :: Libraries :: Python Modules",
26+
]
27+
28+
dependencies = [
29+
"jinja2>=3.1.0",
30+
"plotly>=5.18.0",
31+
"pydantic>=2.0.0",
32+
"pillow>=10.0.0",
33+
]
34+
35+
[project.optional-dependencies]
36+
dev = [
37+
"pytest>=8.0.0",
38+
"ruff>=0.1.0",
39+
]
40+
all = [
41+
"openadapt-viewer[dev]",
42+
]
43+
44+
[project.scripts]
45+
openadapt-viewer = "openadapt_viewer.cli:main"
46+
47+
[project.urls]
48+
Homepage = "https://github.com/OpenAdaptAI/openadapt-viewer"
49+
Repository = "https://github.com/OpenAdaptAI/openadapt-viewer"
50+
51+
[tool.hatch.build.targets.wheel]
52+
packages = ["src/openadapt_viewer"]
53+
54+
[tool.ruff]
55+
line-length = 100

src/openadapt_viewer/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""OpenAdapt Viewer - Standalone HTML viewer generation for ML dashboards and benchmarks."""
2+
3+
__version__ = "0.1.0"
4+
5+
from openadapt_viewer.core.html_builder import HTMLBuilder
6+
from openadapt_viewer.core.types import (
7+
BenchmarkRun,
8+
BenchmarkTask,
9+
TaskExecution,
10+
ExecutionStep,
11+
)
12+
13+
__all__ = [
14+
"HTMLBuilder",
15+
"BenchmarkRun",
16+
"BenchmarkTask",
17+
"TaskExecution",
18+
"ExecutionStep",
19+
]

0 commit comments

Comments
 (0)