"""Formal decode API routes.""" from __future__ import annotations from typing import Any from uuid import UUID from fastapi import APIRouter, Body, Depends, HTTPException from app.dependencies import _env_file, get_acquisition_repository, get_decode_repository from app.schemas import ( CandidateItemSchema, DecodeResultSchema, IngestItemRequest, IngestItemResponse, IngestItemResultSchema, IngestRecordSchema, KnowledgeParticleSchema, MediaAssetSchema, PayloadDraftSchema, ScopeResultSchema, ) from core.config import IngestApiConfig from decode_content.ingest import KnowledgeIngestClient, ingest_payload_draft router = APIRouter(prefix="/api/decode", tags=["decode"]) @router.get("/items/{item_id}/result", response_model=DecodeResultSchema) def decode_result( item_id: UUID, repo: Any = Depends(get_decode_repository), ) -> DecodeResultSchema: getter = getattr(repo, "get_decode_result_for_item", None) if getter is None: raise HTTPException(status_code=501, detail="decode result reader is not implemented") return DecodeResultSchema.model_validate(getter(item_id)) @router.get("/items/{item_id}/detail") def decode_item_detail( item_id: UUID, acquisition_repo: Any = Depends(get_acquisition_repository), decode_repo: Any = Depends(get_decode_repository), ) -> dict[str, Any]: try: item = acquisition_repo.get_candidate_item(item_id) media = acquisition_repo.list_media_assets_for_item(item_id) except Exception as exc: raise HTTPException(status_code=404, detail=f"candidate item not found: {exc}") from exc try: result = decode_repo.get_decode_result_for_item(item_id) except Exception as exc: result = None list_particles = getattr(decode_repo, "list_knowledge_particles", None) list_scopes = getattr(decode_repo, "list_scope_results", None) list_payloads = getattr(decode_repo, "list_payload_drafts", None) list_ingests = getattr(decode_repo, "list_ingest_records", None) raw_item = item.model_dump() if hasattr(item, "model_dump") else dict(item) item_payload = CandidateItemSchema.model_validate( { **raw_item, "media_assets": [ MediaAssetSchema.model_validate(row).model_dump(mode="json") for row in media ], } ).model_dump(mode="json") return { "item": item_payload, "decode_result": ( DecodeResultSchema.model_validate(result).model_dump(mode="json") if result is not None else None ), "knowledge_particles": [ KnowledgeParticleSchema.model_validate(row).model_dump(mode="json") for row in (list_particles(item_id=item_id) if list_particles else []) ], "scope_results": [ ScopeResultSchema.model_validate(row).model_dump(mode="json") for row in (list_scopes(item_id=item_id) if list_scopes else []) ], "payload_drafts": [ PayloadDraftSchema.model_validate(row).model_dump(mode="json") for row in (list_payloads(item_id=item_id) if list_payloads else []) ], "ingest_records": [ IngestRecordSchema.model_validate(row).model_dump(mode="json") for row in (list_ingests(item_id=item_id) if list_ingests else []) ], } @router.post("/items/{item_id}/jobs") def enqueue_decode_job( item_id: UUID, repo: Any = Depends(get_decode_repository), ) -> dict[str, Any]: create = getattr(repo, "create_decode_job", None) if create is None: raise HTTPException(status_code=501, detail="decode job writer is not implemented") job = create(item_id=item_id, status="pending") return {"job": job.model_dump(mode="json") if hasattr(job, "model_dump") else job} @router.post("/items/{item_id}/ingest", response_model=IngestItemResponse) def ingest_item_payloads( item_id: UUID, request: IngestItemRequest | None = Body(default=None), repo: Any = Depends(get_decode_repository), ) -> IngestItemResponse: request = request or IngestItemRequest() list_payloads = getattr(repo, "list_payload_drafts", None) if list_payloads is None: raise HTTPException(status_code=501, detail="payload draft reader is not implemented") drafts = list_payloads(item_id=item_id) client = None if not request.dry_run: config = IngestApiConfig.from_env(_env_file()) if not config.url: raise HTTPException(status_code=400, detail="CK_INGEST_API_URL 未配置,真实 ingest 已关闭") client = KnowledgeIngestClient(config) records = [] for draft in drafts: records.append(ingest_payload_draft(repo, draft, dry_run=request.dry_run, client=client)) results = [ IngestItemResultSchema( payload_draft_id=record.payload_draft_id, ingest_record=IngestRecordSchema.model_validate(record), ) for record in records ] ingested_count = sum(1 for record in records if record.status == "ingested") failed_count = sum(1 for record in records if record.status == "failed") return IngestItemResponse( item_id=item_id, dry_run=request.dry_run, total=len(drafts), ingested_count=ingested_count, failed_count=failed_count, results=results, )