PMFlow/backend/app/api/semantic_facts_api/routes.py

105 lines
3.4 KiB
Python

from __future__ import annotations
from typing import Optional
from fastapi import APIRouter, Depends, Query
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
from ...core.database import get_db
from ...core.response import error_response, success_response
from ...dependencies import require_manager_user
from ...models import User
from ...services.semantic_fact_service import SemanticFactService
router = APIRouter(prefix="/semantic-facts", tags=["semantic_facts"])
class SemanticFactsBackfillRequest(BaseModel):
entity_types: Optional[list[str]] = Field(default=None)
only_missing: bool = Field(default=True)
limit_per_type: int = Field(default=200, ge=1, le=1000)
@router.get("/admin/overview")
async def get_semantic_facts_overview(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_manager_user),
):
service = SemanticFactService(db, tenant_id=int(current_user.tenant_id))
return success_response(await service.get_admin_overview())
@router.get("/admin/objects")
async def list_semantic_fact_objects(
entity_type: str | None = Query(default=None),
q: str | None = Query(default=None),
status: str | None = Query(default=None),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_manager_user),
):
service = SemanticFactService(db, tenant_id=int(current_user.tenant_id))
items = await service.list_admin_objects(
entity_types=[entity_type] if entity_type else None,
q=q,
status=status,
)
return success_response({"items": items})
@router.get("/admin/objects/{entity_type}/{entity_id}")
async def get_semantic_fact_object_detail(
entity_type: str,
entity_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_manager_user),
):
service = SemanticFactService(db, tenant_id=int(current_user.tenant_id))
try:
detail = await service.get_admin_detail(entity_type=entity_type, entity_id=int(entity_id))
except ValueError as exc:
return JSONResponse(status_code=404, content=error_response(404, str(exc)))
return success_response(detail)
@router.post("/admin/objects/{entity_type}/{entity_id}/reextract")
async def reextract_semantic_fact_object(
entity_type: str,
entity_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_manager_user),
):
service = SemanticFactService(db, tenant_id=int(current_user.tenant_id))
row = await service.enqueue_entity_event(
entity_type=entity_type,
entity_id=int(entity_id),
operation="extract",
trigger_type="manual_retry",
)
await db.commit()
return success_response(
{
"queued": True,
"event_id": int(row.id),
"entity_type": str(entity_type),
"entity_id": int(entity_id),
}
)
@router.post("/admin/backfill")
async def enqueue_semantic_fact_backfill(
body: SemanticFactsBackfillRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_manager_user),
):
service = SemanticFactService(db, tenant_id=int(current_user.tenant_id))
result = await service.enqueue_backfill(
entity_types=body.entity_types,
only_missing=bool(body.only_missing),
limit_per_type=int(body.limit_per_type),
)
await db.commit()
return success_response(result)