Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 7 additions & 18 deletions .cursor/skills/studio/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ description: >
argument-hint: "[wait for edits from CSS Studio]"
---

# Studio

## Overview

CSS Studio is a visual editor that lets users edit CSS properties, HTML attributes, text content, and CSS variables on a live page. When connected via MCP, edits made in the panel are sent as structured data describing the change.
Expand All @@ -15,14 +13,12 @@ CSS Studio is a visual editor that lets users edit CSS properties, HTML attribut

Your **first** `css-studio` call must be `announce`:

```typescript
css-studio({ action: "announce", capabilities: { subagent: <reflect-your-actual-support>, backgroundSubagents: <true-only-if-genuinely-detached>, channelEvents: <reflect-your-actual-support>, ask: <reflect-your-actual-support> } })
```

**Each capability value must reflect your host's ACTUAL supported features, not a literal copy-paste of these placeholder values:**
css-studio({ action: "announce", capabilities: { subagent: true, backgroundSubagents: true, channelEvents: true, ask: true } })
```

- `subagent` — `true` if you can spawn subagents via a Task/Agent tool.
- `backgroundSubagents` — `true` **only** if your host's subagent spawn is _detached_ so your turn continues immediately while the subagent runs (e.g. a `run_in_background: true` flag or equivalent capability). This is what enables parallel tasks. If you have such a capability, announce `true` and use it; if your spawn primitive blocks the parent, **you must announce `false` or omit this field** — tasks then route in **linear mode** and the hub queues them FIFO on this thread. That's correct behaviour — just no parallelism.
- `backgroundSubagents` — `true` **only** if your host's subagent spawn is _detached_ so your turn continues immediately while the subagent runs (e.g. a `run_in_background: true` flag or equivalent capability). This is what enables parallel tasks. If you have such a capability, announce `true` and use it; if your spawn primitive blocks the parent, announce `false` or omit — tasks then route in **linear mode** and the hub queues them FIFO on this thread. That's correct behaviour — just no parallelism.
- `channelEvents` — `true` if your environment delivers `notifications/claude/channel` notifications.
- `ask` — `true` if you can block on a user answer.

Expand All @@ -39,13 +35,6 @@ Then keep calling `get` in a loop forever:
- Never exit the loop except on user interrupt.
- Channel notifications (`notifications/claude/channel`) may arrive while a `get` is in flight — they unblock it early so the task is delivered without waiting out the poll. They do **not** replace the poll loop; the loop is mandatory.

**Backoff and resilience:**

- **After empty/immediate responses:** If `get` returns immediately without data multiple times in a row (e.g., 3+ consecutive empty responses within 1 second each), add a small delay (e.g., 2-5 seconds) before the next `get` call to avoid tight-looping. The underlying `get` timeout remains 60000ms.
- **After errors:** If `get` returns an error, log it and retry with exponential backoff (e.g., wait 1s, 2s, 4s, 8s, capping at 30s) between retries. Keep the `get` timeout at 60000ms.
- **Circuit breaker:** After N consecutive errors (e.g., 10), pause polling for a longer period (e.g., 60 seconds) before resuming, to avoid overwhelming a failing service.
- **Bounded concurrency:** Maintain a bounded queue or explicit maximum number of concurrent in-flight tasks (e.g., a limit of 10 parallel subagents in orchestrator mode, or 1 in-flight linear task). Once capacity is reached, queue new tasks internally or signal back-pressure to the hub. This prevents resource exhaustion during bursts.

## Tasks

Every task arrives with a `mode`. The task carries a `payload` object — the canonical, deduped data the subagent needs (or you need, in linear mode).
Expand All @@ -60,7 +49,7 @@ The task carries **`subagentPromptPath`**: an absolute path to a file the hub ha

**Spawn call shape:**

```typescript
```
Comment thread
cursor[bot] marked this conversation as resolved.
Task({
subagent_type: "general-purpose",
description: "CSS Studio task",
Expand Down Expand Up @@ -102,7 +91,7 @@ Handle the task inline. The `payload` has everything:

Diff-style edits carry `from` and `to` as separate fields so the values can contain any characters (including arrows). Non-diff edits use `value`.

```typescript
```json
{
"changes": [
{
Expand Down Expand Up @@ -155,6 +144,6 @@ Loose `changes` (no task wrapper) are keystroke edits. Apply them to source; don

> The CSS Studio MCP server is not installed. Install it with:
>
> ```bash
> npx cssstudio@1.0.0 install
> ```
> npx cssstudio install
> ```
Comment thread
cursor[bot] marked this conversation as resolved.
11 changes: 9 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ storybook-static/
/playwright-report/
/blob-report/
/playwright/
/.freebuff
/.freebuff/
/~/.cache/
/.agents/skills/studio

