Overview
The RAG (Retrieval Augmented Generation) demo is Chirp's most comprehensive example. It showcases fragments, Server-Sent Events, dual-model streaming, event delegation, and free-threading — all with zero client-side JavaScript frameworks.
Location:examples/rag_demo/
What It Demonstrates
| Feature | How |
|---|---|
| Fragments | Fragment("ask.html", "answer", ...)streams HTML blocks via SSE |
| SSE | EventStream yields fragments; htmx swaps into sse-swaptargets |
| Dual streaming | Compare two models side-by-side; each streams independently (free-threading) |
| Event delegation | Copy button and compare toggle usedocument.addEventListener because hx-ondoes not run on SSE-swapped content |
| chirp.data | SQLite with typed frozen dataclasses for document storage |
| chirp.ai | Streaming LLM via Ollama (default) or Anthropic |
| referenced routes | /share/{slug} and /ask/stream use referenced=True so chirp checkdoes not flag them as orphan |
| chirp-ui | Layout macros, cards, badges, alert;use_chirp_ui(app) + chirp_ui.register_filters(app) |
Key Patterns
SSE Swap Target Structure
- Outer
.answer—sse-swap="answer"target withhx-target="this"; has padding and border - Inner
.answer-body—data-copy-text; no extra padding (avoids double borders) - Content
.answer-content.prose— markdown-rendered answer
Usehx-disinherit="hx-target hx-swap" on sse-connect and hx-target="this" on each sse-swapelement. See SSE patterns for the multi-swap layout.
Copy Button
Keep.copy-btn in normal flow — avoid position: absolutein your CSS so each button stays anchored to its answer. In compare mode, each card has its own copy button.
Event Delegation
hx-on::click does not work on content swapped by htmx. The RAG demo uses AppConfig(delegation=True), which injects a document-level listener for .copy-btn and .compare-switch:
document.addEventListener('click', function(e) {
var copyBtn = e.target.closest('.copy-btn');
if (copyBtn) {
var wrap = copyBtn.closest('[data-copy-text]');
if (wrap) navigator.clipboard.writeText(wrap.dataset.copyText || '');
}
});
Compare Toggle
Arole="switch" button toggles aria-checked and enables/disables the second model selector. The form's input[name=compare]stays in sync for server-side handling.
Run
pip install chirp[ai,data]
ollama serve # Start Ollama first
cd examples/rag_demo && python app.py
Open http://127.0.0.1:8000 and ask a question about the docs.
Chirp Macros
For the standard answer structure (body + prose + copy button), use thesse_answermacro:
{% from "chirp/sse_answer.html" import sse_answer %}
{{ sse_answer(text, text | markdown | cite(sources) | safe(reason="patitas")) }}
The RAG demo uses custom logic for streaming states; the macro suits the final "done" state.
Next Steps
- SSE patterns — Multi-swap layout, hx-target, compile-time checks
- Server-Sent Events — SSE in depth
- Fragments — Block-level rendering
- htmx Patterns — Event delegation and more