5.4 KiB
5.4 KiB
#region AgentChat.Ux [C:3] [TYPE ADR] [SEMANTICS ux,agent-chat,panel,native-stack] @defgroup Ux UX contract — Gradio submit() + LangChain v1 astream_events(). Structured JSON metadata (ChatMessage.metadata). No REST, no WebSocket, no follower.
FSM
idle ──[submit("/chat")]──→ streaming ──[metadata.type: stream_token]──→ (tokens appear)
streaming ──[metadata.type: tool_start]──→ (tool card + spinner)
streaming ──[metadata.type: tool_end]──→ (tool done, checkmark)
streaming ──[metadata.type: tool_error]──→ (tool error, cross)
streaming ──[metadata.type: confirm_required]──→ awaiting_confirmation
awaiting_confirmation ──[2nd submit(__resume__,"confirm")]──→ streaming (confirm_resolved)
awaiting_confirmation ──[2nd submit(__resume__,"deny")]──→ idle (confirm_resolved: denied)
awaiting_confirmation ──[timeout 5min]──→ idle (expired)
streaming ──[final chunk]──→ idle
streaming ──[submission.cancel()]──→ idle (partial)
streaming ──[metadata.type: error]──→ error
idle ──[load history]──→ loading_history ──[success]──→ idle
loading_history ──[failure]──→ error
idle ──[connect fail]──→ disconnected ──[retry 5×5s]──→ idle
disconnected ──[retry exhausted]──→ disconnected_permanent
State Mappings
| @UX_STATE | Visual | ARIA | User Can |
|---|---|---|---|
| idle (with msgs) | Input enabled, Send active, Stop hidden. Connection dot green. | — | Send, switch/delete convos, expand to /agent |
| idle (new convo) | Welcome card + examples chips (Gradio examples). |
aria-label="Новый диалог" |
Click chip, type |
| streaming | Tokens appear in real time. Tool cards inline. Send→Stop (destructive). Input locked. | aria-live="polite" aria-busy="true" |
Stop, expand tool cards |
| awaiting_confirmation | Streaming pauses. Warning card: bg-warning-light border-warning-DEFAULT. Prompt text + Confirm/Deny buttons. Timer 5:00→0:00. |
role="alertdialog" |
Confirm, Deny |
| awaiting_confirmation (expired) | Card stays. Buttons disabled. Timer=0. "Повторить запрос?" | — | Retry |
| error | Card: bg-destructive-light border-destructive-ring. Error + Retry. Partial text preserved. |
role="alert" |
Retry, new message |
| disconnected | Dot red. Banner: "Агент недоступен. Переподключение через Xс..." | role="alert" |
Read history, manual reconnect |
| disconnected_permanent | Dot red. Manual reconnect button only. | role="alert" |
Manual reconnect |
| loading_history | Skeleton cards (3-5, animate-pulse bg-surface-muted). Input locked. |
aria-busy="true" |
Wait |
Feedback
| Trigger | Feedback |
|---|---|
metadata.type: stream_token |
Append token to assistant message |
metadata.type: tool_start |
Tool card: name (mono) + spinner |
metadata.type: tool_end |
Spinner→checkmark, result in tooltip |
metadata.type: tool_error |
Spinner→cross, error in tooltip |
metadata.type: confirm_required |
Confirmation card + timer, Confirm/Deny |
metadata.type: confirm_resolved(result=confirmed) |
Card collapses, streaming resumes |
metadata.type: confirm_resolved(result=denied) |
Card collapses, agent responds |
| Stream complete | Stop→Send, input enabled |
| Connection lost | Dot red. Banner. Retry countdown |
| Connection restored | Dot green. Banner dismissed |
Recovery
| From | Action | To |
|---|---|---|
| error | Retry (re-sends last msg) | streaming |
| disconnected | Auto-retry 5×5s or manual | idle |
| awaiting_confirmation (expired) | "Повторить запрос" | streaming |
| streaming (cancelled) | Type new msg | idle→streaming |
| empty (no convos) | Click chip or type | streaming |
Reactivity
AgentChat.Modelatoms → component props → DOM@gradio/clientsubmit()→for await (event)→model._onStreamData()→$stateupdate- No
$effectfor data loading — all via stream events assistantChatStore.conversationId→ model reads (read-only)
UX Tests
| @UX_TEST | Given | When | Then |
|---|---|---|---|
| Happy path | idle (new) | Type "привет", Send | streaming, tokens appear, idle |
| Tool call | streaming | Agent calls tool | Card with spinner→checkmark |
| Dangerous confirm | streaming | Agent wants deploy | "⏸️" card, confirm_id, timer |
| Confirm accepted | awaiting_confirmation | 2nd submit(resume) | "▶️", streaming resumes |
| Confirm denied | awaiting_confirmation | 2nd submit(deny) | "⏹️", idle |
| Stream cancel | streaming | submission.cancel() | idle, partial text |
| LLM error | streaming | LLM fails | "Ошибка: ..." + retry |
| Gradio disconnect | idle | Gradio dies | Dot red, 5×5s retry |
| Multi-tab gate | idle | Tab 2 opens same convo | Tab 2: send rejected |
| Empty convo | idle (new) | Load | Examples chips visible |
| History load error | idle | API fails | Error + retry |
#endregion AgentChat.Ux