from __future__ import annotations import asyncio from datetime import datetime from typing import Any, Literal, Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from sqlalchemy import and_, or_, select from sqlalchemy.ext.asyncio import AsyncSession from ...core.logging import get_logger from ...core.response import success_response from ...core.database import AsyncSessionLocal, get_db from ...dependencies.auth import get_current_user from ...dependencies.tenant import get_tenant_id from ...models import DecisionRecord, IngestEvent, PlanBundle, PortfolioSnapshot, Project, ReplyReceipt, User, UserProject, WorkItem from ...services.auth import has_any_role from ...services.ai_pmo.ai_pmo_cadence import AIPMOCadenceService from ...services.ai_pmo.ai_pmo_kernel import AIPMOKernelService router = APIRouter(prefix="/ai-pmo", tags=["AI PMO"]) logger = get_logger(__name__) FRONT_NOISE_INTENTS = {"query.read_only", "query.answer"} def _can_view_all_work_items(user: User) -> bool: return has_any_role(str(getattr(user, "role", "") or ""), ["OWNER", "REVIEWER"]) def _visible_work_item_filter(*, current_user: User, tenant_id: int): if _can_view_all_work_items(current_user): return True return or_( WorkItem.owner_user_id == int(current_user.id), WorkItem.project_id.in_( select(Project.id) .outerjoin( UserProject, and_( UserProject.tenant_id == Project.tenant_id, UserProject.project_id == Project.id, UserProject.user_id == int(current_user.id), ), ) .where( and_( Project.tenant_id == int(tenant_id), or_(Project.owner_id == int(current_user.id), UserProject.user_id.is_not(None)), ) ) ), )