""" 插件运行日志 API 端点 提供 run 状态查询、事件列表、artifact 元数据和下载。 """ import os from pathlib import Path from fastapi import APIRouter, HTTPException from fastapi.responses import FileResponse from app.plugins.run_log import get_run_log_service router = APIRouter() @router.get("/{plugin_name}/runs/{run_id}") async def get_run(plugin_name: str, run_id: str): """获取运行记录详情。""" service = get_run_log_service() run = service.get_run(run_id) if run is None: raise HTTPException(status_code=404, detail=f"运行记录不存在: {run_id}") if run.plugin_name != plugin_name: raise HTTPException(status_code=404, detail=f"运行记录不属于插件: {plugin_name}") return run.model_dump(mode="json") @router.get("/{plugin_name}/runs/{run_id}/events") async def list_run_events(plugin_name: str, run_id: str): """获取运行事件列表。""" service = get_run_log_service() run = service.get_run(run_id) if run is None: raise HTTPException(status_code=404, detail=f"运行记录不存在: {run_id}") if run.plugin_name != plugin_name: raise HTTPException(status_code=404, detail=f"运行记录不属于插件: {plugin_name}") events = service.list_events(run_id) return {"run_id": run_id, "events": [e.model_dump(mode="json") for e in events]} @router.get("/{plugin_name}/runs/{run_id}/artifacts") async def list_run_artifacts(plugin_name: str, run_id: str): """获取运行产物列表。""" service = get_run_log_service() run = service.get_run(run_id) if run is None: raise HTTPException(status_code=404, detail=f"运行记录不存在: {run_id}") if run.plugin_name != plugin_name: raise HTTPException(status_code=404, detail=f"运行记录不属于插件: {plugin_name}") artifacts = service.list_artifacts(run_id) return {"run_id": run_id, "artifacts": [a.model_dump(mode="json") for a in artifacts]} @router.get("/{plugin_name}/runs/{run_id}/artifacts/{artifact_id}") async def get_artifact_meta(plugin_name: str, run_id: str, artifact_id: str): """获取产物元数据。""" service = get_run_log_service() run = service.get_run(run_id) if run is None: raise HTTPException(status_code=404, detail=f"运行记录不存在: {run_id}") if run.plugin_name != plugin_name: raise HTTPException(status_code=404, detail=f"运行记录不属于插件: {plugin_name}") artifact = service.get_artifact(run_id, artifact_id) if artifact is None: raise HTTPException(status_code=404, detail=f"产物不存在: {artifact_id}") return artifact.model_dump(mode="json") @router.get("/{plugin_name}/runs/{run_id}/artifacts/{artifact_id}/download") async def download_artifact(plugin_name: str, run_id: str, artifact_id: str): """下载产物文件。""" service = get_run_log_service() run = service.get_run(run_id) if run is None: raise HTTPException(status_code=404, detail=f"运行记录不存在: {run_id}") if run.plugin_name != plugin_name: raise HTTPException(status_code=404, detail=f"运行记录不属于插件: {plugin_name}") artifact = service.get_artifact(run_id, artifact_id) if artifact is None: raise HTTPException(status_code=404, detail=f"产物不存在: {artifact_id}") try: file_path = service.get_artifact_path(run_id, artifact_id) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) if file_path is None or not file_path.exists(): raise HTTPException(status_code=404, detail="产物文件不存在") return FileResponse( path=str(file_path), filename=artifact.filename, media_type=artifact.media_type, )