feat: make Ollama/gemma4 the primary backend with Claude as fallback

Rolls back the claudification backend preference: PREFER_CLOUD_BACKEND now
defaults to false, resolve_backend() picks Ollama first and uses Claude when
explicitly preferred or when the new Ollama startup health check fails. The
Steward retries mid-request failures on the other backend in both directions.

Also hardens the fallback itself: Anthropic SDK imports are lazy so a broken
anthropic package degrades to Ollama-only instead of crashing at import time
(root cause of the production outage since April), anthropic is pinned to a
pydantic-ai-1.27-compatible range, ANTHROPIC_MODEL defaults to claude-sonnet-5
(sonnet-4-20250514 retired 2026-06-15), sampling parameters are stripped from
Claude calls (Sonnet 5 rejects them), and the Steward timeout is configurable
(STEWARD_TIMEOUT, default 60s) since gemma4 needs ~35s warm for analysis.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 18:35:53 +02:00
co-authored by Claude Fable 5
parent 427ad311dc
commit 033a1c01e8
11 changed files with 323 additions and 71 deletions
+11 -7
View File
@@ -64,21 +64,21 @@ class Config(BaseSettings):
API_PORT: int = Field(default=8000, description="API port")
API_PREFIX: str = Field(default="/v1", description="API route prefix")
# Anthropic Configuration (Claude - preferred backend)
# Anthropic Configuration (Claude - cloud fallback)
ANTHROPIC_API_KEY: str | None = Field(
default=None,
description="Anthropic API key for Claude access"
description="Anthropic API key for the Claude fallback backend"
)
ANTHROPIC_MODEL: str = Field(
default="claude-sonnet-4-20250514",
description="Claude model to use"
default="claude-sonnet-5",
description="Claude model for the fallback backend"
)
PREFER_CLOUD_BACKEND: bool = Field(
default=True,
description="Prefer Claude over Ollama when available"
default=False,
description="Prefer Claude over Ollama (default: local-first)"
)
# Ollama Configuration (local fallback)
# Ollama Configuration (local - primary backend)
OLLAMA_HOST: HttpUrl = Field(
default="http://localhost:11434",
description="Ollama server URL"
@@ -91,6 +91,10 @@ class Config(BaseSettings):
default=120,
description="Ollama request timeout in seconds"
)
STEWARD_TIMEOUT: int = Field(
default=60,
description="Steward analysis timeout in seconds (gemma4 needs ~35s warm)"
)
STREAM_TIMEOUT: int = Field(
default=20,
description="Timeout for each streaming turn in seconds"
+9 -3
View File
@@ -9,7 +9,11 @@ from src.agents.biographer import register_biographer
from src.agents.housekeeper import register_housekeeper
from src.agents.librarian import register_librarian
from src.agents.tatlock_core import TATLOCK_CORE_CAPABILITY, tatlock_core_tools
from src.anthropic.model_selector import check_claude_health, get_model_info
from src.anthropic.model_selector import (
check_claude_health,
check_ollama_health,
get_model_info,
)
from src.core.household_registry import get_household_registry
from src.core.logging_config import get_logger
@@ -87,7 +91,7 @@ async def initialize_application():
Initialize the application.
Performs all startup tasks:
1. Check Claude API health (for backend selection)
1. Check Ollama (primary) and Claude (fallback) health for backend selection
2. Register household members
3. (Future) Initialize connections
@@ -95,13 +99,15 @@ async def initialize_application():
"""
logger.info("application_initialization_starting")
# Check Claude API health for backend selection
# Check backend health: Ollama is primary, Claude is the fallback
await check_ollama_health()
await check_claude_health()
model_info = get_model_info()
logger.info(
"model_backend_configured",
backend=model_info["backend"],
model=model_info["model"],
ollama_available=model_info["ollama_available"],
claude_available=model_info["claude_available"],
)