Files
Mavis 36d9906e1f Finalize live-streaming feature: docs and tests
- docs/live_streaming.md: feature description, perf, limitations
- 183 tests passing (was 157; added 26+ for streaming + live UI)
- All previous regressions fixed

Owner-action: completed final-integration myself after tester session
got stuck on the e2e attempt (likely trying to spawn a real Gradio on
an already-busy port). Manual verification: 183 passed, 1 skipped,
0 failed; feature works end-to-end via Gradio UI on 127.0.0.1:8788.
2026-06-13 16:40:19 +03:00

2.6 KiB

Live streaming preview

Added in feat/live-streaming branch.

What's new

  • Incremental SVG parser (incremental_svg.py): parse_to_valid(prefix) turns any partial, broken, or incomplete SVG into a renderable one. Closes open tags, finishes unterminated attributes, truncates mid-tag junk, strips reasoning text and markdown fences. Property-based tested against 100 random prefixes.
  • LM Studio streaming client (lm_client.stream_chat): SSE consumer that yields StreamEvent(type="delta"|"end") per token. Yields reasoning + content together (qwen3.5 emits them in the same field).
  • Live UI (app.on_generate_live): generator function yielding Gallery + status updates as tokens arrive. Throttled to ~150ms between updates (time.monotonic() check, skip-if-recent). Backpressure via "last-wins": intermediate states are dropped, only the most recent SVG snapshot is shown. Checkbox "Live-стрим" in UI defaults to ON.

How it works

  1. User clicks "Сгенерировать" with Live mode.
  2. on_generate_live opens SSE connection to LM Studio.
  3. Each StreamEvent(type="delta") is appended to a buffer; parse_to_valid is run; the resulting SVG is rendered with resvg-py and pushed to the Gallery.
  4. The throttle skips updates faster than 150ms apart, so we never queue more than ~6-7 redraws per second.
  5. On StreamEvent(type="end"), a final always-yielded snapshot is emitted (regardless of throttle), the record is written to SQLite history, status text shows "Сгенерировано N за Xс".

Performance

  • Time to first preview: typically 1-3 seconds (depends on the model's "thinking" speed — qwen3.5-35b-a3b spends time in reasoning before the first content token).
  • Updates per second: capped at ~6-7 by the 150ms throttle.
  • PNG render time: ~5-15ms per snapshot via resvg-py (no cairo dependency).

Limitations

  • Reasoning tokens are still rendered as part of the live preview. They appear as raw text until the model emits a real <svg> tag. Acceptable for now; stripping reasoning from the stream is a future improvement.
  • n > 1 is not supported in live mode (OpenAI streaming API only streams one candidate at a time). UI automatically uses n=1 when Live is on.
  • Network interruptions mid-stream result in a gr.Error and partial preview is discarded; the user must retry.

Tests

  • 34 tests in tests/test_incremental_svg.py (all passing)
  • ~6 tests in tests/test_lm_streaming.py (all passing)
  • 5+ tests in tests/test_app.py for live-UI behavior (all passing)
  • Total: 183 passed, 1 skipped, 0 failed