Generate slide decks from a plan you can edit.
Palette is a chat-style harness around a fine-tuned gpt-oss-20b-palette-lora. You describe a deck in plain English (or upload reference docs); a stage-1 planner drafts an editable markdown plan; the LoRA turns the plan into per-slide pptxgenjs JavaScript; a Node renderer + LibreOffice produce a .pptx and a PDF preview you can iterate on slide by slide.
request + refs plan.md
──────────────► Stage 1 ──────► Stage 2 ──────► Stage 3 ───► .pptx
(crafter) (designer (geometry
+ coder) detector +
repair loop)
You can:
- Draft from a request ("Build a deck about RAG architectures for engineers")
- Attach reference docs (PDF, DOCX, PPTX, Markdown) and have them shape the plan
- Start from an example plan that ships with the repo
- Edit the plan freely as Markdown, then build/regenerate
- Retry individual slides that came out poorly, or type instructions to refine one specifically
- Download the
.pptxwhen you're satisfied
The whole loop is clone → install → set key → run. Five to ten minutes the first time, two minutes on a warm machine.
Install once per machine. macOS instructions shown; equivalents work on Linux.
| Tool | Why | macOS install |
|---|---|---|
| Python 3.11+ | the app | brew install python@3.12 |
uv |
env + Python deps | curl -LsSf https://astral.sh/uv/install.sh | sh |
| Node 20+ | runs pptxgenjs (the renderer) |
brew install node |
| LibreOffice | converts .pptx → .pdf for inline previews |
brew install --cask libreoffice |
| Poppler | pdfplumber's backend (geometry detector) |
brew install poppler |
| IBM Plex (font family) | Palette's typography for IBM-styled decks | already ships on macOS — verify with fc-list | grep Plex |
On Debian/Ubuntu: apt install python3 python3-pip nodejs npm libreoffice poppler-utils fonts-ibm-plex. The Dockerfile (Dockerfile at the repo root) captures the full Linux setup if you'd rather not install locally.
git clone <this-repo-url>
cd project-palette
# Python: uv creates an isolated env in .venv and installs deps
uv venv
source .venv/bin/activate
uv pip install -r requirements.txt
# Node: just pptxgenjs
npm installOne-liner alternative:
make installrunspip install -r requirements.txt && npm install. Use this if you've already got an activated env.
Palette talks to IBM's RITS inference service. Export your bearer token before starting:
export RITS_API_KEY=<your key>Without this, the server starts fine but every build will fail at the first model call.
python app.py
# Palette -> http://127.0.0.1:18814Open the URL. You should see the Palette UI — a chat composer on the left, a deck preview area on the right.
Empty state. When you first land, the left pane shows an empty-state prompt. Three ways to start:
- Describe what you want in the composer at the bottom (e.g. "Build a deck explaining vector databases to backend engineers") and hit Send.
- Attach reference files via the 📎 button next to the composer, then describe how to use them.
- Start from an example plan — click that button to pick from
reference_plans/(RAG, all-hands, etc.). Loads a pre-built plan you can edit.
Plan editing. Once a plan exists it shows as rendered Markdown in the chat thread. Click Edit in the plan card's header to switch to a textarea, make changes, and click Done. Then click Build deck (the orange button at the bottom of the plan card).
While the deck builds. Status messages stream into the chat — designer running, slides coding, repair loop, ready. A 10–15 slide deck takes ~2–4 minutes on the default models.
Iterating. When the deck is built:
- Click any slide thumbnail at the bottom of the preview pane to view it large
- Hit ↻ Retry this slide (the orange button below the slide image) to re-roll just that one slide at a small temperature variation, then re-run the geometry repair on it
- Or type a specific instruction in the chat — "make slide 5 a table", "swap the order of bullets 2 and 3" — for surgical edits
- Tweak the plan markdown and hit Regenerate deck for whole-deck changes
Download. When the deck looks right, click the Download button in the header to get the .pptx.
Other controls in the header:
- ⚙ Models — pick the model for each pipeline stage (planner / designer + coder / critic). Defaults are sensible.
- Tips — opens an in-app cheat sheet of all the above
- Load plan — load any
.mdfile as a plan directly, skipping the planner - New — start a fresh session
palette.py exposes the same pipeline as importable functions and a CLI, so an
agent or script can drive Palette without running the server. The web UI above
is unaffected. Three commands, used as a loop — build a plan → confirm →
edit → build the deck:
# 1. plan from a request (add --context "<pasted material>" to ground it)
python palette.py build-plan "Build me a 5-slide deck on RAG" --out plan.md
# 2. revise the plan (repeat as needed)
python palette.py edit-plan "Make it 3 slides and use a casual tone" --plan plan.md --out plan.md
# 3. render the deck -> writes deck.pptx into the out dir and prints its path
python palette.py build-deck --plan plan.md --out-dir ./my_deckAdd --json to any command for a {"ok": true, ...} envelope. Or import directly:
from palette import build_plan, edit_plan, build_deck
plan = build_plan("Build me a 5-slide deck on RAG")
plan = edit_plan(plan, "Make it 3 slides and use a casual tone")
result = build_deck(plan, out_dir="./my_deck") # {"deck", "pptx", "previews", ...}For agent / skill integration, SKILL.md documents the commands and the
confirm-before-build workflow.
If you'd rather not install Python / Node / LibreOffice on your machine:
docker build -t palette .
docker run --rm -p 8080:8080 -e RITS_API_KEY=$RITS_API_KEY palette
# -> http://localhost:8080The Dockerfile installs everything Palette needs — Python, Node, LibreOffice, Poppler, fonts (IBM Plex, Inter, JetBrains Mono, Carlito). ~1.3 GB image, ~5 minutes to build the first time.
| Env var | Required | Default | What |
|---|---|---|---|
RITS_API_KEY |
yes | — | Bearer token for RITS. Without it, builds fail at the first model call. |
PORT |
no | 18814 |
Bind port. Useful in container deployments where the platform injects a port. |
RITS_BASE_URL |
no | (set in config.py) |
Override only if pointing at a non-default RITS endpoint. |
app.py FastAPI app, routes, per-session logging
ui.py Single-file HTML/CSS/JS frontend (served at /)
config.py Paths, model roster, RITS client config
intake.py Stage 1 — crafter: request + refs → plan.md
pipeline.py Stage 2 — designer + coder: plan.md → per-slide JS
render.py Node + pptxgenjs renderer; LibreOffice → PDF previews
detector.py Geometry defect detector (pdfplumber-based, no model)
refine.py Stage 3 — detector + editor + verify-gate repair loop
llm.py RITS HTTP client (custom RITS_API_KEY header)
session.py Per-thread session state
harness_prompts.py System prompts for crafter / critic / editor
prompts.py SFT designer + coder prompts (the LoRA's contract)
postprocess.py Deterministic JS fixes (deck.json normalisation, etc.)
reference_plans/ Example plans (also serve as crafter exemplars)
assets/ Logo, favicons, IBM brand assets used by the renderer
icons/carbon/ Carbon icon library
workspace/ Per-session decks (gitignored, ephemeral)
See LICENSE.