A curl-like viewer that renders SSE / OpenAI-style event streams for humans
Project description
sseurl
A curl-like viewer that renders SSE (text/event-stream) streams for humans.
curl -N shows you a wall of data: {...} JSON. sseurl parses the wire
format, auto-detects what kind of stream it is, and renders it readably:
accumulated markdown for LLM output, aligned colored log lines for job event
streams, syntax-highlighted JSON for everything else — plus a live tail while
streaming and an end-of-stream summary (event count, bytes, duration, time to
first event, tokens).
Install
From PyPI:
uv tool install sseurl
# or: pipx install sseurl
On internal Debian trixie machines, install from the private APT repository. The repository keyring is provided by the repo admin.
sudo tee /etc/apt/sources.list.d/hashtable.sources >/dev/null <<'EOF'
Types: deb
URIs: https://repo.hashtable.io/apt/debian
Suites: trixie
Components: main
Architectures: amd64 arm64
Signed-By: /usr/share/keyrings/archive-keyring.gpg
EOF
sudo apt-get update && sudo apt-get install sseurl
From a checkout:
uv tool install .
# or: pipx install .
Requires Python ≥ 3.10. Dependencies: httpx, rich.
Build your own Debian package
dpkg-buildpackage -us -uc -b
sudo apt-get install ./sseurl_0.1.0_all.deb
Usage
Fetch a stream directly (curl-style flags):
sseurl https://gateway.example.com/v1/jobs/job_x/events/stream \
-H 'Authorization: Bearer TOKEN'
sseurl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" -d @request.json
Or keep your existing curl command and pipe into it (- = stdin):
curl -sN https://api.example.com/stream | sseurl -
It also reads saved dumps: sseurl dump.sse.
Rendering profiles
Detected automatically from the first event; force one with --profile.
| Profile | Detected by | Rendering |
|---|---|---|
openai |
chat.completion.chunk / choices[].delta |
accumulates delta.content into live markdown; reasoning deltas dim; tool_calls assembled per index and shown with pretty-printed args; usage/finish_reason in summary |
responses |
response.* event names |
accumulates response.output_text.delta; function-call args assembled; usage from response.completed |
gateway |
JobEvent frames (job_id + seq) |
one aligned line per event: seq, time, colored stream (stdout/stderr/system), level, type; known adapter payloads get semantic rendering — codex command_execution as ⏵ $ cmd with exit mark and clipped output, agent_message as a markdown panel, turn_completed as a token-usage line, claude assistant/tool_use/result similarly; unknown payloads inline-compact or clipped highlighted JSON (full with --raw); gap frames as red warning panels |
generic |
anything else | event name + syntax-highlighted JSON (or plain text) |
Keepalive comments (: keepalive) are shown dimmed, so you can tell an idle
connection from a dead one.
Options
-X, --request METHOD HTTP method (default GET, or POST when -d is given)
-H, --header 'K: V' request header, repeatable
-d, --data DATA request body; @file reads a file, @- reads stdin
-k, --insecure skip TLS verification
--timeout SECS read timeout (default: wait forever)
--last-event-id ID send Last-Event-ID header
--profile P auto | openai | responses | gateway | generic
--raw also print each event's full JSON
--json-theme NAME render JSON with a pygments theme (dracula, monokai,
nord, github-dark, …); default is a built-in palette
--color force colors (overrides NO_COLOR and pipes)
--no-color disable colors
--events compact line per event in accumulating profiles
--no-md don't render accumulated text as markdown
--no-live disable the live tail region
--idle-exit SECS exit after SECS without a new event (keepalives don't
count) — for scripted watching of finite streams
-q, --quiet suppress the end-of-stream summary
Try it without a server
uv run sseurl tests/fixtures/openai-chat.sse
uv run sseurl tests/fixtures/gateway.sse
uv run python examples/demo_server.py & # serves fixtures over HTTP with delays
uv run sseurl http://127.0.0.1:8399/openai-chat
Development
uv sync
uv run pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sseurl-0.1.0.tar.gz.
File metadata
- Download URL: sseurl-0.1.0.tar.gz
- Upload date:
- Size: 35.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7760fa4295f688b30112d01c55e1e4236721dc9f6e18f702a19a2f6dfddc0868
|
|
| MD5 |
efe6056e9556fb6d8d55349105f3ce80
|
|
| BLAKE2b-256 |
4cad428724fb378c34618ff1f53eb9859bc4f29d80244d2e0fd739eac03ac2b0
|
File details
Details for the file sseurl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sseurl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6719b15d66b69ecaa24d7fc2b5a7846eac06127a8312a2fa7a71ee4ccb952b1
|
|
| MD5 |
50e9bff3cbd8150c2b8555cf292543f8
|
|
| BLAKE2b-256 |
c9fd80ef7c3e7b2f2d3d99ce1c305415055b0de25f0a4a29935f8d4a3ec78972
|