Working-tree changes that had accumulated without being committed. The test updates matter most: tests/test_caller_gen.py was left behind when caller_gen started requiring voice and age in regulars_included, so the committed tree had a failing suite that only passed locally. - Caller dialog moves from Haiku 4.5 to Sonnet 4.6 (~$1/show to ~$3-4/show) - Grok pinned to x-ai/grok-4.3; grok-4, grok-4-fast and grok-4.1-fast were retired from OpenRouter, and llm.py swallows the 404 and returns empty text, so a retired id makes callers go silent with nothing in the logs - Devon's web_search now runs against SearXNG on the NAS - Assorted TTS, audio, news, cost tracker and control-panel changes - CLAUDE.md updated to match Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.9 KiB
Python
71 lines
2.9 KiB
Python
"""Configuration settings for the AI Radio Show backend"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from pydantic_settings import BaseSettings
|
|
from dotenv import load_dotenv
|
|
|
|
# Load .env from parent directory
|
|
load_dotenv(Path(__file__).parent.parent / ".env")
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
# API Keys
|
|
elevenlabs_api_key: str = os.getenv("ELEVENLABS_API_KEY", "")
|
|
openrouter_api_key: str = os.getenv("OPENROUTER_API_KEY", "")
|
|
inworld_api_key: str = os.getenv("INWORLD_API_KEY", "")
|
|
|
|
# SignalWire
|
|
signalwire_project_id: str = os.getenv("SIGNALWIRE_PROJECT_ID", "")
|
|
signalwire_space: str = os.getenv("SIGNALWIRE_SPACE", "")
|
|
signalwire_token: str = os.getenv("SIGNALWIRE_TOKEN", "")
|
|
signalwire_phone: str = os.getenv("SIGNALWIRE_PHONE", "")
|
|
signalwire_stream_url: str = os.getenv("SIGNALWIRE_STREAM_URL", "")
|
|
|
|
# Email (IMAP)
|
|
submissions_imap_host: str = os.getenv("SUBMISSIONS_IMAP_HOST", "")
|
|
submissions_imap_user: str = os.getenv("SUBMISSIONS_IMAP_USER", "")
|
|
submissions_imap_pass: str = os.getenv("SUBMISSIONS_IMAP_PASS", "")
|
|
|
|
# SearXNG (Devon's web search + caller news grounding) — runs on the NAS.
|
|
# Override with SEARXNG_URL in .env (e.g. http://localhost:8888 for a local container).
|
|
searxng_url: str = os.getenv("SEARXNG_URL", "http://mmgnas:8888")
|
|
|
|
# LLM Settings
|
|
llm_provider: str = "openrouter" # "openrouter" or "ollama"
|
|
openrouter_model: str = "anthropic/claude-sonnet-4.6" # primary/default model
|
|
ollama_model: str = "llama3.2"
|
|
ollama_host: str = "http://localhost:11434"
|
|
|
|
# Per-category model routing
|
|
category_models: dict = {
|
|
"caller_dialog": "anthropic/claude-sonnet-4.6", # live caller dialog — quality matters ($3/$15)
|
|
"devon_ask": "x-ai/grok-4.3", # Devon matches show energy (grok-4.1-fast deprecated 2026-05)
|
|
"devon_monitor": "google/gemini-2.5-flash", # just yes/no decisions, keep cheap ($0.15/$0.60)
|
|
"background_gen": "anthropic/claude-sonnet-4.6", # backgrounds drive the whole call — worth the quality ($3/$15, ~$0.30/show)
|
|
"call_summary": "google/gemini-2.5-flash", # post-call, no personality needed ($0.15/$0.60)
|
|
"news_summary": "google/gemini-2.5-flash", # just digesting headlines ($0.15/$0.60)
|
|
"topic_gen": "google/gemini-2.5-flash", # structured output ($0.15/$0.60)
|
|
}
|
|
|
|
# TTS Settings
|
|
tts_provider: str = "inworld" # "kokoro", "elevenlabs", "inworld", "vits", or "bark"
|
|
|
|
# Audio Settings
|
|
sample_rate: int = 24000
|
|
|
|
# Paths
|
|
base_dir: Path = Path(__file__).parent.parent
|
|
sounds_dir: Path = base_dir / "sounds"
|
|
music_dir: Path = base_dir / "music"
|
|
ads_dir: Path = base_dir / "ads"
|
|
idents_dir: Path = base_dir / "idents"
|
|
sessions_dir: Path = base_dir / "sessions"
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
extra = "ignore"
|
|
|
|
|
|
settings = Settings()
|