Files
haos-hacs-immich-album-watcher/packages/server/src/immich_watcher_server/main.py
alexei.dolgolyov 88ffd5d077
Some checks failed
Validate / Hassfest (push) Has been cancelled
Add Claude AI Telegram bot enhancement (Phase 6)
Integrate Claude AI into the notification system for intelligent
conversational interactions and AI-powered captions.

New modules:
- ai/service.py: Claude API client with conversation history,
  caption generation, and album activity summarization
- ai/telegram_webhook.py: Telegram webhook handler for incoming
  bot messages, routes to AI service for responses

Features:
- Conversational bot: users chat with the bot about albums
- AI captions: intelligent notification messages based on album
  context (people, locations, dates) - enabled per target via
  "ai_captions" config flag
- Album summaries: "what's new?" triggers AI-generated overview
- /start command with welcome message
- Webhook register/unregister endpoints

Architecture:
- Per-chat conversation history (in-memory, capped at 20 messages)
- Graceful degradation: AI features completely disabled without
  IMMICH_WATCHER_ANTHROPIC_API_KEY env var (zero impact)
- AI caption failure falls back to Jinja2 template rendering
- Health endpoint reports ai_enabled status

Config: IMMICH_WATCHER_ANTHROPIC_API_KEY, IMMICH_WATCHER_AI_MODEL,
IMMICH_WATCHER_AI_MAX_TOKENS

Server now has 45 API routes (was 42 after Phase 5).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:38:51 +03:00

100 lines
2.7 KiB
Python

"""FastAPI application entry point."""
from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from .config import settings
from .database.engine import init_db
from .services.scheduler import start_scheduler, stop_scheduler
from .auth.routes import router as auth_router
from .api.servers import router as servers_router
from .api.trackers import router as trackers_router
from .api.templates import router as templates_router
from .api.targets import router as targets_router
from .api.users import router as users_router
from .api.status import router as status_router
from .api.sync import router as sync_router
from .ai.telegram_webhook import router as telegram_ai_router
logging.basicConfig(
level=logging.DEBUG if settings.debug else logging.INFO,
format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
)
_LOGGER = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan: startup and shutdown."""
# Startup
settings.data_dir.mkdir(parents=True, exist_ok=True)
await init_db()
_LOGGER.info("Database initialized at %s", settings.effective_database_url)
await start_scheduler()
yield
# Shutdown
await stop_scheduler()
app = FastAPI(
title="Immich Watcher",
description="Standalone Immich album change notification server",
version="0.1.0",
lifespan=lifespan,
)
# CORS for frontend dev server
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# API routes
app.include_router(auth_router)
app.include_router(servers_router)
app.include_router(trackers_router)
app.include_router(templates_router)
app.include_router(targets_router)
app.include_router(users_router)
app.include_router(status_router)
app.include_router(sync_router)
app.include_router(telegram_ai_router)
# Serve frontend static files if available
_frontend_dist = Path(__file__).parent / "frontend"
if _frontend_dist.is_dir():
app.mount("/", StaticFiles(directory=_frontend_dist, html=True), name="frontend")
@app.get("/api/health")
async def health():
"""Health check endpoint."""
from .ai.service import is_ai_enabled
return {"status": "ok", "version": "0.1.0", "ai_enabled": is_ai_enabled()}
def run():
"""Run the server (entry point for `immich-watcher` CLI command)."""
import uvicorn
uvicorn.run(
"immich_watcher_server.main:app",
host=settings.host,
port=settings.port,
reload=settings.debug,
)