# Local agent/tool state
/.ocx/
/.opencode/
/.remember/logs/
/.cursor/hooks/state/

# Lockfiles for non-pnpm package managers (this repo uses pnpm exclusively)
package-lock.json
yarn.lock
bun.lockb
bun.lock
.cursor/hooks/state/continual-learning.json
/.cursor/hooks/state/continual-learning.json
/.cursor/hooks/state/continual-learning-index.json
/.dirac-cache
22 changes: 11 additions & 11 deletions ABOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ Olive recipes are JSON documents with strict pass ordering, execution-provider c

## Technology

| Layer | Stack |
|-------|--------|
| UI | React 19, Tailwind CSS, Radix UI |
| Server | Express, Vite (dev), SSE log streaming |
| Optimization | Python 3.9+, `olive-ai` in project `.venv` |
| Optional AI | Gemini, OpenAI, Anthropic, Mistral (user-provided keys) |
| Layer | Stack |
| ------------ | ------------------------------------------------------------------ |
| UI | React 19, Tailwind CSS, Radix UI |
| Server | Express, Vite (dev), SSE log streaming |
| Optimization | Python 3.10–3.13 (3.12 recommended), `olive-ai` in project `.venv` |
| Optional AI | Gemini, OpenAI, Anthropic, Mistral (user-provided keys) |

## License & attribution

