Skip to content

Add assembly webhooks listen command for local webhook development#110

Merged
alexkroman merged 2 commits into
mainfrom
claude/dreamy-fermat-bg3ltl
Jun 12, 2026
Merged

Add assembly webhooks listen command for local webhook development#110
alexkroman merged 2 commits into
mainfrom
claude/dreamy-fermat-bg3ltl

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

Summary

Adds a new assembly webhooks listen command that binds a local HTTP server to receive AssemblyAI webhook deliveries, exposes it via a cloudflared quick tunnel for public access, and prints each delivery as it arrives. This enables local webhook development without deploying a public endpoint.

Key Changes

  • New webhook_listen module (aai_cli/webhook_listen.py): Core engine for the webhook listener

    • shape_event(): Parses incoming webhook bodies and pulls up transcript_id/status fields for AssemblyAI webhooks
    • forward(): Re-POSTs deliveries to a local app via --forward-to, with failures reported on the event rather than to AssemblyAI
    • _EventSink: Serializes concurrent handler threads into one printed record per delivery, with --max-events support
    • run_listen(): Orchestrates the HTTP server, tunnel setup, and delivery loop
  • New webhooks command (aai_cli/commands/webhooks.py): CLI interface with options for:

    • --port: Local listener port (default 8989)
    • --forward-to: Optional URL to relay deliveries to a local app
    • --no-tunnel: Local-only mode (skip cloudflared)
    • --max-events: Exit after N deliveries (0 = run until Ctrl-C)
    • --json: NDJSON output mode
  • Enhanced tunnel infrastructure (aai_cli/init/tunnel.py):

    • require_cloudflared(): Centralized missing-dependency check with platform-specific install hints
    • open_quick_tunnel(): Spawns cloudflared process, captures tunnel URL and logs, strips API key from environment
    • install_hint(): Platform-aware installation guidance (brew on macOS, docs link elsewhere)
  • Refactored share command (aai_cli/commands/share.py): Moved cloudflared dependency logic to tunnel module to avoid duplication

  • Batch LLM support (aai_cli/transcribe_batch.py): Extended batch mode to support --llm chains

    • Per-source LLM chains run after transcription and land in each sidecar's transform key
    • Chain is resumable: re-runs with missing/changed prompts replay just the LLM step against the recorded transcript ID
    • _transform_satisfied(): Detects when a sidecar already records the exact chain (same prompts + model)
    • _resume_one(): Finishes sources with completed transcriptions, optionally replaying the chain
  • Comprehensive test coverage (tests/test_webhook_listen.py, tests/test_transcribe_batch_llm.py):

    • Real loopback server tests with background POST threads and pytest-timeout bounds
    • Event shaping, forwarding, and tunnel wiring tests
    • Batch LLM chain resumption and replay scenarios
    • Forward failure handling (non-fatal, reported on event)
  • Updated help text and examples: Added webhook examples to transcribe command, updated batch mode documentation to mention LLM resumption

Notable Implementation Details

  • Deliveries are acknowledged with HTTP 200 immediately before printing/forwarding, ensuring AssemblyAI doesn't retry
  • Forwarding failures are captured as event data, never exceptions — the delivery was already acknowledged
  • The tunnel process has its API key stripped from the environment to keep secrets out of cloudflared logs
  • Batch LLM chains are anchored to recorded transcript IDs, enabling cost-efficient retries on chain failures
  • Tests use @pytest.mark.allow_hosts(["127.0.0.1"]) to permit loopback binding while blocking external network

https://claude.ai/code/session_01VLyrC2KRm8hbptLatHeFSh

Two features that close gaps the solutions org keeps rebuilding by hand:

- `assembly transcribe <dir|glob> --llm "prompt"` now works in batch mode:
  the prompt chain runs per source once its transcription is recorded,
  landing under the sidecar's `transform` key. The chain resumes on its
  own — a failed gateway call leaves a resumable transcription, and a
  re-run (or changed prompts/--model) replays just the LLM step against
  the recorded transcript id, never a second transcription.

- `assembly webhooks listen` (stripe-CLI style): binds a local sink,
  exposes it via a cloudflared quick tunnel, prints each delivery
  (NDJSON under --json), and can re-POST bodies to a local app with
  --forward-to. --no-tunnel, --max-events, and a --port floor cover
  local/scripted use. The cloudflared plumbing (require/install hint,
  quick-tunnel spawn with API-key-stripped env, terminate) moved from
  the share command into aai_cli/init/tunnel.py for reuse.

https://claude.ai/code/session_01VLyrC2KRm8hbptLatHeFSh
@alexkroman alexkroman enabled auto-merge (squash) June 12, 2026 14:05
@alexkroman alexkroman merged commit 92478f2 into main Jun 12, 2026
16 checks passed
@alexkroman alexkroman deleted the claude/dreamy-fermat-bg3ltl branch June 12, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants