style: apply ruff's automatic fixes and formatter

Mechanical only, and separated from the judgment calls that follow so the
reviewable changes are not buried in a 98-file whitespace diff.

227 automatic fixes: 60 blank lines carrying whitespace, 60 unsorted import
blocks, 34 Optional[X] to X | None, 28 unused imports, 16 deprecated typing
imports, 12 datetime.timezone.utc to datetime.UTC, and assorted smaller
modernisations. Then `ruff format` over src and tests: 98 files reformatted,
35 already conforming.

No file among the unused-import findings defines __all__ or is an __init__.py,
so nothing here removes a re-export.

`make test`: 658 passed, unchanged from HEAD.

Two things observed while verifying, neither addressed here:

`pytest tests/` cannot collect — tests/e2e/test_orchestration_e2e.py uses an
`e2e` marker that is not registered, and the config is strict about markers.
This fails identically at HEAD, so it predates this change; `make test` passes
because it ignores tests/e2e, tests/integration and tests/contracts.

test_tatlock_tool_call_logging_calculator is flaky. It failed once in a full run
with these changes and passed on the next, passes in isolation with them, and
fails in isolation at HEAD. It is order- or timing-dependent, not a regression
from this commit — established by running the full suite both ways rather than
by reasoning about which change could have caused it.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 17:25:18 +02:00
co-authored by Claude
parent 57fa6c13fc
commit 78066fab1b
103 changed files with 1601 additions and 1749 deletions
+34 -86
View File
@@ -2,6 +2,7 @@
Global application configuration.
Following best practice of splitting config across domains.
"""
from enum import Enum
from functools import lru_cache
from pathlib import Path
@@ -42,6 +43,7 @@ def _get_version_from_pyproject() -> str:
class Environment(str, Enum):
"""Application environment."""
DEVELOPMENT = "development"
PRODUCTION = "production"
TESTING = "testing"
@@ -54,6 +56,7 @@ class Config(BaseSettings):
Loads from environment variables and .env file.
Domain-specific configs should be in their respective modules.
"""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
@@ -74,143 +77,90 @@ class Config(BaseSettings):
# Anthropic Configuration (Claude - cloud fallback)
ANTHROPIC_API_KEY: str | None = Field(
default=None,
description="Anthropic API key for the Claude fallback backend"
default=None, description="Anthropic API key for the Claude fallback backend"
)
ANTHROPIC_MODEL: str = Field(
default="claude-sonnet-5",
description="Claude model for the fallback backend"
default="claude-sonnet-5", description="Claude model for the fallback backend"
)
PREFER_CLOUD_BACKEND: bool = Field(
default=False,
description="Prefer Claude over Ollama (default: local-first)"
default=False, description="Prefer Claude over Ollama (default: local-first)"
)
# Ollama Configuration (local - primary backend)
OLLAMA_HOST: HttpUrl = Field(
default="http://localhost:11434",
description="Ollama server URL"
)
OLLAMA_DEFAULT_MODEL: str = Field(
default="gemma4:e2b",
description="Default Ollama model"
)
OLLAMA_TIMEOUT: int = Field(
default=120,
description="Ollama request timeout in seconds"
)
OLLAMA_HOST: HttpUrl = Field(default="http://localhost:11434", description="Ollama server URL")
OLLAMA_DEFAULT_MODEL: str = Field(default="gemma4:e2b", description="Default Ollama model")
OLLAMA_TIMEOUT: int = Field(default=120, description="Ollama request timeout in seconds")
STEWARD_TIMEOUT: int = Field(
default=60,
description="Steward analysis timeout in seconds (gemma4 needs ~35s warm)"
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"
default=20, description="Timeout for each streaming turn in seconds"
)
# SearXNG Configuration
SEARXNG_HOST: HttpUrl = Field(
default="http://searxng:8080",
description="SearXNG server URL (container name; internal port 8080)"
)
SEARXNG_TIMEOUT: int = Field(
default=30,
description="SearXNG request timeout in seconds"
description="SearXNG server URL (container name; internal port 8080)",
)
SEARXNG_TIMEOUT: int = Field(default=30, description="SearXNG request timeout in seconds")
# Redis Configuration
REDIS_HOST: str = Field(
default="localhost",
description="Redis server host"
)
REDIS_PORT: int = Field(
default=6379,
description="Redis server port"
)
REDIS_TIMEOUT: int = Field(
default=5,
description="Redis connection timeout in seconds"
)
REDIS_HOST: str = Field(default="localhost", description="Redis server host")
REDIS_PORT: int = Field(default=6379, description="Redis server port")
REDIS_TIMEOUT: int = Field(default=5, description="Redis connection timeout in seconds")
# Library-Desk Configuration (The Librarian backend)
LIBRARIAN_TIMEOUT: int = Field(
default=180,
description="Total time budget for a librarian delegation in seconds"
default=180, description="Total time budget for a librarian delegation in seconds"
)
LIBRARY_DESK_HOST: HttpUrl = Field(
default="http://library-desk:8089",
description="Library-Desk API URL (container name; internal port 8089)"
description="Library-Desk API URL (container name; internal port 8089)",
)
LIBRARY_DESK_API_KEY: str = Field(
default="",
description="API key for Library-Desk authentication"
default="", description="API key for Library-Desk authentication"
)
LIBRARY_DESK_TIMEOUT: int = Field(
default=60,
description="Library-Desk request timeout in seconds"
default=60, description="Library-Desk request timeout in seconds"
)
# Core-API Configuration (The Housekeeper backend)
CORE_API_HOST: HttpUrl = Field(
default="http://core-api:8083",
description="Core-API URL for Home Assistant integration (container name; internal port 8083)"
)
CORE_API_KEY: str = Field(
default="",
description="API key for Core-API authentication"
)
CORE_API_TIMEOUT: int = Field(
default=30,
description="Core-API request timeout in seconds"
description="Core-API URL for Home Assistant integration (container name; internal port 8083)",
)
CORE_API_KEY: str = Field(default="", description="API key for Core-API authentication")
CORE_API_TIMEOUT: int = Field(default=30, description="Core-API request timeout in seconds")
# Qdrant Configuration (Memory vector storage)
QDRANT_HOST: str = Field(
default="localhost",
description="Qdrant server host"
)
QDRANT_PORT: int = Field(
default=6333,
description="Qdrant server port"
)
QDRANT_HOST: str = Field(default="localhost", description="Qdrant server host")
QDRANT_PORT: int = Field(default=6333, description="Qdrant server port")
QDRANT_EMBEDDING_DIM: int = Field(
default=768,
description="Embedding dimension (768 for nomic-embed-text)"
default=768, description="Embedding dimension (768 for nomic-embed-text)"
)
# Ollama Embedding Configuration
OLLAMA_EMBEDDING_MODEL: str = Field(
default="nomic-embed-text",
description="Ollama model for embeddings"
default="nomic-embed-text", description="Ollama model for embeddings"
)
# Redis Memory Database
REDIS_MEMORY_DB: int = Field(
default=1,
description="Redis database number for memory cache"
)
REDIS_MEMORY_TTL_HOURS: int = Field(
default=24,
description="TTL for session context in hours"
)
REDIS_MEMORY_DB: int = Field(default=1, description="Redis database number for memory cache")
REDIS_MEMORY_TTL_HOURS: int = Field(default=24, description="TTL for session context in hours")
# Logging
LOG_LEVEL: str | None = Field(
default=None,
description="Logging level (auto-set based on environment if not specified)"
default=None, description="Logging level (auto-set based on environment if not specified)"
)
# User Configuration
DEFAULT_USER: str | None = Field(
default=None,
description="Default user for single-user setup (auto-set based on environment if not specified)"
description="Default user for single-user setup (auto-set based on environment if not specified)",
)
# CORS
CORS_ORIGINS: list[str] = Field(
default=["*"],
description="Allowed CORS origins"
)
CORS_ORIGINS: list[str] = Field(default=["*"], description="Allowed CORS origins")
CORS_ALLOW_CREDENTIALS: bool = True
CORS_ALLOW_METHODS: list[str] = ["*"]
CORS_ALLOW_HEADERS: list[str] = ["*"]
@@ -235,8 +185,7 @@ class Config(BaseSettings):
if (
self.ENVIRONMENT != Environment.PRODUCTION
and self.DEFAULT_USER is not None
and sanitize_user_id(self.DEFAULT_USER)
== sanitize_user_id(PRODUCTION_TENANT)
and sanitize_user_id(self.DEFAULT_USER) == sanitize_user_id(PRODUCTION_TENANT)
):
raise ValueError(
f"Refusing to start: ENVIRONMENT={self.ENVIRONMENT.value} is "
@@ -299,8 +248,7 @@ class Config(BaseSettings):
return self.DEFAULT_USER or PRODUCTION_TENANT
if self.DEFAULT_USER is not None and (
self.DEFAULT_USER == TEST_TENANT
or self.DEFAULT_USER.startswith(TEST_TENANT_PREFIX)
self.DEFAULT_USER == TEST_TENANT or self.DEFAULT_USER.startswith(TEST_TENANT_PREFIX)
):
return self.DEFAULT_USER
return TEST_TENANT