From b71eec94f8f63b75170535b1ff91f3999f8b64e7 Mon Sep 17 00:00:00 2001 From: Frank Schwenk Date: Wed, 10 Jun 2026 10:03:35 +0200 Subject: [PATCH] speling is difikult --- server/ai.py | 48 ++++++++++++++++++++++++++++++++---------- server/app.py | 14 +++++++++++- server/scheduler.py | 20 +++++++++++++++++- tools/regenerate_ai.py | 39 ++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 tools/regenerate_ai.py diff --git a/server/ai.py b/server/ai.py index 77cc15f..4da193c 100644 --- a/server/ai.py +++ b/server/ai.py @@ -72,7 +72,11 @@ async def _call_openrouter(prompt: str, *, system: str) -> str | None: def _motivation_key(day: str) -> str: - return f"motivation:{day}" + return f"motivation:v2:{day}" + + +def _oracle_key(week: str) -> str: + return f"oracle:v2:{week}" async def _cached_motivation(db: aiosqlite.Connection, day: str) -> str | None: @@ -86,12 +90,15 @@ async def generate_daily_motivation( db: aiosqlite.Connection, tz: ZoneInfo, day: str | None = None, + *, + force: bool = False, ) -> dict[str, Any]: day = day or datetime.now(tz).strftime("%Y-%m-%d") key = _motivation_key(day) - cached = await _cached_motivation(db, day) - if cached: - return {"text": cached, "source": "cache", "day": day} + if not force: + cached = await _cached_motivation(db, day) + if cached: + return {"text": cached, "source": "cache", "day": day} stats = await compute_stats(db) prompt = ( @@ -123,12 +130,19 @@ async def get_roast_of_the_day(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[s return await get_daily_motivation(db, tz) -async def get_oracle(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]: - week = datetime.now(tz).strftime("%Y-W%W") - key = f"oracle:{week}" - cached = await _get_cache(db, key) - if cached: - return {"text": cached, "source": "cache", "week": week} +async def generate_oracle( + db: aiosqlite.Connection, + tz: ZoneInfo, + week: str | None = None, + *, + force: bool = False, +) -> dict[str, Any]: + week = week or datetime.now(tz).strftime("%Y-W%W") + key = _oracle_key(week) + if not force: + cached = await _get_cache(db, key) + if cached: + return {"text": cached, "source": "cache", "week": week} stats = await compute_stats(db) days_active = stats["days_active"] @@ -165,5 +179,17 @@ async def get_oracle(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]: else: source = "ai" await _set_cache(db, key, text) - + logger.info("Generated oracle for %s (%s)", week, source) return {"text": text, "source": source, "week": week} + + +async def get_oracle(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]: + week = datetime.now(tz).strftime("%Y-W%W") + cached = await _get_cache(db, _oracle_key(week)) + if cached: + return {"text": cached, "source": "cache", "week": week} + return { + "text": "Das Orakel bereitet sich vor. Schau später nochmal rein.", + "source": "pending", + "week": week, + } diff --git a/server/app.py b/server/app.py index b70914d..a4930fd 100644 --- a/server/app.py +++ b/server/app.py @@ -11,7 +11,7 @@ from fastapi import Depends, FastAPI, HTTPException, Request from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles -from server.ai import generate_daily_motivation, get_oracle, get_roast_of_the_day +from server.ai import generate_daily_motivation, generate_oracle, get_oracle, get_roast_of_the_day from server.auth import create_token, require_auth, verify_pin from server.config_loader import load_meds_config, slot_to_dict, today_str from server.db import ensure_schema, get_db, new_id, utc_now_iso @@ -37,6 +37,17 @@ async def _ensure_today_motivation() -> None: await db.close() +async def _ensure_weekly_oracle() -> None: + config = load_meds_config() + db = await get_db() + try: + await generate_oracle(db, config.timezone) + except Exception: + logger.exception("Startup oracle generation failed") + finally: + await db.close() + + @asynccontextmanager async def lifespan(app: FastAPI): db = await get_db() @@ -44,6 +55,7 @@ async def lifespan(app: FastAPI): await db.close() start_scheduler() asyncio.create_task(_ensure_today_motivation()) + asyncio.create_task(_ensure_weekly_oracle()) yield diff --git a/server/scheduler.py b/server/scheduler.py index f524afb..a0f9079 100644 --- a/server/scheduler.py +++ b/server/scheduler.py @@ -8,7 +8,7 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.date import DateTrigger -from server.ai import generate_daily_motivation +from server.ai import generate_daily_motivation, generate_oracle from server.config_loader import load_meds_config, slot_to_dict, today_str from server.db import get_db, utc_now_iso from server.messages import pick @@ -49,6 +49,17 @@ async def _generate_daily_motivation() -> None: await db.close() +async def _generate_weekly_oracle() -> None: + config = load_meds_config() + db = await get_db() + try: + await generate_oracle(db, config.timezone) + except Exception: + logger.exception("Weekly oracle generation failed") + finally: + await db.close() + + async def _evening_check() -> None: db = await get_db() try: @@ -119,6 +130,13 @@ def start_scheduler() -> None: replace_existing=True, ) + scheduler.add_job( + _generate_weekly_oracle, + trigger=CronTrigger(day_of_week="mon", hour=3, minute=5, timezone=tz), + id="weekly-oracle", + replace_existing=True, + ) + if not scheduler.running: scheduler.start() logger.info("Scheduler started for timezone %s", tz) diff --git a/tools/regenerate_ai.py b/tools/regenerate_ai.py new file mode 100644 index 0000000..cecf427 --- /dev/null +++ b/tools/regenerate_ai.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""Regenerate cached AI texts (motivation + oracle).""" + +from __future__ import annotations + +import argparse +import asyncio + +from server.ai import generate_daily_motivation, generate_oracle +from server.config_loader import load_meds_config +from server.db import get_db + + +async def main() -> None: + parser = argparse.ArgumentParser(description="Regenerate cached AI texts") + parser.add_argument("--motivation", action="store_true", help="Regenerate today's motivation") + parser.add_argument("--oracle", action="store_true", help="Regenerate this week's oracle") + parser.add_argument("--force", action="store_true", help="Regenerate even if cache exists") + args = parser.parse_args() + + if not args.motivation and not args.oracle: + args.motivation = True + args.oracle = True + + config = load_meds_config() + db = await get_db() + try: + if args.motivation: + result = await generate_daily_motivation(db, config.timezone, force=args.force) + print("motivation:", result) + if args.oracle: + result = await generate_oracle(db, config.timezone, force=args.force) + print("oracle:", result) + finally: + await db.close() + + +if __name__ == "__main__": + asyncio.run(main())