"""Platform Prompt asset inventory API.""" from __future__ import annotations from fastapi import APIRouter, Depends, Query from fastapi.responses import JSONResponse from sqlalchemy.ext.asyncio import AsyncSession from ..core.database import get_db from ..core.response import error_response, success_response from ..dependencies.permissions import require_platform_ai_ops from ..dependencies.tenant import get_tenant_id from ..models import User from ..schemas.prompt_assets import ( PromptAssetReleaseActionRequest, PromptAssetReleaseCheckRunRequest, PromptAssetReleaseCreateRequest, PromptGoldenCaseCreateRequest, PromptGoldenCaseFromCallRequest, PromptReplayRunCreateRequest, ) from ..services.prompt_assets_service import PromptAssetService router = APIRouter(prefix="/prompt-assets", tags=["prompt_assets"]) @router.get("") async def list_prompt_assets( family: str | None = Query(default=None), runtime_source: str | None = Query(default=None), editable_policy: str | None = Query(default=None), keyword: str | None = Query(default=None, max_length=200), current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) data = await PromptAssetService(db).list_assets( tenant_id=effective_tenant_id, family=family, runtime_source=runtime_source, editable_policy=editable_policy, keyword=keyword, ) return success_response(data) @router.get("/{prompt_key}") async def get_prompt_asset( prompt_key: str, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).get_asset(tenant_id=effective_tenant_id, prompt_key=prompt_key) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.get("/{prompt_key}/quality-summary") async def get_prompt_asset_quality_summary( prompt_key: str, window: str = Query(default="7d", pattern="^(24h|7d|30d)$"), status: str | None = Query(default=None, max_length=80), release_id: int | None = Query(default=None), current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).get_quality_summary( tenant_id=effective_tenant_id, prompt_key=prompt_key, window=window, status=status, release_id=release_id, include_all_pmflow_feedback=True, ) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.get("/{prompt_key}/golden-cases") async def list_prompt_asset_golden_cases( prompt_key: str, status: str | None = Query(default=None, max_length=40), current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).list_golden_cases( tenant_id=effective_tenant_id, prompt_key=prompt_key, status=status, ) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.post("/{prompt_key}/golden-cases") async def create_prompt_asset_golden_case( prompt_key: str, request: PromptGoldenCaseCreateRequest, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).create_golden_case( tenant_id=effective_tenant_id, prompt_key=prompt_key, case_name=request.case_name, input_payload=request.input_payload, expected_contract=request.expected_contract, expected_contains=request.expected_contains, case_type=request.case_type, priority=request.priority, scenario_key=request.scenario_key, tags=request.tags, created_by=int(current_user.id) if getattr(current_user, "id", None) is not None else None, notes=request.notes, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data) @router.post("/{prompt_key}/golden-cases/from-call/{ai_call_log_id}") async def create_prompt_asset_golden_case_from_call( prompt_key: str, ai_call_log_id: int, request: PromptGoldenCaseFromCallRequest | None = None, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).create_golden_case_from_call( tenant_id=effective_tenant_id, prompt_key=prompt_key, ai_call_log_id=ai_call_log_id, created_by=int(current_user.id) if getattr(current_user, "id", None) is not None else None, case_name=request.case_name if request else None, notes=request.notes if request else None, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data) @router.get("/{prompt_key}/releases") async def list_prompt_asset_releases( prompt_key: str, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).list_releases(tenant_id=effective_tenant_id, prompt_key=prompt_key) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.post("/{prompt_key}/releases") async def create_prompt_asset_release( prompt_key: str, request: PromptAssetReleaseCreateRequest, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).create_release( tenant_id=effective_tenant_id, prompt_key=prompt_key, body_snapshot=request.body_snapshot, created_by=int(current_user.id) if getattr(current_user, "id", None) is not None else None, notes=request.notes, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data) @router.post("/{prompt_key}/releases/{release_id}/activate") async def activate_prompt_asset_release( prompt_key: str, release_id: int, request: PromptAssetReleaseActionRequest | None = None, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): _ = request effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).activate_release( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, actor_id=int(current_user.id) if getattr(current_user, "id", None) is not None else None, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data) @router.get("/{prompt_key}/releases/{release_id}/checks") async def list_prompt_asset_release_checks( prompt_key: str, release_id: int, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).list_release_checks( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, ) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.post("/{prompt_key}/releases/{release_id}/checks/run") async def run_prompt_asset_release_checks( prompt_key: str, release_id: int, request: PromptAssetReleaseCheckRunRequest | None = None, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).run_release_checks( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, actor_id=int(current_user.id) if getattr(current_user, "id", None) is not None else None, reason=request.reason if request else None, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data) @router.post("/{prompt_key}/releases/{release_id}/replay-runs") async def create_prompt_asset_replay_run( prompt_key: str, release_id: int, request: PromptReplayRunCreateRequest, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).create_replay_run( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, created_by=int(current_user.id) if getattr(current_user, "id", None) is not None else None, mode=request.mode, case_status=request.case_status, max_cases=request.max_cases, include_llm=request.include_llm, judge_enabled=request.judge_enabled, reason=request.reason, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data) @router.get("/{prompt_key}/releases/{release_id}/watch-summary") async def get_prompt_asset_release_watch_summary( prompt_key: str, release_id: int, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).get_release_watch_summary( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, ) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.get("/{prompt_key}/releases/{release_id}/replay-runs") async def list_prompt_asset_replay_runs( prompt_key: str, release_id: int, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).list_replay_runs( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, ) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.get("/{prompt_key}/replay-runs/{run_id}") async def get_prompt_asset_replay_run( prompt_key: str, run_id: int, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).get_replay_run( tenant_id=effective_tenant_id, prompt_key=prompt_key, run_id=run_id, ) except ValueError as exc: return JSONResponse(status_code=404, content=error_response(404, str(exc))) return success_response(data) @router.post("/{prompt_key}/releases/{release_id}/rollback") async def rollback_prompt_asset_release( prompt_key: str, release_id: int, request: PromptAssetReleaseActionRequest | None = None, current_user: User = Depends(require_platform_ai_ops), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): _ = request effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) try: data = await PromptAssetService(db).rollback_release( tenant_id=effective_tenant_id, prompt_key=prompt_key, release_id=release_id, actor_id=int(current_user.id) if getattr(current_user, "id", None) is not None else None, ) except ValueError as exc: return JSONResponse(status_code=400, content=error_response(400, str(exc))) return success_response(data)