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.QObject

QObject 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.

stage_changed[source]
chunk_ready[source]
finished[source]
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.

run() None[source]

Consume the provider stream, emitting stage/chunk/finished signals.

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).