Skip to content

LLM Providers

Garot Conklin edited this page May 14, 2026 · 1 revision

LLM Providers

Signal supports two LLM backends. Claude is the default and the only supported provider for automated (scheduled) runs.


Provider comparison

Claude Code CLI Ollama (qwen2.5:14b)
Cost Claude Pro/Max subscription (no per-token billing) Free (local compute)
Speed ~9 sec/article, ~22 min full run ~10 sec/article, ~28 min full run
Analysis quality Significantly better — more nuanced entity extraction, sharper framing analysis Good
Requires Claude Code CLI installed + claude auth login Ollama server running + model pulled
Safe for launchd Yes No — crashes macOS kernel via Metal GPU driver
Network Requires internet (HTTPS to Anthropic API) Fully offline
Model Claude (version determined by CLI) Configured in sources.yaml

Claude Code CLI (default)

How it works

Each LLM call in analyzer.py runs:

subprocess.run(
    ["claude", "-p", prompt, "--print"],
    capture_output=True, text=True, timeout=180
)

This spawns a new claude process per call, which makes an HTTPS request to Anthropic's API and returns the response to stdout. There is no persistent connection or session — each call is fully independent.

Installation

npm install -g @anthropic-ai/claude-code
claude --version   # 2.x.x (Claude Code)

Authentication

Claude Code CLI uses your Claude Pro or Max subscription:

claude auth login

No API key is required. Authentication is tied to your Anthropic account session.

Verify it works

echo "Say hello" | claude -p "Say hello" --print

If that returns a response, Pass 1 will work.

Why Claude is required for scheduled runs

When Ollama is invoked from a launchd background daemon, it accesses Apple Metal for GPU inference in a non-interactive GPU session context. This mismatch triggers a Metal driver kernel panic, which hard-locks the machine. Claude avoids this entirely — it's a network call with no GPU involvement.

See Troubleshooting for the full crash investigation.


Ollama (manual/interactive use only)

How it works

Each LLM call uses the ollama Python client:

client = ollama.Client(host=base_url)
response = client.generate(
    model=model,
    prompt=prompt,
    options={"temperature": 0.1, "num_predict": 2048},
)

This connects to the Ollama HTTP server running locally at http://localhost:11434.

Setup

# Install Ollama
brew install ollama

# Start the server
ollama serve

# Pull the model
ollama pull qwen2.5:14b

# Verify
ollama list   # should show qwen2.5:14b

Recommended models for M1/M2/M3 Mac

Model VRAM Speed Quality Use case
qwen2.5:14b ~9 GB ~10 sec/call Best Production (manual runs)
llama3.1:8b ~5 GB ~5 sec/call Good Development/testing
mistral:7b ~4 GB ~4 sec/call Baseline Quick smoke tests

Use Ollama for a manual run

# Start Ollama first
ollama serve

# Run with Ollama
SIGNAL_LLM_PROVIDER=ollama python3 main.py

Do not run Ollama via run_and_publish.sh or launchctl start — the Metal GPU crash will lock up your Mac regardless of available RAM.


Switching providers

Precedence (highest first)

  1. SIGNAL_LLM_PROVIDER environment variable
  2. llm.provider in config/sources.yaml
  3. Hard-coded fallback: "ollama"

Option 1 — Environment variable (one-off override)

# Force Claude
SIGNAL_LLM_PROVIDER=claude python3 main.py

# Force Ollama
SIGNAL_LLM_PROVIDER=ollama python3 main.py

Option 2 — sources.yaml (persistent default)

llm:
  provider: claude # change to "ollama" for local inference

Option 3 — launchd plist (scheduled run default)

The plist already sets SIGNAL_LLM_PROVIDER=claude. To change:

<key>EnvironmentVariables</key>
<dict>
    <key>SIGNAL_LLM_PROVIDER</key>
    <string>claude</string>   <!-- or "ollama" — but see crash warning above -->
</dict>

After editing scripts/com.flexrpl.signal.plist:

launchctl unload ~/Library/LaunchAgents/com.flexrpl.signal.plist
cp scripts/com.flexrpl.signal.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.plist

run_and_publish.sh default

The script sets SIGNAL_LLM_PROVIDER explicitly so it behaves identically to launchd when run manually:

export SIGNAL_LLM_PROVIDER="${SIGNAL_LLM_PROVIDER:-claude}"

This means claude is the default, but you can still override it:

SIGNAL_LLM_PROVIDER=ollama bash scripts/run_and_publish.sh

Clone this wiki locally