373 lines
14 KiB
Python
373 lines
14 KiB
Python
"""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)
|