spacr.qt.ai.providers

Provider abstraction — one class per AI vendor. Each shells out to the vendor’s own coding-agent CLI so authentication piggy-backs on the user’s chat subscription (Claude.ai Pro, ChatGPT Plus/Pro/Team, Google account) — no separate API billing.

  • Anthropic Claude → the claude CLI (“Claude Code”)

  • OpenAI ChatGPT → the codex CLI

  • Google Gemini → the gemini CLI

Each provider:

is_installed() — is the CLI on PATH? is_logged_in() — best-effort check; falls back to “assume yes if

installed” (the actual auth error surfaces on the first stream chunk).

stream_chat() — spawn the CLI subprocess, yield stdout chunks.

Conversation context is carried by concatenating the full message history into each prompt (simplest approach that works uniformly across all three CLIs). For subscription users token count is not a concern.

Classes

ChatProvider

Abstract base for AI chat providers that shell out to a vendor CLI.

ClaudeCliProvider

Anthropic Claude via the claude (Claude Code) CLI.

CodexCliProvider

OpenAI ChatGPT via the codex CLI.

GeminiCliProvider

Google Gemini via the gemini CLI.

Functions

configured_providers(→ List[ChatProvider])

Return only providers whose CLI is installed and logged in.

get_provider(→ Optional[ChatProvider])

Look up a registered provider by its short id.

list_providers(→ List[ChatProvider])

Return every registered provider, regardless of install state.

Module Contents

class spacr.qt.ai.providers.ChatProvider[source]

Bases: abc.ABC

Abstract base for AI chat providers that shell out to a vendor CLI.

Subclasses set the name/label/cli_name/install_hint/ login_command class attributes and implement stream_chat().

Variables:
  • name – short id (“claude” / “codex” / “gemini”).

  • label – human-readable label shown in the UI.

  • cli_name – executable expected on PATH.

  • install_hint – shell one-liner suggested for installation.

  • login_command – shell one-liner the user runs to authenticate.

cancel_stream() None[source]

Kill the running subprocess (if any).

This is the ONLY reliable way to unblock a stream that’s stuck waiting on stdout — flipping a Python flag would only unblock between chunks, which may never come.

is_configured() bool[source]

Return True when the CLI is both installed and logged in.

is_installed() bool[source]

Return True when the provider’s CLI executable is on PATH.

is_logged_in() bool[source]

Best-effort — override per provider if a cheap check exists.

Default: assume yes when installed. The real auth error will surface as a normal subprocess failure on the first send.

source_of_key() str[source]

Compat string for the old KeysDialog — now describes the CLI’s install/login state.

abstract stream_chat(messages: List[Dict], system: str = '', model: str | None = None) Iterator[str][source]

Yield text chunks streaming from the CLI subprocess.

cli_name: str = ''[source]
install_hint: str = ''[source]
label: str = ''[source]
login_command: str = ''[source]
name: str = ''[source]
class spacr.qt.ai.providers.ClaudeCliProvider[source]

Bases: ChatProvider

Anthropic Claude via the claude (Claude Code) CLI.

stream_chat(messages: List[Dict], system: str = '', model: str | None = None) Iterator[str][source]

Stream a chat completion from the claude CLI.

Parameters:
  • messages – conversation history as {role, content} dicts.

  • system – optional system prompt appended via --append-system-prompt.

  • model – optional model override passed via --model. When None the current response-speed setting supplies one.

Returns:

iterator yielding stdout text chunks.

cli_name = 'claude'[source]
install_hint = 'curl -fsSL https://claude.ai/install.sh | bash   # or npm install -g @anthropic-ai/claude-code'[source]
label = 'Claude (via Claude Code)'[source]
login_command = 'claude setup-token'[source]
name = 'claude'[source]
class spacr.qt.ai.providers.CodexCliProvider[source]

Bases: ChatProvider

OpenAI ChatGPT via the codex CLI.

stream_chat(messages: List[Dict], system: str = '', model: str | None = None) Iterator[str][source]

Stream a chat completion from the codex CLI.

Parameters:
  • messages – conversation history as {role, content} dicts.

  • system – optional system prompt folded into the prompt body.

  • model – optional model override passed via --model. When None the current response-speed setting supplies one.

Returns:

iterator yielding stdout text chunks.

cli_name = 'codex'[source]
install_hint = 'npm install -g @openai/codex   # or brew install codex'[source]
label = 'ChatGPT (via Codex CLI)'[source]
login_command = 'codex login'[source]
name = 'codex'[source]
class spacr.qt.ai.providers.GeminiCliProvider[source]

Bases: ChatProvider

Google Gemini via the gemini CLI.

stream_chat(messages: List[Dict], system: str = '', model: str | None = None) Iterator[str][source]

Stream a chat completion from the gemini CLI.

Parameters:
  • messages – conversation history as {role, content} dicts.

  • system – optional system prompt folded into the prompt body.

  • model – optional model override passed via -m. When None the current response-speed setting supplies one.

Returns:

iterator yielding stdout text chunks.

cli_name = 'gemini'[source]
install_hint = 'npm install -g @google/gemini-cli   # or brew install gemini-cli'[source]
label = 'Gemini (via Gemini CLI)'[source]
login_command = 'gemini'[source]
name = 'gemini'[source]
spacr.qt.ai.providers.configured_providers() List[ChatProvider][source]

Return only providers whose CLI is installed and logged in.

spacr.qt.ai.providers.get_provider(name: str) ChatProvider | None[source]

Look up a registered provider by its short id.

Parameters:

name – provider id ("claude", "codex", "gemini").

Returns:

the matching provider, or None if no such id.

spacr.qt.ai.providers.list_providers() List[ChatProvider][source]

Return every registered provider, regardless of install state.