ADK-gateway/backend/app/api/cli.py
2026-08-05 16:33:27 +08:00

98 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""CLI 接口:任务提交/查询/取消/完成事件订阅。"""
from fastapi import APIRouter, Depends, Request
from fastapi.responses import StreamingResponse
from app.models.schemas import TaskInfo, TaskSubmit
from app.services.notifier import TaskNotifier
from app.services.security import require_auth
from app.services.task_service import TaskService
router = APIRouter()
def get_task_service(request: Request) -> TaskService:
return TaskService(request.app.state.redis)
def get_notifier(request: Request) -> TaskNotifier:
return TaskNotifier(request.app.state.redis)
@router.get("/events", summary="订阅任务完成事件SSE 长连接)")
async def subscribe_events(
request: Request,
cli_session_id: str,
auth: str = "",
notifier: TaskNotifier = Depends(get_notifier),
):
"""任务到达终态时网关主动推送完成事件,无需轮询。
返回 SSE 流text/event-stream每帧形如
data: {"event": "task_done", "request_id": "...", "status": "success", "result": {...}}
"""
require_auth(auth)
channel = notifier.channel(cli_session_id)
pubsub = request.app.state.redis.pubsub()
await pubsub.subscribe(channel)
replay = await notifier.replay(cli_session_id)
async def event_gen():
try:
# 先回放已完成的(连接晚于任务完成的场景)
for evt in replay:
yield f"data: {evt}\n\n"
# 再实时监听
async for message in pubsub.listen():
if await request.is_disconnected():
break
if message["type"] != "message":
continue
data = message["data"]
if isinstance(data, bytes):
data = data.decode()
yield f"data: {data}\n\n"
finally:
await pubsub.unsubscribe(channel)
await pubsub.aclose()
return StreamingResponse(
event_gen(),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
)
@router.post("/tasks", response_model=TaskInfo, summary="提交任务")
async def submit_task(body: TaskSubmit, svc: TaskService = Depends(get_task_service)):
require_auth(body.auth)
return await svc.submit(body)
@router.get("/tasks/{request_id}", response_model=TaskInfo, summary="查询任务")
async def get_task(
request_id: str,
auth: str = "",
svc: TaskService = Depends(get_task_service),
):
from fastapi import HTTPException
require_auth(auth)
task = await svc.get(request_id)
if not task:
raise HTTPException(status_code=404, detail="task not found")
return task
@router.post("/tasks/{request_id}/cancel", summary="取消任务")
async def cancel_task(
request_id: str,
auth: str = "",
svc: TaskService = Depends(get_task_service),
):
from fastapi import HTTPException
require_auth(auth)
if not await svc.cancel(request_id):
raise HTTPException(status_code=400, detail="cannot cancel task")
return {"ok": True, "request_id": request_id}