3.8 KiB
3.8 KiB
#region AgentChat.ApiUx [C:3] [TYPE ADR] [SEMANTICS ux,api,agent-chat,final]
@defgroup Ux Final API contract — Gradio submit(), LangChain v1 HITL via second submit(), PostgreSQL checkpointer, structured metadata.
Gradio Transport
Connection: Client.connect("/api/agent/gradio") → nginx proxy → Gradio. JWT forwarded.
Primary submit: app.submit("/chat", {message, conversation_id}) — two-arg API. conversation_id is an additional_inputs key.
Examples on UI (for additional_inputs): [["Покажи дашборды", null], ["Статус системы", null]].
Cancel: submission.cancel().
Streaming Events
for await (event of submission) — event.data is a ChatMessage with structured metadata:
metadata.type |
metadata fields |
UX Reaction |
|---|---|---|
stream_token |
{token: "При"} |
Append token to last assistant msg |
tool_start |
{tool: "search_dashboards", input: {...}} |
Tool card: name + spinner |
tool_end |
{tool: "search_dashboards", output: {...}} |
Spinner→checkmark |
tool_error |
{tool: "search_dashboards", error: "..."} |
Spinner→cross + detail |
confirm_required |
{prompt: "Подтвердить деплой?", thread_id: "..."} |
Confirmation card + timer |
confirm_resolved |
{result: "confirmed"/"denied"} |
Card collapses |
error |
{code: "LLM_UNAVAILABLE", detail: "..."} |
Error card + retry |
| (none) | plain {content: "..."} |
Normal text |
HITL Resume Protocol
Primary: submit("/chat", {message, conversation_id})
→ handler: agent.astream_events(config={"thread_id": conversation_id})
→ HITL fires → checkpoint saved (PostgreSQL)
→ handler: yield {metadata: {type: "confirm_required", thread_id: conversation_id}}
→ Svelte: renders confirmation card (stream still open)
Confirm: submit("/chat", message, [conversation_id, "confirm"])
→ handler: detects "confirm" in additional_inputs[1]
→ loads checkpoint by conversation_id as thread_id
→ invokes graph with Command(resume={"action": "confirm"}, config={"thread_id": conversation_id})
Deny: submit("/chat", message, [conversation_id, "deny"])
→ handler: Command(resume={"action": "deny"}, config=...)
REST Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/api/assistant/conversations |
Create → {id, title, created_at} |
GET |
/api/assistant/conversations |
Paginated list + search |
GET |
/api/assistant/history |
Paginated messages |
POST |
/api/assistant/conversations/{id}/messages |
Append after stream_end |
DELETE |
/api/assistant/conversations/{id} |
Soft-delete (is_archived=true) |
GET |
/api/agent/conversations/{id}/active |
Multi-tab gate |
POST |
/api/auth/service-token |
Gradio obtains service JWT |
Dual Identity RBAC
Gradio handler:
service_jwt = POST /api/auth/service-token (authenticates the agent service)
user_jwt = request.headers["Authorization"] (browser user)
@tool function:
call FastAPI with:
Authorization: Bearer {service_jwt} (agent is authorized to call)
X-User-JWT: {user_jwt} (operation authorized under user)
FastAPI endpoint:
validates service JWT → agent is legitimate
validates X-User-JWT → user has RBAC permission for this operation
audit: {service_actor: "agent", user_actor: user_id, operation, ...}
Error Taxonomy
| Code | Source | UX |
|---|---|---|
UNAUTHORIZED |
JWT | Redirect /login |
FORBIDDEN |
RBAC | metadata.type="error" in stream |
LLM_UNAVAILABLE |
LangChain | metadata.type="error" → card + retry |
GRADIO_UNREACHABLE |
connect | Dot red, retry 5×5s |
TOOL_FAILURE |
FastAPI | metadata.type="tool_error" |
#endregion AgentChat.ApiUx