| """ |
| 插件运行日志 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, |
| ) |
|
|