diff --git a/examples/starter/.env.example b/examples/starter/.env.example index 5e8733aa..232aed1d 100644 --- a/examples/starter/.env.example +++ b/examples/starter/.env.example @@ -21,3 +21,10 @@ OPENAI_BASE_URL=http://host.docker.internal:11434/v1 # Optional — read by the bank_name resolver. # BANK_NAME=Northwind Bank + +# Optional — a per-conversation *cumulative* token budget: every turn's input + +# output tokens, summed over the life of the conversation. This is not the +# model's context window. Once a conversation has spent this many tokens in +# total, further turns are refused with a 429; the widget shows how full the +# budget is so the user sees it coming. +# CONTEXT_MAX_TOKENS=2000 diff --git a/src/agent_manager/api/routes.py b/src/agent_manager/api/routes.py index 445b2f9b..cbcfa0e3 100644 --- a/src/agent_manager/api/routes.py +++ b/src/agent_manager/api/routes.py @@ -19,6 +19,7 @@ SendMessageRequest, SendMessageResponse, StreamEventOut, + TokenBudgetResponse, ToolRecord, ) from agent_manager.application import ( @@ -50,6 +51,20 @@ async def list_messages(conversation_id: str, service: Service) -> list[MessageO return [MessageOut(role=m.role, content=m.content, created_at=m.created_at) for m in msgs] +@router.get("/conversations/{conversation_id}/usage", response_model=TokenBudgetResponse) +async def get_usage(conversation_id: str, service: Service) -> TokenBudgetResponse: + try: + usage = await service.usage(conversation_id) + except ConversationNotFound as exc: + raise HTTPException(status_code=404, detail="conversation not found") from exc + return TokenBudgetResponse( + used_tokens=usage.used_tokens, + max_tokens=usage.max_tokens, + percent=usage.percent, + severity=usage.severity, + ) + + @router.post("/conversations/{conversation_id}/messages", response_model=SendMessageResponse) async def send_message( conversation_id: str, body: SendMessageRequest, service: Service diff --git a/src/agent_manager/api/schemas.py b/src/agent_manager/api/schemas.py index 3777ee5c..1c2452a2 100644 --- a/src/agent_manager/api/schemas.py +++ b/src/agent_manager/api/schemas.py @@ -10,7 +10,7 @@ from pydantic import BaseModel -from agent_manager.domain import Role +from agent_manager.domain import BudgetSeverity, Role class CreateConversationRequest(BaseModel): @@ -49,6 +49,13 @@ class SendMessageResponse(BaseModel): used_tools: list[ToolRecord] +class TokenBudgetResponse(BaseModel): + used_tokens: int + max_tokens: int | None = None + percent: float = 0.0 + severity: BudgetSeverity = BudgetSeverity.NORMAL + + class StreamEventOut(BaseModel): type: str content: str | None = None diff --git a/src/agent_manager/api/static/demo.html b/src/agent_manager/api/static/demo.html index db9313bc..852e0161 100644 --- a/src/agent_manager/api/static/demo.html +++ b/src/agent_manager/api/static/demo.html @@ -21,16 +21,15 @@
<script type="module" src="https://your-backend/widget.js"></script> -<agent-chat title="Home Assistant" color="#2563eb"></agent-chat>+<agent-chat title="Support" color="#18181b"></agent-chat> -
↘ The launcher is in the bottom-right corner. Click it and try - “turn on the kitchen lights”, then “now turn it off”.
+↘ The launcher is in the bottom-right corner. Click it and ask a question.