Skip to content

amrrs/open-artifacts-lite

Repository files navigation

Open Artifacts Lite

An open-source, self-hostable AI app generator and artifact runner, powered by open models (default: GLM-5.2) served via Nebius Token Factory.

Describe what you want — a single-page site, an interactive React component, a 3D scene, a data-analysis script, or a short video — and the model streams a structured "artifact" that runs entirely in your browser. No server sandbox, no per-run cloud compute bill: the only paid dependency is the Nebius API key.

What it can generate

Artifact type Runtime Where it runs
HTML / SVG / Markdown / diagrams sandboxed <iframe srcdoc> browser
React components & 3D (Three.js / R3F) Sandpack browser (CodeSandbox bundler, no API key)
Python (data analysis, matplotlib, code-interpreter) Pyodide (CPython → WASM) Web Worker
Video ffmpeg.wasm (driven by a Python script via Pyodide) Web Worker
Motion graphics (React) Remotion @remotion/player (via Sandpack) browser (no API key)
Motion graphics (HTML/GSAP) HeyGen HyperFrames @hyperframes/player web component browser (no API key)

Quick start

git clone <this repo>
cd open-artifacts
npm install
cp .env.example .env.local   # add your NEBIUS_API_KEY
npm run dev                  # http://localhost:3000

Get a Nebius Token Factory API key at https://tokenfactory.nebius.com → API keys → Create.

Environment variables

Variable Required Default Notes
NEBIUS_API_KEY yes Nebius Token Factory key
ENABLE_CROSS_ORIGIN_ISOLATION no false Set true to emit COOP/COEP headers for the faster multi-threaded ffmpeg core

The default model id is not an env var — it lives in code as NEBIUS_DEFAULTS.modelId (lib/providers/nebius.ts) and in the picker list (lib/models.ts). Run curl -H "Authorization: Bearer $NEBIUS_API_KEY" https://api.tokenfactory.nebius.com/v1/models to find valid ids for your account, then add one to lib/models.ts.

Testing

The project is built test-first. Three layers:

npm test            # Vitest unit + component tests (jsdom, no network)
npm run test:e2e    # Playwright E2E in real Chromium (needs network for CDNs)
npm run typecheck   # tsc --noEmit

Each runtime has a dedicated E2E page (/e2e/html, /e2e/react, /e2e/react-3d, /e2e/python, /e2e/video, /e2e/remotion, /e2e/hyperframes) that renders a canned artifact and asserts it actually executes — e.g. that Pyodide prints stdout and renders a matplotlib figure, and that ffmpeg.wasm encodes a real, playable MP4.

Architecture

flowchart LR
    user[User prompt] --> chat["/api/chat (edge route)"]
    chat -->|"chat.completions.create\n(official openai SDK, json_schema response_format)"| nebius["Nebius Token Factory\n(GLM-5.2, OpenAI-compatible)"]
    nebius --> artifact["Structured Artifact JSON\n(type, files, deps)"]
    artifact --> selector["ArtifactPanel\nselectRuntime(type)"]
    selector --> htmlRt["iframe srcdoc\n(html / svg / markdown)"]
    selector --> sandpackRt["Sandpack\n(react / 3D)"]
    selector --> pyodideRt["Pyodide worker\n(python)"]
    selector --> ffmpegRt["ffmpeg.wasm worker\n(video)"]
Loading
  • /api/chat is an edge-compatible route handler (app/api/chat/route.ts) that streams a structured artifact via the official openai SDK's chat.completions.create({ stream: true, response_format: { type: "json_schema", ... } }), pointed at Nebius's OpenAI-compatible endpoint, and re-emits each delta as a newline-delimited JSON frame ({"type":"reasoning"|"content","text":"..."}) so the client can show live chain-of-thought progress separately from the artifact body. No Node-only APIs, so it runs on any serverless/edge free tier.
  • Provider registry (lib/providers/nebius.ts) wraps the official openai SDK's client with the Nebius base URL; adding other OpenAI-compatible providers is a few lines.
  • Streaming on the client (lib/hooks/useObjectStream.ts) reads the response body as it arrives, accumulates the "content" frames, and re-parses them with a small hand-rolled partial-JSON repairer (lib/streaming/partial-json.ts) so the UI can render fields as they stream in — no third-party AI SDK on either side of the wire.
  • Pure runtime planners (lib/runtime/*.ts) are fully unit-tested: schema validation, package splitting, srcdoc building, Sandpack file mapping, ffmpeg arg helpers.
  • Workers load Pyodide and the ffmpeg core from CDNs at runtime (their npm ESM builds pull in Node-only / nested-worker code that breaks browser bundling), keeping the app bundle small and the heavy wasm out of git.

Deploy on a free tier

The server side is just one streaming route with one env var, so any serverless free tier works.

Netlify (primary)

A netlify.toml is included. The Next.js plugin (@netlify/plugin-nextjs) is auto-detected; set NEBIUS_API_KEY in Netlify's site env vars and deploy. Optional COOP/COEP headers for the multi-threaded ffmpeg core are commented in the file.

Vercel

Import the repo — App Router + streaming work with zero config. Add NEBIUS_API_KEY in Project Settings → Environment Variables.

Cloudflare Pages

Use @cloudflare/next-on-pages; Workers' free-tier CPU limit doesn't count time spent waiting on the Nebius stream, making it the most timeout-resistant option for long generations.

Project layout

app/
  api/chat/route.ts          edge streaming route (Nebius via the openai SDK, json_schema response_format)
  e2e/<runtime>/page.tsx     canned-artifact pages for runtime E2E tests
  page.tsx, layout.tsx       chat + artifact split-pane UI
components/
  chat/                      ChatPanel, ModelPicker (defaults to GLM-5.2)
  artifact/                  ArtifactPanel + HtmlPreview / ReactPreview / PythonPreview / VideoPreview
lib/
  schema.ts                  Zod ArtifactSchema
  providers/                 Nebius OpenAI-compatible provider (official openai SDK client)
  prompt.ts                  system prompt (per-type authoring rules)
  models.ts                  model picker list (GLM-5.2 default)
  hooks/useObjectStream.ts   client-side fetch + streaming partial-JSON hook
  streaming/partial-json.ts  best-effort repair/parse of truncated JSON
  runtime/                   pure, tested planners (select-runtime, html-preview, sandpack-config, pyodide-plan, ffmpeg-args)
public/workers/ffmpeg-runner.js   classic worker: Pyodide + ffmpeg.wasm from CDN
workers/pyodide.worker.ts    module worker: Pyodide from CDN

Notes & trade-offs

  • Isolation is the browser's own sandbox model (sandboxed iframe + Web Workers), not OS-level microVMs (E2B/Daytona). That's the right trade-off for a personal/local tool with no public multi-tenant exposure; the selectRuntime abstraction leaves room to add a server sandbox as another runtime later.
  • Sandpack uses CodeSandbox's free public bundler — no CSB API key required (keys are only needed to export sandboxes into a CodeSandbox project).
  • ffmpeg.wasm uses the single-thread core by default so it runs anywhere; flip ENABLE_CROSS_ORIGIN_ISOLATION=true for the ~2-4x faster multi-threaded core.

About

Open Artifacts Lite - like Claude Artifacts but for open models powered by @nebiustf and WASM!

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors