introduce separate SIMPLE_MODEL config for lightweight tasks

Add a distinct simple_model field alongside ai_model so operators can
route cheap, simple tasks (e.g. email-label translation) to a low-cost
model while keeping the strong model for parent conversations. The two
models can be from different providers (e.g. Gemini + Haiku). If
SIMPLE_MODEL is unset, falls back to AI_MODEL with a warning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 22:18:28 +01:00
co-authored by Claude Sonnet 4.6
parent 14fde88c89
commit 602b8eddc3
4 changed files with 36 additions and 7 deletions
+12 -5
View File
@@ -6,14 +6,21 @@
# --------------------------------------------------------------- # ---------------------------------------------------------------
# --------------------------------------------------------------- # ---------------------------------------------------------------
# AI Model (via litellm — supports any provider) # AI Models (via litellm — supports any provider)
# --------------------------------------------------------------- # ---------------------------------------------------------------
# Use litellm model strings: "<provider>/<model-name>" # Use litellm model strings: "<provider>/<model-name>"
# Examples: #
# anthropic/claude-opus-4-6 (default) # AI_MODEL — primary model for conversation with parents (complex reasoning).
# openai/gpt-4o # SIMPLE_MODEL — lightweight model for simple tasks, e.g. email-label translation.
# gemini/gemini-2.0-flash # Can be from a different provider than AI_MODEL.
# If unset, falls back to AI_MODEL with a warning in the logs.
#
# Examples — mixing providers:
# AI_MODEL=gemini/gemini-3-pro-preview + SIMPLE_MODEL=gemini/gemini-3-flash
# AI_MODEL=anthropic/claude-opus-4-6 + SIMPLE_MODEL=anthropic/claude-haiku-4-5-20251001
# AI_MODEL=gemini/gemini-3-pro-preview + SIMPLE_MODEL=anthropic/claude-haiku-4-5-20251001
AI_MODEL=anthropic/claude-opus-4-6 AI_MODEL=anthropic/claude-opus-4-6
SIMPLE_MODEL=anthropic/claude-haiku-4-5-20251001
# Extended thinking — Anthropic models only (leave unset to disable). # Extended thinking — Anthropic models only (leave unset to disable).
# Enables a reasoning phase before the model's reply, which improves # Enables a reasoning phase before the model's reply, which improves
+1
View File
@@ -58,6 +58,7 @@ _notifier = AdminNotifier(
indoor_email=_config.admin_email_indoor, indoor_email=_config.admin_email_indoor,
outdoor_email=_config.admin_email_outdoor, outdoor_email=_config.admin_email_outdoor,
cc_emails=[e.strip() for e in _config.admin_email_cc.split(",") if e.strip()], cc_emails=[e.strip() for e in _config.admin_email_cc.split(",") if e.strip()],
model=_config.simple_model,
) )
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
+1
View File
@@ -61,6 +61,7 @@ def build_components(config: Config):
indoor_email=config.admin_email_indoor, indoor_email=config.admin_email_indoor,
outdoor_email=config.admin_email_outdoor, outdoor_email=config.admin_email_outdoor,
cc_emails=[e.strip() for e in config.admin_email_cc.split(",") if e.strip()], cc_emails=[e.strip() for e in config.admin_email_cc.split(",") if e.strip()],
model=config.simple_model,
) )
agent = EmailAgent( agent = EmailAgent(
+22 -2
View File
@@ -1,9 +1,12 @@
"""Configuration loaded from environment variables.""" """Configuration loaded from environment variables."""
import logging
import os import os
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
logger = logging.getLogger(__name__)
try: try:
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
@@ -13,10 +16,17 @@ except ImportError:
@dataclass @dataclass
class Config: class Config:
# AI model — litellm format, e.g. "anthropic/claude-opus-4-6" or "openai/gpt-4o". # Primary model for conversation with parents — litellm format.
# The matching API key must be set as an env var (ANTHROPIC_API_KEY, OPENAI_API_KEY, …). # The matching API key must be set as an env var (ANTHROPIC_API_KEY, OPENAI_API_KEY, …).
# Example: "anthropic/claude-opus-4-6", "google/gemini-2.0-flash", "openai/gpt-4o"
ai_model: str = "anthropic/claude-opus-4-6" ai_model: str = "anthropic/claude-opus-4-6"
# Lightweight model for simple tasks such as email-label translation.
# Can be from a different provider than ai_model.
# If not configured (SIMPLE_MODEL env var unset), falls back to ai_model with a warning.
# Example: "anthropic/claude-haiku-4-5-20251001", "openai/gpt-4o-mini"
simple_model: str = ""
# Email — IMAP (receiving) # Email — IMAP (receiving)
imap_host: str = "" imap_host: str = ""
imap_port: int = 993 imap_port: int = 993
@@ -57,8 +67,18 @@ class Config:
@classmethod @classmethod
def from_env(cls) -> "Config": def from_env(cls) -> "Config":
ai_model = os.getenv("AI_MODEL", "anthropic/claude-opus-4-6")
simple_model = os.getenv("SIMPLE_MODEL", "")
if not simple_model:
logger.warning(
"SIMPLE_MODEL not configured — falling back to AI_MODEL (%s) for simple tasks "
"(set SIMPLE_MODEL to a cheaper model, e.g. anthropic/claude-haiku-4-5-20251001)",
ai_model,
)
simple_model = ai_model
return cls( return cls(
ai_model=os.getenv("AI_MODEL", "anthropic/claude-opus-4-6"), ai_model=ai_model,
simple_model=simple_model,
imap_host=os.getenv("IMAP_HOST", ""), imap_host=os.getenv("IMAP_HOST", ""),
imap_port=int(os.getenv("IMAP_PORT", "993")), imap_port=int(os.getenv("IMAP_PORT", "993")),
imap_username=os.getenv("IMAP_USERNAME", ""), imap_username=os.getenv("IMAP_USERNAME", ""),