RAG Demo

Streaming AI Q&A with cited sources — Chirp's flagship example

2 min read 425 words

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.answersse-swap="answer" target with hx-target="this"; has padding and border
  • Inner.answer-bodydata-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