Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/agent_manager/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from agent_engine.runtime.streaming import RunStreamEvent
from agent_manager.api.deps import get_service
from agent_manager.api.schemas import (
ConversationSummary,
CreateConversationRequest,
CreateConversationResponse,
MessageOut,
Expand All @@ -23,6 +24,7 @@
ToolRecord,
)
from agent_manager.application import (
ConversationAccessDenied,
ConversationNotFound,
ConversationService,
ConversationTokenBudgetExceeded,
Expand All @@ -42,6 +44,26 @@ async def create_conversation(
return CreateConversationResponse(conversation_id=session_id, session_id=session_id)


@router.get("/conversations", response_model=list[ConversationSummary])
async def list_conversations(service: Service, user_id: str) -> list[ConversationSummary]:
"""List a user's conversations.

`user_id` is a caller-supplied identifier, not an authenticated principal —
it scopes the listing, it does not authorize it. A deployment that needs a
real boundary puts auth in front of this router (or overrides `get_service`)
and derives the id from the verified credential instead of the query string.
"""
sessions = await service.list_conversations(user_id)
return [
ConversationSummary(
conversation_id=s.session_id,
title=s.title,
last_message_at=s.last_message_at,
)
for s in sessions
]


@router.get("/conversations/{conversation_id}/messages", response_model=list[MessageOut])
async def list_messages(conversation_id: str, service: Service) -> list[MessageOut]:
try:
Expand Down Expand Up @@ -73,6 +95,8 @@ async def send_message(
result = await service.send(conversation_id, body.message, user_id=body.user_id)
except ConversationNotFound as exc:
raise HTTPException(status_code=404, detail="conversation not found") from exc
except ConversationAccessDenied:
raise HTTPException(status_code=403, detail="conversation owned by another user") from None
except ConversationTokenBudgetExceeded:
raise HTTPException(status_code=429, detail="conversation token budget exceeded") from None
except Exception as exc: # engine failure
Expand Down Expand Up @@ -115,6 +139,8 @@ async def stream_message(
first = None
except ConversationNotFound as exc:
raise HTTPException(status_code=404, detail="conversation not found") from exc
except ConversationAccessDenied:
raise HTTPException(status_code=403, detail="conversation owned by another user") from None
except ConversationTokenBudgetExceeded:
raise HTTPException(status_code=429, detail="conversation token budget exceeded") from None

Expand Down
6 changes: 6 additions & 0 deletions src/agent_manager/api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class CreateConversationResponse(BaseModel):
session_id: str


class ConversationSummary(BaseModel):
conversation_id: str
title: str | None = None
last_message_at: datetime | None = None


class MessageOut(BaseModel):
role: Role
content: str
Expand Down
Loading
Loading