Streaming Long AI Jobs to the Browser: SSE Patterns From Building an Audit Tool
Unpacking the lessons of building an AI-powered contract audit tool that leverages Server-Sent Events to solve user drop-offs during multi-minute processing tasks.

Stock photo for illustration only, not from the actual event
- Replaced a spinning loader with Server-Sent Events for real-time one-way data streaming.
- Designed a fixed vocabulary of typed events spanning four distinct categories for stability.
- Decoupled background worker processes from the streaming channel to handle disconnection issues.
- Managed event emission frequency and backpressure to prevent server-side memory leaks.
The first version of the spectr-ai web frontend had a spinner. You uploaded a contract, the spinner spun, and several minutes later results appeared, or didn't. My test users all did the same thing: around the ninety-second mark they refreshed the page, killing the audit that was about to finish. A spinner with no progress is indistinguishable from a hang, and users act accordingly.
Consequently, the pipeline was rebuilt around Server-Sent Events, where most of the learned insights did not come from standard tutorials. Most tutorials only cover streaming a chat completion for ten seconds and calling it a day, whereas multi-minute jobs with real state present an entirely different set of challenges. Here is what actually mattered.
When handling long-running jobs in modern web applications, developers must look beyond basic tools designed for brief requests, such as traditional HTTP requests that time out or terminate when limits are reached. Decoupling the worker process from the SSE stream architecture is crucial for maintaining durability and resilience against real-world connection drops.
In short, the browser never needs to talk back mid-job. The client uploads a contract, then listens. That is precisely the shape SSE was built for, providing one-directional server-to-client communication over plain HTTP without connection upgrades or socket lifecycle management, while the browser's EventSource provides automatic reconnection for free as its most valuable protocol feature. While WebSockets would certainly work, they would require maintaining bidirectional machinery just to utilize ten percent of their capabilities.

Stock photo for illustration only, not from the actual event
The initial attempt streamed whatever the pipeline felt like emitting, including raw model tokens, log lines, and half-thoughts, which turned the frontend into a parser for an undocumented format that kept changing whenever the backend was touched.
A second attempt introduced a fixed vocabulary of typed events treated as a real API contract, powered by four core types covering everything:
- progress: Carries semantic steps rather than percentages, keeping users engaged through minute four with explicit messages.
- partial-finding: Serves as the primary retention feature by shipping individual findings the moment they are validated.
- error: Distinguishes recoverable failures from fatal ones to determine whether a retry is warranted.
Laptops sleep, phones switch networks, and proxies kill idle connections. Over several minutes, disconnection is a statistical certainty at scale, and EventSource handles automatic reconnection by sending a Last-Event-ID header containing the last received event.
Source: Dev.to
Found something wrong in this article? Report an issue with this article
Comments
Leave a Comment