Files
ss-tools/specs/033-gradio-agent-chat/contracts/ux/agent-chat-ux.md
2026-06-09 09:43:34 +03:00

5.4 KiB
Raw Blame History

#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.Model atoms → component props → DOM
  • @gradio/client submit()for await (event)model._onStreamData()$state update
  • No $effect for 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