spacr.qt.ai.worker
QThread worker that streams chat completions from a ChatProvider so the UI stays responsive during long generations.
- Emits:
stage_changed(str) — coarse progress: “connecting”, “streaming” chunk_ready(str) — a partial completion chunk finished(bool, str) — (ok, full_text_or_error)
Module Contents
- class spacr.qt.ai.worker.StreamWorker(provider: spacr.qt.ai.providers.ChatProvider, messages: List[Dict], system: str = '', model: str | None = None)[source]
Bases:
PySide6.QtCore.QObjectQObject that drives one provider stream on a worker QThread.
- Variables:
stage_changed – coarse progress signal (“connecting”, “streaming”).
chunk_ready – emitted with each partial completion chunk.
finished – emitted with
(ok, full_text_or_error)on completion.
- cancel() None[source]
Cancel: kill the subprocess so the reader unblocks.
Setting a Python flag alone isn’t enough — the worker is blocked in a for line in proc.stdout iteration until the subprocess writes or closes. We terminate the subprocess directly via provider.cancel_stream(); the reader then exits with an empty read and run() completes cleanly.
- spacr.qt.ai.worker.make_stream_thread(provider: spacr.qt.ai.providers.ChatProvider, messages: List[Dict], system: str = '', model: str | None = None, parent: PySide6.QtCore.QObject | None = None) tuple[PySide6.QtCore.QThread, StreamWorker][source]
Return (QThread, StreamWorker) — connect signals, then start().
IMPORTANT: pass a parent (typically the panel that owns this stream). Without a Qt parent the QThread’s C++ object gets tied exclusively to Python’s refcount — and dropping the ref while QThread.isRunning() is still True (which happens in the tiny window between worker.run returning and thread.finished firing) triggers Qt’s QThread: Destroyed while thread is still running / Aborted crash. A parent keeps the C++ object alive until deleteLater runs.
Callers must ALSO keep a Python reference to the worker until the stream truly finishes (see ConsolePanel._retire).