66 lines
2 KiB
Python
66 lines
2 KiB
Python
"""Persona catalog routes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, HTTPException, Response, status
|
|
from pydantic import BaseModel
|
|
|
|
from ..deps import CurrentPrincipal
|
|
from ..persona_repository import CatalogPersona, list_catalog_personas
|
|
|
|
router = APIRouter(prefix="/personas", tags=["personas"])
|
|
|
|
|
|
class PersonaSummary(BaseModel):
|
|
code: str
|
|
display_name: str
|
|
difficulty: str
|
|
theory_target: list[str]
|
|
demographics: dict[str, Any]
|
|
presenting_summary: str
|
|
voice_preset: str | None = None
|
|
source: str = "database"
|
|
degraded: bool = False
|
|
|
|
|
|
def _first_text_value(data: dict[str, Any]) -> str:
|
|
for value in data.values():
|
|
if isinstance(value, str) and value.strip():
|
|
return value.strip()
|
|
return ""
|
|
|
|
|
|
def _summary(entry: CatalogPersona) -> PersonaSummary:
|
|
card = entry.card
|
|
return PersonaSummary(
|
|
code=card.code,
|
|
display_name=card.display_name,
|
|
difficulty=card.difficulty,
|
|
theory_target=card.theory_target,
|
|
demographics=card.demographics,
|
|
presenting_summary=_first_text_value(card.presenting),
|
|
source=entry.source,
|
|
degraded=entry.degraded,
|
|
)
|
|
|
|
|
|
@router.get("", response_model=list[PersonaSummary])
|
|
async def list_personas(response: Response, _principal: CurrentPrincipal) -> list[PersonaSummary]:
|
|
"""Return latest approved personas from app.persona_card."""
|
|
try:
|
|
personas = await list_catalog_personas()
|
|
except Exception as exc:
|
|
raise HTTPException(
|
|
status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="persona catalog database unavailable",
|
|
) from exc
|
|
|
|
if any(entry.degraded for entry in personas):
|
|
response.headers["X-Vignette-Degraded"] = "true"
|
|
response.headers["X-Vignette-Catalog-Source"] = "seed_fallback"
|
|
else:
|
|
response.headers["X-Vignette-Catalog-Source"] = "database"
|
|
|
|
return [_summary(entry) for entry in personas]
|