Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sseurl-0.1.0.tar.gz (35.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sseurl-0.1.0-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

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

Hashes for sseurl-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7760fa4295f688b30112d01c55e1e4236721dc9f6e18f702a19a2f6dfddc0868
MD5 efe6056e9556fb6d8d55349105f3ce80
BLAKE2b-256 4cad428724fb378c34618ff1f53eb9859bc4f29d80244d2e0fd739eac03ac2b0

See more details on using hashes here.

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

Hashes for sseurl-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d6719b15d66b69ecaa24d7fc2b5a7846eac06127a8312a2fa7a71ee4ccb952b1
MD5 50e9bff3cbd8150c2b8555cf292543f8
BLAKE2b-256 c9fd80ef7c3e7b2f2d3d99ce1c305415055b0de25f0a4a29935f8d4a3ec78972

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page