Files
ss-tools/specs/035-agent-chat-context/contracts/ux/api-ux.md
busya ff60865183 feat(agent-chat): 035-agent-chat-context — контекст, guardrails, tools, database discovery
== User stories ==
US1: Контекст с дашборда/датасета → /agent с URL params
US2: Guardrails card — env badge, 7 risk tones, countdown, permission_denied
US3: Tools optimization — retry, timeout, summarise, RBAC + context affinity

== Backend ==
- _context.py (NEW): UIContext validation (7 checks)
- _tool_filter.py (NEW): RBAC + context affinity pipeline
- _confirmation.py: build_confirmation_contract_v2, permission_denied_payload
- tools.py: superset_list_databases, retry/summarise/timeout wrappers
- app.py: _inject_uicontext, _inject_env_id_into_tools, database
  prefetch в runtime context
- _persistence.py: prefetch_databases()
- agent_superset_explore.py: GET /databases endpoint
- _llm_async_http.py, _persistence.py: fix double /v1 в LLM URL
  (LM Studio Unexpected endpoint)

== Frontend ==
- AgentChatModel.svelte.ts: 5 atoms, 3 actions, countdown, context
- AgentChat.svelte: production banner, process steps, debug panel
- ConfirmationCard.svelte: 7 risk tones, permission_denied, countdown
- ToolCallCard.svelte: retrying/timeout/cancelled states
- StreamProcessor.svelte.ts: tool_retry, timeout, permission_denied
- TopNavbar: sparkles icon + Ассистент
- sidebarNavigation: AI section
- DashboardHeader, datasets/+page: contextual AI buttons
- Icon: sparkles, brain, cpu icons
- tailwind: assistant category colors
- i18n: en/ru nav keys

== Tests ==
- 159 backend agent tests (+16 US3: retry, timeout, summarise, contracts)
- 2544 frontend tests (+11 model + component tests)
- 15 JSON fixtures (10 API + 5 model)

== Specs ==
- specs/035-agent-chat-context/: spec, UX, plan, tasks, research,
  data-model, contracts, quickstart, traceability, fixtures, checklists

Closes #035
2026-07-04 22:47:17 +03:00

4.7 KiB
Raw Blame History

#region ApiUx [C:3] [TYPE ADR] [SEMANTICS ux,api,agent-chat] @defgroup Ux API shapes and UX expectations for agent chat endpoints.

API 1: Gradio submit — extended payload

Request (Gradio chat submit):

client.submit("chat", [
  { text, files },           // 0: message
  null,                      // 1: (reserved)
  convId,                    // 2: conversation_id (thread_id)
  uicontext,                 // 3: NEW — JSON string or null
  userId,                    // 4: user_id
  userJwt,                   // 5: user JWT
  envId,                     // 6: env_id (from store, may differ from context)
]);

UIContext shape:

{
  "objectType": "dashboard" | "dataset" | "migration" | null,
  "objectId": "42" | null,
  "objectName": "Отчёт по энергопотреблению" | null,
  "envId": "ss-dev" | null,
  "route": "/dashboards/42",
  "contextVersion": 1
}

UX loading: Не применимо — контекст передаётся в существующем submit, loading уже есть.


API 2: SSE Stream Events — extended metadata

confirm_required (extended)

{
  "metadata": {
    "type": "confirm_required",
    "thread_id": "uuid",
    "tool_name": "deploy_dashboard",
    "tool_args": {"env_id": "prod", "dashboard_id": 42},
    "risk": "write",
    "risk_level": "guarded",
    "dangerous": false,
    "env_context": "prod",
    "permission_granted": true,
    "required_role": null,
    "alternatives": null
  }
}

UX: Фронтенд рендерит ConfirmationCard с соответствующим тоном и бейджами. При dangerous:true — активирует 10s таймер.

confirm_required (permission denied)

{
  "metadata": {
    "type": "confirm_required",
    "tool_name": "deploy_dashboard",
    "tool_args": {},
    "risk": "write",
    "risk_level": "guarded",
    "dangerous": false,
    "env_context": "prod",
    "permission_granted": false,
    "required_role": "admin",
    "alternatives": [
      {"action": "get_health_summary", "prompt": "Запросить отчёт о состоянии дашборда"},
      {"action": "export_config", "prompt": "Экспортировать конфигурацию дашборда"}
    ]
  }
}

UX: ConfirmationCard в состоянии permission_denied — только кнопка «Закрыть». После закрытия агент отправляет сообщение с альтернативами.

tool_retry (NEW)

{
  "metadata": {
    "type": "tool_retry",
    "tool": "search_dashboards",
    "attempt": 1,
    "max_attempts": 2
  }
}

UX: ToolCallCard переходит в состояние retrying — текст «Повторная попытка... (1/2)».

tool_timeout (NEW)

{
  "metadata": {
    "type": "tool_timeout",
    "tool": "superset_explore_database",
    "timeout_seconds": 30,
    "retryable": true
  }
}

UX: ToolCallCard переходит в состояние timeout⏱️ иконка + «Превышено время ожидания (30с)» + кнопка [🔄 Повторить].

tool_error (extended)

{
  "metadata": {
    "type": "tool_error",
    "tool": "search_dashboards",
    "error": "Error 502: Bad Gateway",
    "retryable": true,
    "retry_count": 2,
    "retry_exhausted": true,
    "timeout": false
  }
}

UX: При retry_exhausted:true — ToolCallCard показывает + кнопка [🔄 Повторить] для ручного ретрая.


API 3: LLM Status — без изменений

GET /api/agent/llm-status — существующий, без изменений.


API 4: Conversation History — без изменений

GET /api/assistant/conversations — существующий. Контекст НЕ сохраняется в историю (per spec: stateless per-request).


Sequence: Retry Flow

sequenceDiagram
    participant User
    participant Agent as AgentChat.svelte
    participant Backend as Gradio Agent
    participant FastAPI

    User->>Agent: "Найди дашборды"
    Agent->>Backend: submit(...)
    Backend->>Backend: search_dashboards → transient 502
    Backend->>Backend: auto-retry (1s backoff)
    Backend-->>Agent: tool_retry {attempt:1, max:2}
    Agent->>Agent: ToolCallCard: "Повторная попытка..."
    Backend->>FastAPI: retry → 502 again
    Backend-->>Agent: tool_error {retry_exhausted:true}
    Agent->>Agent: ToolCallCard: ❌ + [🔄 Повторить]
    Agent-->>User: Карточка с ошибкой
    User->>Agent: Клик "Повторить"
    Agent->>Backend: повторная отправка (full retry)

#endregion ApiUx