"""Background scheduler for Knowledge Flywheel analysis. Runs every 5 minutes via APScheduler, picks up AISession entries with analysis_status='pending' and runs flow proposal analysis. Each session is committed individually to prevent a single failure from rolling back all progress or causing duplicate proposals. """ import logging from sqlalchemy import select from app.core.admin_database import _admin_session_factory as async_session_maker from app.models.ai_session import AISession from app.services.knowledge_flywheel import analyze_session logger = logging.getLogger(__name__) async def process_pending_analyses() -> None: """Process resolved sessions awaiting Knowledge Flywheel analysis.""" async with async_session_maker() as db: try: result = await db.execute( select(AISession.id) .where(AISession.analysis_status == "pending") .order_by(AISession.resolved_at.asc()) .limit(10) ) session_ids = [row[0] for row in result.all()] except Exception as e: logger.error("Knowledge Flywheel scheduler query error: %s", e) return if not session_ids: return logger.info("Processing %d pending Knowledge Flywheel analyses", len(session_ids)) # Process each session in its own DB session to isolate failures for session_id in session_ids: async with async_session_maker() as db: try: result = await db.execute( select(AISession).where(AISession.id == session_id) ) session = result.scalar_one_or_none() if not session or session.analysis_status != "pending": continue await analyze_session(session, db) session.analysis_status = "completed" await db.commit() logger.info("Knowledge Flywheel completed for session %s", session_id) except Exception as e: await db.rollback() logger.warning( "Knowledge Flywheel failed for session %s: %s", session_id, e, ) # Mark as failed in a separate transaction try: async with async_session_maker() as db2: result = await db2.execute( select(AISession).where(AISession.id == session_id) ) s = result.scalar_one_or_none() if s: s.analysis_status = "failed" await db2.commit() except Exception: logger.error("Failed to mark session %s as failed", session_id)