Small, self-contained single-page demos of the Algolia Agent Studio context APIs. No build step, no framework, no bundler — every demo is plain HTML, CSS and JavaScript served as static files.
Live at https://agent-studio-demos.pages.dev/, one demo per path. Every push to main deploys it.
| Demo | Path | Status |
|---|---|---|
| Chat with a book | /chat-with-book/ |
Live |
| Infinite conversation | /infinite-conversation/ |
Live |
public/ the deployable root — this is what Cloudflare Pages serves
index.html landing page, one card per demo
shared/ the demo kit — see CONTRIBUTING.md § The demo kit
tokens.css the design language: colors, fonts, spacing, base primitives
md.js vendored sanitizing markdown renderer (window.renderMarkdown)
meter.js the cost strip: cost model, mode switch, rendering (window.DemoMeter)
meter.css the cost strip's styling, linked after tokens.css
books.js the bookshelf manifest and its suggestion chips (window.DEMO_BOOKS)
compactor.js the auto-compact loop, as a stateless driver (window.DemoCompactor)
config.example.js template for local credentials — copy it, never edit it in place
config.js your real credentials (gitignored, never committed)
assets/texts/ 25 public-domain works, plain text, source boilerplate removed
assets/convs/ three seeded conversations, plus the manifest every figure derives from
chat-with-book/ index.html + app.js + style.css
infinite-conversation/ index.html + app.js + style.css
scripts/ node, zero dependencies — see § The shelf and § The search index
fetch-books.js download, strip and count the texts, from either source
wikisource.js wikitext → prose, for the one work not on Gutenberg
build-passages.js cut the texts into passages → passages.jsonl
index-passages.js apply settings and push one language's passages (dry run by default)
index-settings.json the index settings, as reviewable data
index-settings-languages.json the per-language overlay on those settings
measure-tokens.js characters per token, per book, via /context/trim
tools/ node, zero dependencies — bakes the seeded conversations
tests/ node:test smoke tests — no framework, no install
eslint.config.js flat config, rules written out, zero dependencies
.github/workflows/ ci.yml (lint + tests), deploy.yml (Cloudflare Pages)
Each demo links ../shared/tokens.css first, then any shared stylesheet it uses (meter.css), then its own style.css. A demo's stylesheet never redeclares a token; the landing page uses nothing but tokens.css.
Sixteen books are committed under public/assets/texts/ as plain text, from The
Yellow Wallpaper at 6,085 words to War and Peace at 563,286. All sixteen are
Project Gutenberg ebooks in the public domain in the
United States; each file is Gutenberg's own text with the licence header and footer
removed and nothing else edited, so any claim a demo makes about a book can be
checked against the file. scripts/fetch-books.js is that download-and-strip step
written down — run it with --force and it re-derives all sixteen files and reports
them identical.
Nine more are shelved separately, under In the original: works in French,
German, Spanish, Italian, Russian, Japanese and Chinese, none of them a
translation. Eight of the nine are Gutenberg too; Белые ночи is not there as
plain text at all and comes from ru.wikisource.org, with the editorial layer its
licence covers stripped by scripts/wikisource.js. Each language gets its own
Algolia index, because indexLanguages is a settings-global and CJK segmentation
only happens when the index declares the language — 紅樓夢 cannot share one with
Faust. The search tool is bound to all eight, and the Algolia MCP server turns
that into one tool per index, so the model picks a language by picking a tool.
Their suggested questions are in the book's own language, and the page's chrome
follows the reader's: eight languages in the masthead picker, and the choice rides
on every request as a short note asking the model to answer in it.
charsPerToken is measured per book, not per language, through /context/trim
(scripts/measure-tokens.js). English alone runs 2.98 on Alice to 4.02 on The
Time Machine — a 35% spread inside one tongue — and Japanese runs about 0.32,
three tokens to the character. It is not a rounding detail: one shared 3.0 was
folding the Arabian Nights, which at its own 3.95 fits the window whole.
The shelf is grouped by what loading a book will do, and each tile says so before you click it:
| Regime | What happens | On the shelf |
|---|---|---|
| budget-compact | fits the model window whole, exceeds the working budget, so one summarizer call on the first question | the nine smaller books |
| oversize-fold | past 0.8 × the model's real window, so summarized in N parts on arrival, then compacted as you chat | the seven larger books |
| cost-gated | an estimate big enough to be worth a decision, so it asks before it spends | model-dependent — none at the default rate |
None of that is written down per book. window.DEMO_BOOKS.estimate(book, opts)
derives it from the book's character count and the config in force: the model's
window decides what folds, its price decides what a fold costs, the budget decides
where compaction fires, and the token count comes from a chars-per-token ratio the
page re-measures on every trim probe. Change the model or the budget and the groups
rearrange.
Ingesting a big book costs real money. The summarizer reads every word once, on your own provider credentials, and that is a per-click cost rather than a one-off: War and Peace is about 941k tokens through the fold. At the default model's rate that is roughly nine cents and at ten times the rate it is ninety-four, so each tile carries its own estimate and the expensive ones ask first. The figures are estimates — the API does not report the summarizer's own token usage — and where the rate behind one is a placeholder rather than a published price, the tile says so.
# 1 · credentials — copy the template, then fill in your own values
cp public/shared/config.example.js public/shared/config.js
$EDITOR public/shared/config.js
# 2 · serve the deployable root (any static server works)
python3 -m http.server 8766 --bind 127.0.0.1 --directory public
# 3 · open it
# http://127.0.0.1:8766/Open a file over http://, not file:// — the demos load their config and shared modules with relative paths, which a file:// origin blocks.
public/shared/config.js defines a single global, window.DEMO_CONFIG, read once at startup. It carries:
host,appId,apiKey— which Agent Studio deployment to call and with what credentials.models— the entries offered in the model picker: an agent id, a provider id, the model name, its context window, and a per-million-token price used by the cost meter.budgets,defaultBudget,compactAtRatio,keepLastMessages— the working budget the meter fills against, and when auto-compaction fires.- Optional tuning for hierarchical folding — chunk size, fold depth, unfold limits. Everything has a default in
app.js, so an older config keeps working.
config.example.js documents every field. It is committed; config.js is in .gitignore at every depth and must never be committed.
These pages call the API directly from the browser so the wire log can show you every request. That means the key is visible to anyone who opens devtools. Use a key scoped to exactly what the demo needs, and put a backend in front of it before shipping anything like this.
Two gates, both runnable verbatim on a laptop with nothing installed:
npx eslint public/ tests/ eslint.config.js # flat config, rules written out by name
node --test tests/*.test.js # node:test, no frameworkCI runs exactly these on every pull request and on main. Both are green on a clean
checkout with no config.js present — the tests read config.example.js, and eslint
ignores the real config — so a local run covers the same files as CI.
See CONTRIBUTING.md for the PR flow.
The site is static: public/ is the build output, there is no build command, and
.github/workflows/deploy.yml publishes it to Cloudflare Pages on every push to main
(or on demand, via Actions → Deploy → Run workflow).
public/shared/config.js is gitignored, so it is not in the repo and a plain checkout
cannot deploy a working site. A demo that loads without it does not break — it renders a
short notice telling the reader to create the file — but it does not demo anything either.
So the workflow writes that file before uploading, from a repository variable.
What must exist on the repo for a deploy to succeed:
| Kind | Name | What it holds |
|---|---|---|
| Secret | CLOUDFLARE_API_TOKEN |
Cloudflare token, Edit Cloudflare Workers template. Dashboard → My Profile → API Tokens → Create Token. |
| Variable | DEMO_CONFIG_JS |
The entire contents of a working public/shared/config.js. |
The account id (CLOUDFLARE_ACCOUNT_ID) is inline in the workflow — an account id
identifies, it does not authorise.
gh secret set CLOUDFLARE_API_TOKEN --repo algolia/agent-studio-demos
gh variable set DEMO_CONFIG_JS --repo algolia/agent-studio-demos < public/shared/config.jsOne variable, not one per field. The config is a nested structure — several model
entries of up to ten fields each, a reader-proxy object, a dozen fold-tuning scalars.
Reassembling that from ~40 variables would put a second copy of its shape inside the
workflow, and adding a model would mean editing the workflow. Holding the whole file
means the deployed config is identical to the one that already works locally; the cost is
that changing a config field means refreshing the variable, which is the gh variable set
line above.
It is a variable rather than a secret on purpose: the key inside is an intentionally
public, demo-scoped key that any visitor can read from devtools anyway, and secrets are
masked in logs — which makes a malformed config impossible to diagnose. The workflow
syntax-checks the file it writes and asserts host, appId, apiKey and at least one
model are present, so a truncated variable fails with a readable message instead of a
blank page.
Whatever key you configure ends up readable in the browser. Scope it accordingly.
You can still deploy by hand from a working copy — config.js is uploaded even though it
is untracked:
npx wrangler@4 pages deploy public/ --project-name agent-studio-demosmkdir public/<demo-slug>and write anindex.htmlthat links../shared/tokens.css.- Reuse the demo kit in
public/shared/rather than vendoring copies — the meter and the bookshelf are already shared; CONTRIBUTING.md documents the API of each module. - Add a card to
public/index.html— pitch, status, and the endpoints it exercises.
The context APIs these demos use (/1/unstable/context/trim, /1/unstable/context/compact) are unstable and can change without notice.