Minimal TypeScript browser demo for API-key authenticated completion streaming against Bag of Words.
The app validates a bow_... API key, loads accessible agents, creates or updates a report with selected data sources, streams POST /api/reports/{report_id}/completions, and shows both a clean reduced view and raw SSE events. The CURL view focuses on the portable two-call flow: create a report, then stream a completion against it.
npm install
npm run devOpen the Vite URL, usually:
http://127.0.0.1:5173/
Use a BOW URL without /api, for example:
http://localhost:3000
https://bow.example.com
- BOW URL is normalized to
{BOW_URL}/apiinternally. - API key is sent as
Authorization: Bearer bow_.... - Agents load automatically once the BOW URL and API key are available.
- No report ID creates a scratch report with the selected agent IDs in
data_sources. - Existing report ID streams into that report.
- Changing agent selection after a report ID exists updates that report with
PUT /api/reports/{report_id}. - Stop aborts the streaming
fetchrequest withAbortController. - UI / CURL toggles between the interactive UI and dynamic curl commands for report creation and completion streaming.
- Copy in CURL mode copies the generated commands.
The app persists BOW URL and report ID in localStorage. API keys are only persisted if "Remember key on this browser" is enabled.
Create a scratch report:
POST /api/reports
Authorization: Bearer bow_...
Content-Type: application/json
{
"title": "Completion SSE API Demo",
"data_sources": ["agent-or-data-source-id"]
}Stream the completion:
POST /api/reports/{report_id}/completions
Authorization: Bearer bow_...
Content-Type: application/json
Accept: text/event-stream
{
"prompt": {
"content": "Summarize this report",
"mentions": [],
"mode": "chat"
},
"stream": true
}The stream is parsed as standard SSE:
event: completion.started
data: {...}
data: [DONE]
Bag of Words wraps event data with format_sse_event, so the parser uses:
const parsed = JSON.parse(dataStr);
const payload = parsed.data ?? parsed;Clean View handles the common events:
completion.started
block.upsert
block.delta.token
block.delta.text
decision.partial
decision.final
tool.started
tool.progress
tool.finished
completion.finished
completion.error
llm.error
Live Events keeps the raw SSE frames available for full fidelity.
index.html
package.json
src/
bowClient.ts HTTP helpers and streaming fetch
sseParser.ts Tiny SSE parser
cleanReducer.ts Simplified stream-to-UI reducer
main.ts DOM UI and app state
styles.css Minimal styling
Do not expose API keys in public browser apps. This example is appropriate for local demos and trusted internal tools. Public integrations should proxy Bag of Words calls through your own backend and keep API keys server-side.
- Valid key loads agents automatically.
- No report ID creates a scratch report and streams.
- Existing report ID streams into that report.
- Changing selected agents with a report ID updates the report.
- CURL mode reflects current URL, prompt, and selected agents.
- Copy in CURL mode copies the generated commands.
- Invalid key shows
401. - Bad BOW URL shows a network/CORS error.
- Stop aborts without crashing.
- Live Events shows event names, raw frames, and payloads.
- Clean View accumulates
block.delta.tokenandblock.delta.text. - Stream ends on
data: [DONE].