Files
next-tjwater-server/app/api/v1/endpoints/agent_messages.py
T
jiang be6317eb48 feat(server): integrate MCP queries and investigation messaging
Add project-bound OAuth MCP queries and controlled analysis with scoped evidence,
limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs.
Default nearby searches to 200 meters and five results.

Integrate investigation messaging/presentation, health/readiness and typed history;
remove duplicate web search and fix online simulation and compressed baseline writes.
Include project migrations 023-025; keep local credentials and demo seeding excluded.

Validation: 319 focused pytest checks; real PostGIS temporary-table regression;
read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
2026-10-19 18:46:58 +08:00

102 lines
3.6 KiB
Python

"""Actor-private messaging APIs; the Agent verifies its session ownership."""
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query
from psycopg import AsyncConnection
from app.auth.permissions import OPERATIONAL_TASK_VIEW, OPERATIONAL_TASK_WRITE, require_permission
from app.auth.project_dependencies import ProjectContext, get_project_pg_connection
from app.domain.domain_errors import DomainError
from app.domain.schemas.agent_messages import (
AgentMessage, AgentMessageCreate, AgentMessageDelivery, AgentMessagePage, AgentMessageReply,
)
from app.infra.db.postgresql.agent_messages import AgentMessageRepository
from app.services.agent_messages import AgentMessageService
router = APIRouter(prefix="/agent-messages")
def _service(ctx: ProjectContext, conn: AsyncConnection) -> AgentMessageService:
return AgentMessageService(AgentMessageRepository(conn, ctx.project_id), ctx)
def _raise_domain(exc: DomainError):
raise HTTPException(status_code=exc.status, detail=exc.payload()) from exc
@router.post("", response_model=AgentMessage, status_code=201)
async def create_agent_message(
command: AgentMessageCreate,
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_WRITE)),
conn: AsyncConnection = Depends(get_project_pg_connection),
):
try:
return await _service(ctx, conn).create(command)
except DomainError as exc:
_raise_domain(exc)
@router.get("", response_model=AgentMessagePage)
async def list_agent_messages(
session_id: str = Query(min_length=1, max_length=500),
limit: int = Query(default=50, ge=1, le=100),
cursor: str | None = None,
pending_only: bool = False,
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
conn: AsyncConnection = Depends(get_project_pg_connection),
):
try:
return await _service(ctx, conn).list_session(session_id, limit, cursor, pending_only)
except DomainError as exc:
_raise_domain(exc)
@router.get("/pending", response_model=AgentMessagePage)
async def list_pending_agent_messages(
limit: int = Query(default=50, ge=1, le=100),
cursor: str | None = None,
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
conn: AsyncConnection = Depends(get_project_pg_connection),
):
try:
return await _service(ctx, conn).pending(limit, cursor)
except DomainError as exc:
_raise_domain(exc)
@router.get("/{message_id}", response_model=AgentMessage)
async def get_agent_message(
message_id: UUID,
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
conn: AsyncConnection = Depends(get_project_pg_connection),
):
try:
return await _service(ctx, conn).get(message_id)
except DomainError as exc:
_raise_domain(exc)
@router.post("/{message_id}/reply", response_model=AgentMessage, status_code=201)
async def reply_agent_message(
message_id: UUID, command: AgentMessageReply,
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_WRITE)),
conn: AsyncConnection = Depends(get_project_pg_connection),
):
try:
return await _service(ctx, conn).reply(message_id, command)
except DomainError as exc:
_raise_domain(exc)
@router.post("/{message_id}/delivery", response_model=AgentMessage)
async def record_agent_message_delivery(
message_id: UUID, command: AgentMessageDelivery,
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
conn: AsyncConnection = Depends(get_project_pg_connection),
):
try:
return await _service(ctx, conn).delivery(message_id, command)
except DomainError as exc:
_raise_domain(exc)