DEV Community

Alex Chen
Alex Chen

Posted on

Why a Free LLM Server Drops the Last Streamed Chunk: A Tiny SSE Recorder

Most streaming LLM failures are not model failures; they are chunk-passing failures between a server and a client.

Current feeds are full of free model endpoints and free server tiers. The useful skill is not trying every new model. The useful skill is proving where a stream breaks. A recent DEV bug-smash challenge asked contributors to restore a dropped chat config in an SDK. That is the same class of silent stream loss this recorder catches.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

What you will build

A two-file SSE recorder:

  • slow_sse_server.py fakes a slow stream.
  • sse_recorder.py prints raw chunks.
  • optional: run the recorder against a free MonkeyCode server/model endpoint to see the same contract.

No third-party packages. Python 3.11 or newer.

Prerequisites

  • Python 3.11+
  • two terminals
  • 20 minutes
  • optional: a free MonkeyCode endpoint with its operator-reported 30,000,000-token allowance

Step 1: fake the slow server

Create slow_sse_server.py:

import http.server
import json
import os
import time

CHUNKS = [
    {'choices': [{'delta': {'content': 'Hello'}}]},
    {'choices': [{'delta': {'content': ' from'}}]},
    {'choices': [{'delta': {'content': ' the'}}]},
    {'choices': [{'delta': {'content': ' server.'}}]},
    {'choices': [{'delta': {}, 'finish_reason': 'stop'}]},
]

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-Type', 'text/event-stream')
        self.send_header('Cache-Control', 'no-cache')
        self.end_headers()
        for payload in CHUNKS:
            line = 'data: ' + json.dumps(payload) + os.linesep + os.linesep
            self.wfile.write(line.encode())
            self.wfile.flush()
            time.sleep(0.2)
        self.wfile.write(('data: [DONE]' + os.linesep + os.linesep).encode())
        self.wfile.flush()

if __name__ == '__main__':
    http.server.HTTPServer(('127.0.0.1', 8899), Handler).serve_forever()
Enter fullscreen mode Exit fullscreen mode

Step 2: record what the client actually sees

Create sse_recorder.py:

import sys
import urllib.request

URL = sys.argv[1] if len(sys.argv) > 1 else 'http://127.0.0.1:8899/v1/chat/completions'

req = urllib.request.Request(URL, headers={'Accept': 'text/event-stream'})
with urllib.request.urlopen(req, timeout=10) as resp:
    print('status', resp.status)
    print('headers', dict(resp.headers))
    event = []
    for raw in resp:
        line = raw.decode('utf-8').rstrip()
        if line == '':
            if event:
                print('event', event)
                event = []
            continue
        if line.startswith(':'):
            print('comment', line)
            continue
        if line.startswith('data:'):
            event.append(line[5:].strip())
    if event:
        print('trailing', event)
Enter fullscreen mode Exit fullscreen mode

Run it:

python slow_sse_server.py
Enter fullscreen mode Exit fullscreen mode

In a second terminal:

python sse_recorder.py
Enter fullscreen mode Exit fullscreen mode

Expected output:

status 200
headers contain text/event-stream
event count 5
final event [DONE]
Enter fullscreen mode Exit fullscreen mode

Read the raw output before trusting a client

Check these five signals before you wrap a free server with an SDK:

  • number of data: events received
  • presence of finish_reason
  • presence of [DONE]
  • blank lines between events
  • any trailing event after the loop

A client abstraction can hide any of these. The recorder shows what was actually sent on the wire.

Where this breaks

Create not_sse_server.py:

import http.server
import json

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        body = json.dumps({'choices': [{'delta': {'content': 'Hello'}}]})
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body.encode())

http.server.HTTPServer(('127.0.0.1', 8898), Handler).serve_forever()
Enter fullscreen mode Exit fullscreen mode

Run it and point the recorder at it:

python not_sse_server.py
python sse_recorder.py http://127.0.0.1:8898/v1/chat/completions
Enter fullscreen mode Exit fullscreen mode

Expected failure:

status 200
headers contain application/json
event count 1
no [DONE]
Enter fullscreen mode Exit fullscreen mode

There is no blank line and no [DONE]. If your client expects an SSE delta, it may return an empty string, hang, or drop the final assistant message.

This is the same failure mode as a proxy that buffers a stream or strips newlines.

Fix checklist for a free server

  • send Accept: text/event-stream
  • disable output buffering if you control the proxy
  • read until EOF, not until the first blank line
  • print raw lines before parsing JSON
  • verify the final data: [DONE] or finish_reason

Where MonkeyCode fits

I use this recorder as a smoke test before I trust any free model endpoint. The operator reports that MonkeyCode offers free model access, a free server option, and a 30,000,000-token allowance.

Use it for:

  • small stream-contract tests
  • comparing one model response path
  • reproducing a dropped-chunk bug

Do not use it for:

  • production traffic
  • latency or quality benchmarks
  • assumptions about permanence; verify the current quota on the product page

Common mistakes

  • parsing only the first data: event
  • treating [DONE] as JSON
  • closing the connection after one blank line
  • ignoring response.status and response.headers

Extension

Change sse_recorder.py to record a timestamp and byte length for every chunk. Then compare the timestamps against your free server. If the first chunk arrives after a long delay, the problem is upstream buffering, not your parser.

If you try this, post the raw event count you observe before and after adding the Accept header.

Top comments (0)