Expand All @@ -49,8 +49,8 @@ Created by **Anthony Thompson** — [github.com/tonythethompson/Olive-Studio](ht

Paste these into **Repository → Settings → General → About** on GitHub:

| Field | Value |
|-------|--------|
| **Description** | Visual recipe builder and local runner for Microsoft Olive — ONNX conversion, quantization, pruning, and multi-vendor GPU/NPU deployment. |
| **Website** | *(leave blank or link to your demo/docs)* |
| **Topics** | `microsoft-olive` `onnx` `onnxruntime` `model-optimization` `quantization` `huggingface` `tensorrt` `openvino` `react` `local-first` `llm` `edge-ai` |
| Field | Value |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Description** | Visual recipe builder and local runner for Microsoft Olive — ONNX conversion, quantization, pruning, and multi-vendor GPU/NPU deployment. |
| **Website** | _(leave blank or link to your demo/docs)_ |
| **Topics** | `microsoft-olive` `onnx` `onnxruntime` `model-optimization` `quantization` `huggingface` `tensorrt` `openvino` `react` `local-first` `llm` `edge-ai` |
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Thank you for your interest in Olive Studio. This project is a community front e
### Requirements

- **Node.js** 22+ (pnpm 11 requirement)
- **Python** 3.9+ on `PATH` (for live Olive runs and GPU testing)
- **Python** 3.10–3.13 on `PATH` (3.12 recommended for torch/CUDA wheels; for live Olive runs and GPU testing)
- **Git**
- **Desktop (optional):** [Rust](https://rustup.rs/) + Windows WebView2 for `pnpm tauri:dev` / `pnpm tauri:build`

Expand Down
16 changes: 14 additions & 2 deletions ORIGINAL_REQUEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Initial Request — 2026-06-04T18:07:36-07:00

Olive Studio is a React/TypeScript + Node/Express web app (Vite + Tailwind) that provides a GUI for building, validating, and exporting Microsoft Olive model optimization recipes — covering conversion, quantization, pruning, PEFT/LoRA adapters, and hardware execution provider selection. A Gemini AI sidecar provides recipe validation and assistant chat. The codebase currently contains widespread fake scaffolding: simulated progress bars, hardcoded dummy metrics, fictional batch job logs, and a "Execute Live" button that only calls `console.log`.
Olive Studio is a React/TypeScript + Node/Express web app (Vite + Tailwind) that provides a GUI for building, validating, and exporting Microsoft Olive model optimization recipes — covering conversion, quantization, pruning, PEFT/LoRA adapters, and hardware execution provider selection. A Gemini AI sidecar provides recipe validation and assistant chat. The codebase currently contains widespread fake scaffolding: simulated progress bars, hardcoded dummy metrics, fictional batch job logs, and a "Execute Live" button that only calls `console.log`.

The task is to strip out all fake scaffolding and replace it with actual working backend logic.

Expand All @@ -12,6 +12,7 @@ Integrity mode: development (use any approach, library, or tool that works — b
---

## Reference Material

- Microsoft Olive getting started: https://microsoft.github.io/Olive/getting-started/getting-started.html
- Olive pass reference: https://microsoft.github.io/Olive/reference/pass.html
- Olive how-to guides: https://microsoft.github.io/Olive/how-to/index.html
Expand All @@ -25,6 +26,7 @@ Integrity mode: development (use any approach, library, or tool that works — b
### R1. Olive Environment Setup & Real Execution Backend

Add a `/api/olive/run` POST endpoint to `server.ts` that:

- On first call (or on demand), checks whether a Python virtual environment (`venv`) exists in the project directory. If not, creates it (`python -m venv .venv`) and installs Olive (`pip install olive-ai`) before proceeding
- Accepts the generated recipe JSON from the frontend as a POST body
- Writes it to a temp file, then spawns `python -m olive run --config <tmpfile>` inside the venv
Expand All @@ -35,13 +37,14 @@ Add a `/api/olive/run` POST endpoint to `server.ts` that:

The venv setup step must stream its own progress to the SSE endpoint (e.g., "Creating virtual environment...", "Installing olive-ai... this may take a minute") so the frontend can show real status rather than a spinner.

If `python` or `python3` is not found on PATH at all, the endpoint must return HTTP 503 with a clear message: `"Python not found on PATH. Install Python 3.9+ to use Olive execution."`
If `python` or `python3` is not found on PATH at all, the endpoint must return HTTP 503 with a clear message: `"Python not found on PATH. Install Python 3.10–3.13 (3.12 recommended) to use Olive execution."`

### R2. Replace Fake Batch Job Simulator

Remove the fake `setInterval` progress simulator in `BatchProcessingPanel.tsx` (lines ~110–172) and the hardcoded initial job seeds with fabricated metrics (lines ~44–103).

Replace with:

- When "Start Queue" is clicked, call `/api/olive/run` sequentially for each queued job (using the job's configured model, provider, and passes to build the recipe JSON)
- Receive the job ID from the backend and open an SSE connection to `/api/olive/stream/:jobId`
- Append each streamed log line to `job.logs[]` in real-time
Expand All @@ -64,6 +67,7 @@ In `server.ts`, replace all three occurrences of `"gemini-3.5-flash"` (which is
### R5. Real File Chunk Reconstruction

Replace the fake `setInterval` in `InputEnvironmentPanel.tsx`'s `startReconstruction()` function (lines ~363–416) with actual in-browser binary file assembly:

- The user has already selected the chunk files via the file input; use the actual `File` objects (stored in a ref alongside the `{name, size}` metadata currently in state)
- Sort chunks by their numeric suffix (`.001`, `.002`, etc.)
- Read each chunk as an `ArrayBuffer` using `FileReader` or `file.arrayBuffer()`
Expand All @@ -81,6 +85,7 @@ Replace the fake `setInterval` in `InputEnvironmentPanel.tsx`'s `startReconstruc
### R7. Remove or Replace PerformanceMetrics Placeholder

Inspect `PerformanceMetrics.tsx`. If it displays static/hardcoded chart data:

- Either remove it entirely from `ExecutionWorkspace.tsx`, or
- Replace its data source with real metrics parsed from the most recently completed Olive job's log output
- Do not leave fabricated chart data rendering as though it represents real optimization results
Expand All @@ -90,6 +95,7 @@ Inspect `PerformanceMetrics.tsx`. If it displays static/hardcoded chart data:
## Acceptance Criteria

### R1 — Olive Backend

- [ ] `POST /api/olive/run` endpoint exists in `server.ts`
- [ ] If Python is not on PATH, the endpoint returns HTTP 503 with a Python-not-found message (testable by temporarily renaming python)
- [ ] If `.venv` does not exist, hitting the endpoint creates it and installs olive-ai (observable via filesystem and pip list)
Expand All @@ -98,33 +104,39 @@ Inspect `PerformanceMetrics.tsx`. If it displays static/hardcoded chart data:
- [ ] No simulated sleep/delay is used anywhere in the execution path

### R2 — Batch Processing

- [ ] The `setInterval` fake loop is completely removed from `BatchProcessingPanel.tsx`
- [ ] The hardcoded initial job seeds (`job-1`, `job-2`, `job-3`) with fabricated metrics are removed
- [ ] Clicking "Start Queue" produces a network request to `/api/olive/run` (visible in browser DevTools Network tab)
- [ ] Job logs in the UI contain real Olive CLI output lines (or real setup/error lines), not hardcoded strings
- [ ] No hardcoded metrics values (e.g., `"14.2 ms"`, `"70.4 tok/s"`) remain anywhere in the batch panel

### R3 — Execute Live

- [ ] The `console.log("Run triggered")` noop is removed from `App.tsx`
- [ ] Clicking "Execute Live" triggers a real POST to `/api/olive/run`
- [ ] The Optimization Logs section in `ExecutionWorkspace.tsx` updates in real-time with streamed output
- [ ] The button is disabled while a job is running

### R4 — Gemini Model Name

- [ ] `grep -r "gemini-3.5-flash" server.ts` returns no results
- [ ] Gemini API calls succeed without model-not-found errors (requires GEMINI_API_KEY to be set)

### R5 — File Reconstruction

- [ ] The fake `setInterval` in `startReconstruction` is removed
- [ ] Uploading two chunk files (e.g., `model.bin.001` and `model.bin.002`) and clicking reconstruct produces a real download
- [ ] Progress reflects actual bytes read
- [ ] The fake `generateFileHash` simulation is removed; if a hash is shown it is computed via Web Crypto

### R6 — Caching Switch

- [ ] `UIState` in `types.ts` has a `distributedCaching` boolean field
- [ ] The switch in `EnterpriseInfraPanel.tsx` is controlled (reads/writes `state.distributedCaching`)
- [ ] The generated recipe JSON in `ExecutionWorkspace.tsx` reflects the switch state in `engine.cache_dir`

### R7 — PerformanceMetrics

- [ ] No hardcoded/static chart values remain in `PerformanceMetrics.tsx`
- [ ] Either the component is removed, or it only displays data from real completed job output
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Pass combinations are checked against your execution provider (for example, AWQ

- **Node.js** 22+ (pnpm 11 requirement)
- **pnpm** 11.17.0+ (the project uses `packageManager: pnpm@11.17.0`; `npm install` is blocked)
- **Python** 3.9+ (on `PATH`, or set from the app header **Runtime** control if missing)
- **Python** 3.10–3.13 (3.12 recommended) on `PATH`, or set from the app header **Runtime** control if missing. Matches **olive-ai** (≥3.10; classifiers through 3.13).
- **Optional:** NVIDIA / Intel / Qualcomm / AMD tooling for GPU or NPU recipes
- **Optional:** [Hugging Face token](https://huggingface.co/settings/tokens) for gated models

Expand Down Expand Up @@ -111,7 +111,7 @@ Open **<http://localhost:3000>**.

Runs the same Node server inside a native window (WebView) instead of your browser.

**Extra prerequisites:** [Rust](https://rustup.rs/) toolchain, Windows WebView2 (usually preinstalled), Node 22+, Python 3.9+.
**Extra prerequisites:** [Rust](https://rustup.rs/) toolchain, Windows WebView2 (usually preinstalled), Node 22+, Python 3.10–3.13 (3.12 recommended).

```bash
pnpm install
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:coverage:threshold": "vitest run --coverage --coverage.thresholds.lines=90 --coverage.thresholds.statements=88 --coverage.thresholds.branches=80 --coverage.thresholds.functions=85 --coverage.thresholds.perFile=true",
"test:server": "vitest run --config vitest.server.config.ts",
"test:integration": "vitest run --config vitest.integration.config.ts",
"test:watch": "vitest",
"lint": "tsc --noEmit && eslint",
"lint:quick": "oxlint --import-plugin src/",
Expand Down
78 changes: 78 additions & 0 deletions scripts/probe-python-version.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env node
/**
* Probe `python --version` for an absolute interpreter path.
* Invoked as: node scripts/probe-python-version.mjs <absolute-python>
*
* Kept as a fixed Node entrypoint so the Express process never passes a
* user-supplied string as execFile's executable (CodeQL command-injection).
*/
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

const PYTHON_BASENAME_RE = /^python(\d+(\.\d+)*)?(\.exe)?$/i;

function allowedRoots() {
const roots = [
path.resolve("/usr"),
path.resolve("/usr/local"),
path.resolve("/opt"),
path.resolve("/home"),
path.resolve(os.homedir()),
path.resolve(process.cwd(), ".venv"),
];
if (process.platform === "win32") {
for (const key of ["LOCALAPPDATA", "ProgramFiles", "ProgramFiles(x86)", "USERPROFILE"]) {
const v = process.env[key];
if (v) roots.push(path.resolve(v));
}
}
return roots;
}

/** Rebase onto an allowlisted root via path.relative / path.join. */
function rebaseOntoRoot(resolved) {
const normalized = path.normalize(resolved);
if (normalized.includes("\0")) return null;
for (const root of allowedRoots()) {
const rootNorm = path.normalize(root);
const relative = path.relative(rootNorm, normalized);
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) continue;
if (relative.split(path.sep).includes("..")) continue;
return path.join(rootNorm, relative);
}
return null;
}

const target = process.argv[2];
if (!target || target.includes("\0")) {
process.stderr.write("missing python path\n");
process.exit(2);
}
const resolved = path.resolve(target);
const safePath = rebaseOntoRoot(resolved);
if (!safePath || !PYTHON_BASENAME_RE.test(path.basename(safePath))) {
process.stderr.write("python path not allowed\n");
process.exit(2);
}
if (!fs.existsSync(safePath) || !fs.statSync(safePath).isFile()) {
process.stderr.write("python path not a file\n");
process.exit(2);
}

try {
const out = execFileSync(safePath, ["--version"], {
encoding: "utf8",
timeout: 8000,
stdio: ["ignore", "pipe", "pipe"],
});
process.stdout.write(typeof out === "string" ? out : String(out));
process.exit(0);
} catch (err) {
const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr ?? "") : "";
const stdout = err && typeof err === "object" && "stdout" in err ? String(err.stdout ?? "") : "";
process.stdout.write(stdout);
process.stderr.write(stderr || (err instanceof Error ? err.message : String(err)));
process.exit(1);
}
Loading
Loading