Improve agent UX and spec sync
This commit is contained in:
@@ -30,6 +30,9 @@
|
||||
- [x] CHK016 SC-006: Speed improvement ≥30% vs manual UI — measurable via usability benchmark.
|
||||
- [x] CHK017 SC-007: File upload ≤10MB without responsiveness degradation — measurable via load test.
|
||||
- [x] CHK018 SC-008: Hybrid router ensures 50-75% token reduction; embedding model cold-start ~2s, subsequent calls 5-20ms.
|
||||
- [x] CHK018a SC-009: Hidden runtime/prefetch/upload context is not visible in chat bubbles or conversation list titles.
|
||||
- [x] CHK018b SC-010: No-first-event stream surfaces recovery controls within 60-65 seconds.
|
||||
- [x] CHK018c SC-011: Diagnostics are available without exposing debug fields as primary chat actions.
|
||||
|
||||
## 4. Edge Cases & Recovery
|
||||
|
||||
@@ -42,6 +45,9 @@
|
||||
- [x] CHK025 Confirmation token expiry → inform user, offer re-issue (Edge Cases).
|
||||
- [x] CHK026 Multiple browser tabs with same conversation → reject concurrent writes (Edge Cases).
|
||||
- [x] CHK027 Network loss during streaming → detect disconnection, offer reconnect (Edge Cases).
|
||||
- [x] CHK027a Silent Gradio/LLM stream → first-activity timeout and recovery card.
|
||||
- [x] CHK027b User accidentally copies debug JSON → diagnostics are grouped in menu and raw JSON is not rendered into chat by default.
|
||||
- [x] CHK027c Hidden runtime context in prompt/history → frontend strips service blocks and backend persists clean visible text.
|
||||
|
||||
## 5. Decision-Memory Readiness
|
||||
|
||||
@@ -59,7 +65,7 @@
|
||||
- [x] CHK036 P2 stories cover UX quality: tool visibility + conversation context.
|
||||
- [x] CHK037 P3 stories cover polish: persistence + file upload.
|
||||
- [x] CHK038 Key entities defined: AgentConversation, AgentMessage, AgentToolCall, AgentStreamingSession, AgentMemory.
|
||||
- [x] CHK039 23 functional requirements cover: streaming, tool use, hybrid routing, negation guard, embedding infrastructure, RBAC, persistence, UX preservation, backward compatibility.
|
||||
- [x] CHK039 Functional requirements cover: streaming, tool use, hybrid routing, negation guard, embedding infrastructure, RBAC, persistence, hidden runtime context, first-activity recovery, diagnostics, UX preservation, backward compatibility.
|
||||
- [x] CHK040 Dependencies section names specific existing artifacts to integrate with.
|
||||
|
||||
## 7. Constitution Alignment
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
| Contract | C | File | Notes |
|
||||
|----------|---|------|-------|
|
||||
| AgentChat.GradioApp | C4 | `backend/src/agent/app.py` | `gr.ChatInterface(type="messages", multimodal=True, additional_inputs=[gr.Textbox("conversation_id"), gr.Textbox("action")], examples=[["Покажи дашборды",null,null],["Статус системы",null,null]])`. Handler: `def handler(message, history, request: gr.Request, conversation_id=None, action=None)`. Per-user lock via in-memory dict (no global concurrency_limit — Gradio handles concurrent users by default). |
|
||||
| AgentChat.GradioApp.Handler | C4 | ↑ | Creates `create_react_agent()` with PostgresSaver and a compact tool subset. `astream_events(config={"configurable":{"thread_id":conversation_id}})`. Resume: detects `action="confirm"/"deny"` in additional_inputs, loads checkpoint, recreates graph with `interrupt_before=[]` to avoid repeated pause, and continues the checkpoint stream. |
|
||||
| AgentChat.LangGraph.Setup | C4 | `backend/src/agent/langgraph_setup.py` | `create_react_agent(model, tools=<subset>, checkpointer=PostgresSaver(conn_string), interrupt_before=<dangerous tools or []>)`. No `RunnableWithMessageHistory`; history/checkpoint continuity is keyed by `thread_id=conversation_id`. |
|
||||
| AgentChat.GradioApp.Handler | C4 | ↑ | Creates `create_react_agent()` with PostgresSaver and a compact tool subset. `astream_events(config={"configurable":{"thread_id":conversation_id}})`. Resume: detects `action="confirm"/"deny"` in additional_inputs, loads checkpoint, recreates graph with `interrupt_before=[]` to avoid repeated pause, and continues the checkpoint stream. Injects hidden runtime context into LLM-facing message while saving clean visible user text. |
|
||||
| AgentChat.LangGraph.Setup | C4 | `backend/src/agent/langgraph_setup.py` | `create_react_agent(model, tools=<subset>, checkpointer=PostgresSaver(conn_string), interrupt_before=<dangerous tools or []>)`. No `RunnableWithMessageHistory`; history/checkpoint continuity is keyed by `thread_id=conversation_id`. System prompt includes maintenance relative-time rules using runtime context. |
|
||||
| AgentChat.Tools | C4 | `backend/src/agent/tools.py` | 24 native `@tool` functions. Each: Pydantic `BaseModel` args_schema, calls FastAPI REST with dual-identity headers. Includes hybrid `get_tools_for_query()` — keyword primary + embedding fallback for context-budgeted subset selection. |
|
||||
| AgentChat.EmbeddingRouter | C3 | `backend/src/agent/_embedding_router.py` | Lazy-loaded `paraphrase-multilingual-MiniLM-L12-v2`. Tool description corpus (RU+EN). `embedding_top_k(query)` → top-K tool names above cosine threshold. Graceful degradation to empty list if model unavailable. |
|
||||
| AgentChat.Middleware | C3 | `backend/src/agent/middleware.py` | `LoggingMiddleware` only (audit trail). |
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
| Contract | C | File | Notes |
|
||||
|----------|---|------|-------|
|
||||
| AgentChat.Model | C4 | `frontend/src/lib/models/AgentChatModel.svelte.ts` | `submit("/chat", {text,files}, [conversationId, action])` lifecycle. Structured `ChatMessage.metadata` parsing. HITL resume via second submit with `additional_inputs[1]="confirm"/"deny"` and immediate streaming state. No tabRole, no follower. |
|
||||
| AgentChat.Panel | C4 | `frontend/src/lib/components/agent/AgentChat.svelte` | `Client.connect("${window.location.origin}/api/agent/gradio")`, `submit()`, metadata-based rendering of tool cards + confirmation cards, debug/reference block. |
|
||||
| AgentChat.Page | C3 | `frontend/src/routes/agent/+page.svelte` | Full-page route hosting current `AgentChat.svelte`. |
|
||||
| AgentChat.Model | C4 | `frontend/src/lib/models/AgentChatModel.svelte.ts` | `submit("/chat", {text,files}, [conversationId, action])` lifecycle. Structured `ChatMessage.metadata` parsing. HITL resume via second submit with `additional_inputs[1]="confirm"/"deny"` and immediate streaming state. Owns first-activity timeout, recovery actions, visible-text cleaning, process-step derivation. No tabRole, no follower. |
|
||||
| AgentChat.Panel | C4 | `frontend/src/lib/components/agent/AgentChat.svelte` | `Client.connect("${window.location.origin}/api/agent/gradio")`, `submit()`, metadata-based rendering of tool cards + confirmation cards, operator process strip, diagnostics menu, recovery card. |
|
||||
| AgentChat.Page | C3 | `frontend/src/routes/agent/+page.svelte` | Full-page route hosting current `AgentChat.svelte`; syncs auth and selected environment before sends. |
|
||||
| AgentChat.ConversationList | C3 | `frontend/src/lib/components/assistant/ConversationList.svelte` | Search, infinite scroll, date grouping, archive action. |
|
||||
| AgentChat.ToolCallCard | C2 | `frontend/src/lib/components/assistant/ToolCallCard.svelte` | Props from `metadata.type="tool_start"/"tool_end"/"tool_error"`. |
|
||||
| AgentChat.ConfirmationCard | C3 | `frontend/src/lib/components/assistant/ConfirmationCard.svelte` | Renders on `metadata.type="confirm_required"`. Calls second `submit()` with `additional_inputs=[conversation_id, "confirm"/"deny"]`. |
|
||||
|
||||
@@ -14,6 +14,7 @@ idle ──[submit("/chat")]──→ streaming ──[metadata.type: stream_tok
|
||||
awaiting_confirmation ──[timeout 5min]──→ idle (expired)
|
||||
streaming ──[final chunk]──→ idle
|
||||
streaming ──[submission.cancel()]──→ idle (partial)
|
||||
streaming ──[no first event 60s]──→ error (recovery)
|
||||
streaming ──[metadata.type: error]──→ error
|
||||
|
||||
idle ──[load history]──→ loading_history ──[success]──→ idle
|
||||
@@ -29,13 +30,16 @@ idle ──[connect fail]──→ disconnected ──[retry 5×5s]──→ idl
|
||||
|-----------|--------|------|----------|
|
||||
| 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 |
|
||||
| streaming | Tokens appear in real time. Tool cards inline. Send→Stop (destructive). Input locked. Operator process strip marks understanding/tool step active. | `aria-live="polite" aria-busy="true"` | Stop, expand tool cards |
|
||||
| streaming_silent | No token/tool/confirmation yet. Process strip shows "Ожидаю первый ответ". First-activity watchdog active. | `aria-live="polite"` | Stop |
|
||||
| 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 |
|
||||
| error | Recovery card: `bg-destructive-light border-destructive-ring`. Error + actions: retry last request, check LLM, copy diagnostics, new dialog. Partial text preserved. | `role="alert"` | Retry, check LLM, copy debug, new dialog |
|
||||
| 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 |
|
||||
| operator_status | Header strip shows user-facing state and process steps: context, understanding/LLM wait, tools, confirmation, result. | `aria-live="polite"` | Read state |
|
||||
| diagnostics_menu | Header menu contains debug-panel toggle, copy JSON, LLM check. It closes on Escape and outside click. | `role="menu"` | Toggle debug, copy JSON, check LLM |
|
||||
| debug_visible | Header reference block shows `conv_id`, pending `thread_id`, connection/streaming state, env/user, message count, last message id, active tool-call count, error. | — | Copy JSON, hide block |
|
||||
|
||||
## Feedback
|
||||
@@ -50,6 +54,7 @@ idle ──[connect fail]──→ disconnected ──[retry 5×5s]──→ idl
|
||||
| `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 |
|
||||
| No first activity for 60s | Recovery card with retry/check LLM/copy debug/new dialog |
|
||||
| Connection lost | Dot red. Banner. Retry countdown |
|
||||
| Connection restored | Dot green. Banner dismissed |
|
||||
|
||||
@@ -58,6 +63,8 @@ idle ──[connect fail]──→ disconnected ──[retry 5×5s]──→ idl
|
||||
| From | Action | To |
|
||||
|------|--------|-----|
|
||||
| error | Retry (re-sends last msg) | streaming |
|
||||
| error | Check LLM | error/idle with updated banner |
|
||||
| error | New dialog | idle |
|
||||
| disconnected | Auto-retry 5×5s or manual | idle |
|
||||
| awaiting_confirmation (expired) | "Повторить запрос" | streaming |
|
||||
| streaming (cancelled) | Type new msg | idle→streaming |
|
||||
@@ -81,9 +88,12 @@ idle ──[connect fail]──→ disconnected ──[retry 5×5s]──→ idl
|
||||
| Confirm denied | awaiting_confirmation | 2nd submit with `additional_inputs[1]="deny"` | Card resolves, idle |
|
||||
| Stream cancel | streaming | submission.cancel() | idle, partial text |
|
||||
| LLM error | streaming | LLM fails | "Ошибка: ..." + retry |
|
||||
| Silent LLM hang | streaming_silent | 60s without token/tool/confirmation | Recovery card, no endless "Думаю" |
|
||||
| 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 |
|
||||
| Hidden context display | history/current bubble | Message contains `[PRE-FETCHED DATA]` or uploaded content block | Visible text excludes service block |
|
||||
| Diagnostics menu | idle | Open menu, press Escape | Menu closes; debug panel state unchanged |
|
||||
|
||||
#endregion AgentChat.Ux
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
|
||||
**Connection**: `Client.connect("${window.location.origin}/api/agent/gradio")` → Vite/nginx same-origin proxy → Gradio. JWT forwarded.
|
||||
|
||||
**Primary submit**: `client.submit("/chat", {text, files}, [conversation_id, null])` — three-arg API. `additional_inputs=[conversation_id, action]` maps to Gradio textboxes. Handler: `handler(message, history, request, conversation_id, action)`.
|
||||
**Primary submit**: `client.submit("/chat", {text, files}, [conversation_id, null, user_id, user_jwt, env_id])`. `additional_inputs=[conversation_id, action, user_id, user_jwt, env_id]` maps to Gradio inputs. Handler uses persisted history/checkpoints and ignores Gradio-provided history.
|
||||
|
||||
**Cancel**: `submission.cancel()`.
|
||||
|
||||
**First-activity watchdog**: frontend races stream processing with a 60s no-first-event timeout. Activity means any token, tool call, confirmation/checkpoint, or pending thread id.
|
||||
|
||||
### Streaming Events
|
||||
|
||||
`for await (event of submission)` — `event.data` is a `ChatMessage` with structured `metadata`:
|
||||
@@ -21,9 +23,20 @@
|
||||
| `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 |
|
||||
| `error` | `{code: "LLM_UNAVAILABLE", detail: "..."}` | Recovery card |
|
||||
| (none) | plain `{content: "..."}` | Normal text |
|
||||
|
||||
### Hidden Runtime Context
|
||||
|
||||
The handler MAY enrich the LLM-facing message with runtime context:
|
||||
|
||||
- current ISO datetime;
|
||||
- selected `env_id`;
|
||||
- relative-time rules for maintenance requests;
|
||||
- compact dashboard prefetch.
|
||||
|
||||
This context is not part of the persisted user message and MUST NOT appear in `/api/assistant/history` or conversation list titles.
|
||||
|
||||
### HITL Resume Protocol
|
||||
|
||||
```
|
||||
@@ -99,6 +112,7 @@ Embedding fallback triggered when keyword matching yields <3 tools.
|
||||
| `UNAUTHORIZED` | JWT | Redirect /login |
|
||||
| `FORBIDDEN` | RBAC | metadata.type="error" in stream |
|
||||
| `LLM_UNAVAILABLE` | LangChain | metadata.type="error" → card + retry |
|
||||
| `NO_FIRST_ACTIVITY_TIMEOUT` | Frontend watchdog | Recovery card + debug copy/check LLM |
|
||||
| `GRADIO_UNREACHABLE` | connect | Dot red, retry 5×5s |
|
||||
| `TOOL_FAILURE` | FastAPI | metadata.type="tool_error" |
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
| **Cancel** | A — Единая кнопка Stop с момента отправки. `submission.cancel()` | Нативный Gradio cancel |
|
||||
| **Confirmation timeout** | A — Карточка остаётся, кнопки заблокированы, «Повторить» | Явный контроль |
|
||||
| **Reconnect** | A — 5×5s, затем disconnected_permanent + ручная кнопка | Предсказуемо |
|
||||
| **Silent stream** | A — 60s first-activity timeout → recovery card | Gradio/LLM can hang before first token without a connection error; endless "Думаю" is misleading |
|
||||
| **Ошибка истории** | A — Сразу ошибка, без кэша | Консистентность |
|
||||
| **Multi-tab** | A — Client-side gate: `GET /api/agent/conversations/{id}/active` перед отправкой. Отклоняет вторую вкладку | Просто, без server-push. Per-user mutex в handler-е + REST gate; градио обрабатывает множественных пользователей нативно |
|
||||
| **Debug actions** | B — Diagnostics menu instead of primary header buttons | Keeps operator UI focused while preserving copyable state for debugging |
|
||||
|
||||
### Phase 3 — Interaction Design
|
||||
|
||||
@@ -29,6 +31,8 @@
|
||||
| **Отправка** | Gradio `submit()` с `additional_inputs=[conversation_id]` | Нативный механизм. История и checkpoint continuity keyed by `conversation_id` / `thread_id` |
|
||||
| **Stop** | `submission.cancel()` — optimistic UI | Нативный Gradio cancel. Частичный текст сохранён |
|
||||
| **Подтверждение** | Second `submit()` с `additional_inputs[1]="confirm"/"deny"` | Два submit() к одному стриму. LangGraph `interrupt_before` + PostgresSaver checkpoint; resume uses `interrupt_before=[]` |
|
||||
| **Операционный статус** | Header process strip from model-derived steps | Gives non-technical operators a stable mental model before backend exposes explicit progress events |
|
||||
| **Recovery** | Inline recovery card with retry/check LLM/copy debug/new dialog | Keeps failure handling in the same task context; avoids requiring users to paste debug JSON into chat |
|
||||
|
||||
### Phase 4 — API Design
|
||||
|
||||
@@ -44,5 +48,7 @@
|
||||
- **Ручная передача Gradio history** — rejected: handler ignores Gradio `history`; persisted messages/checkpoints are keyed by `conversation_id`
|
||||
- **WebSocket-based active/follower multi-tab pattern** — rejected: сложный server-push, требует pub/sub на Gradio. Client-side gate + per-user mutex — accepted.
|
||||
- **@assistant_tool registry для новых тулов** — rejected: нативные @tool функции LangChain
|
||||
- **Always-visible debug buttons** — rejected: they looked like primary chat actions and encouraged copying service JSON into normal messages
|
||||
- **No timeout before first token** — rejected: upstream hangs make the UI appear active while no work is visible
|
||||
|
||||
#endregion AgentChat.UxDecisions
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
| Confirmation (convo delete) | `confirm()` (native browser) | Pattern |
|
||||
| Custom confirmation (in-chat) | New `ConfirmationCard` inline within message | New component |
|
||||
| Tool-call card | New `ToolCallCard` inline within message | New component |
|
||||
| Operator status strip | Inline header band bound to `AgentChat.Model.processSteps` | Pattern |
|
||||
| Diagnostics menu | Header menu using native buttons + `$lib/ui/Icon` | Pattern |
|
||||
| Recovery card | Inline alert panel in message area | Pattern |
|
||||
|
||||
---
|
||||
|
||||
@@ -98,6 +101,31 @@
|
||||
| Messages area | `flex-1 overflow-y-auto bg-surface-page` |
|
||||
| Input area | `border-t border-border bg-surface-card p-4` |
|
||||
|
||||
### Operator Status Strip
|
||||
| Element | Token |
|
||||
|---------|-------|
|
||||
| Strip shell | `rounded-lg border border-border bg-surface-page px-3 py-2` |
|
||||
| Success step | `border-success bg-success-light text-success` |
|
||||
| Active step | `border-warning bg-warning-light text-warning` |
|
||||
| Blocked step | `border-destructive-ring bg-destructive-light text-destructive` |
|
||||
| Idle step | `border-border bg-surface-page text-text-subtle` |
|
||||
|
||||
### Diagnostics Menu
|
||||
| Element | Token |
|
||||
|---------|-------|
|
||||
| Trigger | `border-border-strong bg-surface-card text-text hover:bg-surface-muted` |
|
||||
| Active trigger | `border-primary-ring bg-primary-light text-primary` |
|
||||
| Menu | `rounded-lg border border-border bg-surface-card shadow-lg` |
|
||||
| Menu item | `text-text hover:bg-surface-muted` |
|
||||
|
||||
### Recovery Card
|
||||
| Element | Token |
|
||||
|---------|-------|
|
||||
| Card | `border-destructive-ring bg-destructive-light rounded-2xl` |
|
||||
| Icon well | `bg-white text-destructive rounded-full` |
|
||||
| Primary recovery action | `border-destructive-ring bg-white text-destructive hover:bg-destructive-light` |
|
||||
| Secondary recovery action | `border-border-strong bg-white text-text hover:bg-surface-muted` |
|
||||
|
||||
### Welcome / Empty
|
||||
| Element | Token |
|
||||
|---------|-------|
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#region AgentChat.ScreenModels [C:3] [TYPE ADR] [SEMANTICS ux,screen-models,agent-chat]
|
||||
@BRIEF Screen model inventory — revised for LangChain v1 native stack. No active/follower. No WebSocket.
|
||||
@RATIONALE Model-first per ADR-0010. AgentChatModel handles Gradio submit() lifecycle, streaming state, tool-call tracking, multi-tab gate.
|
||||
@RATIONALE Model-first per ADR-0010. AgentChatModel handles Gradio submit() lifecycle, streaming state, tool-call tracking, recovery, diagnostics, and operator process visibility.
|
||||
|
||||
## Screens Requiring Models
|
||||
|
||||
| Screen | Route | Model ID | Complexity | Rationale |
|
||||
|--------|-------|----------|------------|-----------|
|
||||
| AssistantChatPanel (drawer + embedded) | overlay | `AgentChat.Model` | C4 | Gradio `submit()` lifecycle, streaming, tool-call parsing, confirmation card state, connection health |
|
||||
| Agent Chat Page (full) | `/agent` | `AgentChat.Model` (shared) | C4 | Same model, two-column layout |
|
||||
| Agent Chat Page (full) | `/agent` | `AgentChat.Model` (shared) | C4 | Same model, two-column layout, environment sync, operator process strip, diagnostics menu |
|
||||
|
||||
## Screens Using Inline State
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
| ConversationList | `conversations[]` из модели. Поиск — локальный. Группировка — `$derived` |
|
||||
| ToolCallCard | Пропсы от родителя |
|
||||
| ConfirmationCard | Пропсы от модели (`confirm_id`, `prompt`) |
|
||||
| DiagnosticsMenu | Локальное раскрытие в `AgentChat.svelte`; команды делегируются модели |
|
||||
| ProcessStrip | `$derived` из `AgentChat.Model.processSteps`, `userFacingStatusTitle`, `userFacingStatusDetail` |
|
||||
| RecoveryCard | Рендерится из `model.streamingState === "error"` и вызывает `retryLastUserMessage()`, `checkLlmStatus()`, `createConversation()` |
|
||||
| MessageBubble | Рендерит одно сообщение. Стриминг — через `$derived` |
|
||||
| ConnectionIndicator | `model.connectionState` → dot color |
|
||||
|
||||
@@ -23,7 +26,17 @@
|
||||
|
||||
| Threshold | Est. | Action |
|
||||
|-----------|:----:|--------|
|
||||
| Lines > 400 | ~220 | Within limit |
|
||||
| Methods > 40 | ~12 | Within limit |
|
||||
| Lines > 400 | ~900 | Exceeded; decomposition exists via StreamProcessor, ConnectionManager, LocalStorage. Further split should target UI subcomponents, not state ownership. |
|
||||
| Methods > 40 | ~25 | Within limit |
|
||||
|
||||
## Derived Operator State
|
||||
|
||||
| Field | Source | Purpose |
|
||||
|-------|--------|---------|
|
||||
| `hasSilentStream` | `streamingState`, `partialText`, `partialTokens`, `activeToolCalls`, `pendingThreadId` | Detect first-event hang before endless "thinking" |
|
||||
| `userFacingStatusTitle` | connection/streaming/error/tool state | Short operator state label |
|
||||
| `userFacingStatusDetail` | env/error/streaming state | Recovery/context hint |
|
||||
| `processSteps` | env/tool/confirmation/error/message state | Header process strip |
|
||||
| `cleanVisibleMessageText()` | raw message text | Strip hidden runtime/prefetch/upload blocks before rendering/retry |
|
||||
|
||||
#endregion AgentChat.ScreenModels
|
||||
|
||||
@@ -36,6 +36,27 @@ Tables: `agent_conversations`, `agent_messages`. Legacy `assistant_messages` pre
|
||||
|
||||
`frontend/src/types/agent.ts` — matches Pydantic schemas. Includes `StreamMetadata`.
|
||||
|
||||
## 4a. Frontend Screen-State Derivations
|
||||
|
||||
`AgentChatModel` owns operator-only derived state. These fields are not persisted and do not cross the API boundary:
|
||||
|
||||
```typescript
|
||||
type AgentProcessStepState = "done" | "active" | "blocked" | "idle";
|
||||
|
||||
interface AgentProcessStep {
|
||||
id: "context" | "reasoning" | "tools" | "confirmation" | "result";
|
||||
label: string;
|
||||
state: AgentProcessStepState;
|
||||
}
|
||||
```
|
||||
|
||||
Derived state:
|
||||
|
||||
- `hasSilentStream`: `streaming` with no token, no tool call, no confirmation/checkpoint.
|
||||
- `userFacingStatusTitle` / `userFacingStatusDetail`: operator-readable status copy.
|
||||
- `processSteps`: UI process strip; currently inferred from stream/tool/confirmation/error state.
|
||||
- `cleanVisibleMessageText(rawText)`: strips hidden runtime/prefetch/upload context before rendering or retrying.
|
||||
|
||||
## 5. PostgreSQL Checkpointer
|
||||
|
||||
LangGraph checkpointer: `langgraph-checkpoint-postgres`. Thread ID = conversation_id. Survives container restarts. Same PostgreSQL instance as FastAPI.
|
||||
@@ -49,4 +70,21 @@ X-User-JWT: {user_jwt}
|
||||
|
||||
Audit: `{service_actor: "agent", user_actor: user_id}`.
|
||||
|
||||
## 7. Hidden Runtime Context
|
||||
|
||||
The backend may prepend hidden runtime context to the LLM-facing message. This is not a persisted message field.
|
||||
|
||||
```text
|
||||
[RUNTIME CONTEXT]
|
||||
Current datetime: <ISO>
|
||||
Environment: <env_id>
|
||||
Rules: interpret now/start/duration from current datetime...
|
||||
Dashboards: compact bounded prefetch...
|
||||
[/RUNTIME CONTEXT]
|
||||
|
||||
<visible user text>
|
||||
```
|
||||
|
||||
Persistence invariant: `agent_messages.text`, title generation input, and frontend-rendered user bubbles use the visible user text only.
|
||||
|
||||
#endregion AgentChat.DataModel
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Implementation Plan: Gradio Agent Chat (LangGraph)
|
||||
|
||||
**Branch**: `033-gradio-agent-chat` | **Date**: 2026-06-08 | **Updated**: 2026-06-30 | **Spec**: `spec.md`
|
||||
**Branch**: `033-gradio-agent-chat` | **Date**: 2026-06-08 | **Updated**: 2026-07-03 | **Spec**: `spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Gradio agent backend + Svelte frontend via `@gradio/client submit()`. LangGraph `create_react_agent()` + static `interrupt_before` + `PostgresSaver`. Hybrid keyword+embedding tool router (keyword primary <1ms, embedding fallback 5-20ms). Negation guard for fast-track HITL. Smart dashboard prefetch trigger. Structured JSON metadata streaming. No REST confirmations, no custom WebSocket, no deprecated assistant tool registry for agent runtime.
|
||||
Gradio agent backend + Svelte frontend via `@gradio/client submit()`. LangGraph `create_react_agent()` + static `interrupt_before` + `PostgresSaver`. Hybrid keyword+embedding tool router (keyword primary <1ms, embedding fallback 5-20ms). Negation guard for fast-track HITL. Smart dashboard prefetch trigger. Hidden runtime context for operational prompts. Structured JSON metadata streaming. Operator recovery for silent streams. No REST confirmations, no custom WebSocket, no deprecated assistant tool registry for agent runtime.
|
||||
|
||||
## Current Implementation Snapshot (2026-06-30)
|
||||
## Current Implementation Snapshot (2026-07-03)
|
||||
|
||||
- `/agent` works through SvelteKit `AgentChat.svelte` and `AgentChatModel.svelte.ts`, connecting with `@gradio/client` to `${window.location.origin}/api/agent/gradio`.
|
||||
- `DEV_MODE=true ./run.sh` wires `BACKEND_URL`, `GRADIO_URL`, and `GRADIO_SERVER_PORT`; `backend/src/agent/run.py` allows port fallback only with `GRADIO_ALLOW_PORT_FALLBACK=true`.
|
||||
@@ -14,10 +14,11 @@ Gradio agent backend + Svelte frontend via `@gradio/client submit()`. LangGraph
|
||||
- `backend/src/agent/_embedding_router.py` provides embedding-based tool similarity fallback (cosine similarity via `paraphrase-multilingual-MiniLM-L12-v2`).
|
||||
- `fast_confirmation_tool()` includes negation guard (`\bне\b`, `\bno\b`) to bypass fast-track for negated requests.
|
||||
- Dashboard prefetch uses intent-aware trigger (excludes creation/diagnostic patterns).
|
||||
- Agent handler injects hidden runtime context (current datetime, selected environment, relative-time rules, bounded dashboard prefetch) into the LLM-facing message while saving clean visible user text.
|
||||
- `run_llm_validation` uses `/api/validation-tasks`; Git, maintenance, migration, backup, and documentation tools call their existing FastAPI REST endpoints with dual identity headers.
|
||||
- HITL resume path recreates the agent with `interrupt_before=[]`, preventing repeated guardrail cards after confirm.
|
||||
- `/agent` exposes debug info: `conv_id`, pending `thread_id`, connection/streaming state, env/user, message count, last message id, active tool calls, and error.
|
||||
- Verified: backend agent tests `375 passed`; frontend `AgentChatModel.test.ts` `73 passed`; frontend build passed.
|
||||
- `/agent` synchronizes selected environment before each send and exposes operator process strip, diagnostics menu, debug JSON copy, first-activity timeout, visible-text cleaning, and recovery card.
|
||||
- Verified: backend hidden-context regression passed; frontend `AgentChatModel.test.ts` `88 passed`; frontend build passed with existing unrelated Svelte warnings.
|
||||
|
||||
## Technical Context
|
||||
|
||||
@@ -57,4 +58,10 @@ The runtime MUST use the hybrid router: keyword primary + embedding fallback.
|
||||
Tool subset is 5-10 tools (50-75% token reduction vs full 24-tool catalog).
|
||||
Embedding model (`paraphrase-multilingual-MiniLM-L12-v2`) lazy-loaded on first fallback call; graceful degradation to keyword-only if `sentence-transformers` unavailable.
|
||||
Dashboard-prefetched requests MUST avoid adding `search_dashboards` to the tool schema (prefetch data already in context).
|
||||
|
||||
## Operator UX Recovery Rule
|
||||
|
||||
The UI MUST not display endless "thinking" when the upstream stream has no first observable event.
|
||||
`AgentChatModel` races stream processing with a 60s first-activity timeout. If no token, tool call, confirmation/checkpoint, or pending thread id arrives, it transitions to an actionable recovery state.
|
||||
The recovery card provides retry of the last clean user request, LLM status check, debug JSON copy, and new-dialog escape.
|
||||
#endregion (plan summary)
|
||||
|
||||
@@ -46,7 +46,11 @@ AUTH_SECRET_KEY=test-secret-key-for-agent-run-tests backend/.venv/bin/python -m
|
||||
backend/tests/test_agent/test_run.py
|
||||
|
||||
# Current frontend model slice
|
||||
cd frontend && npm run test -- src/lib/models/__tests__/AgentChatModel.test.ts --run
|
||||
cd frontend && npm run test -- AgentChatModel.test.ts
|
||||
|
||||
# Hidden runtime-context regression
|
||||
AUTH_SECRET_KEY=test-secret JWT_SECRET=test-secret backend/.venv/bin/python -m pytest \
|
||||
backend/tests/test_agent/test_app.py::TestAgentHandler::test_normal_send_hides_runtime_context_from_saved_history -q
|
||||
|
||||
# Frontend build
|
||||
cd frontend && npm run build
|
||||
@@ -71,7 +75,10 @@ GRADIO_ALLOW_PORT_FALLBACK=false
|
||||
## Troubleshooting
|
||||
|
||||
- If LM Studio or another local OpenAI-compatible runtime returns `request exceeds the available context size`, verify the request is using `get_tools_for_query()` and keep `AGENT_PREFETCH_DASHBOARD_LIMIT` at or below `25`.
|
||||
- The `/agent` header has a toggleable debug block. Use it to inspect `conv_id`, pending `thread_id`, connection/streaming state, active tool calls, and current error.
|
||||
- The `/agent` header has a `Диагностика` menu. Use it to show the debug panel, copy JSON diagnostics, or force an LLM status check. The debug panel includes `conv_id`, pending `thread_id`, connection/streaming state, env/user, active tool calls, and current error.
|
||||
- The `/agent` process strip is the operator-facing source of truth for current UI state. `Ожидаю первый ответ` means the request reached streaming state but no first token/tool/confirmation has arrived yet.
|
||||
- If the recovery card appears after 60 seconds, use `Проверить LLM` first, then retry the last request. `Скопировать debug` is intended for bug reports and must not be pasted as a normal chat message.
|
||||
- Repeated guardrail cards after pressing Confirm indicate a regression: resume must create the graph with `interrupt_before=[]`.
|
||||
- If an agent asks for exact ISO datetime despite a relative maintenance request ("сейчас", "на 15 минут"), verify hidden runtime context is present in the LLM-facing message and absent from saved history.
|
||||
|
||||
#endregion AgentChat.Quickstart
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
**Decision**: `@tool`-decorated functions with Pydantic `args_schema`. Each tool calls FastAPI REST via HTTP with dual-identity headers (`Authorization: Bearer <service_jwt>` + `X-User-JWT: <user_jwt>`).
|
||||
**Rationale**: Single source of metadata. Auto OpenAI function schema. Old `@assistant_tool` → `@DEPRECATED`.
|
||||
**Rejected**: Dual registration (`@assistant_tool` + `StructuredTool`) — redundant.
|
||||
**Current fact (2026-06-30)**: `backend/src/agent/tools.py` contains 24 native LangChain tools plus `get_tools_for_query()` for hybrid intent-based subset selection. Agent runtime does not call the deprecated assistant registry.
|
||||
**Current fact (2026-07-03)**: `backend/src/agent/tools.py` contains 24 native LangChain tools plus `get_tools_for_query()` for hybrid intent-based subset selection. Agent runtime does not call the deprecated assistant registry.
|
||||
|
||||
## 3. Confirmation: `interrupt_before=DANGEROUS_TOOLS`
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
**Rejected**: Full 24-tool catalog — causes Tool Paralysis in Gemma-level models.
|
||||
**Rejected**: Embedding-only — adds 5-20ms to every request, blind to negations.
|
||||
**Rejected**: Pure substring with cosmetic fixes — cannot handle "панели"≠"дашборды".
|
||||
**Current fact (2026-06-30)**: `get_tools_for_query()` uses keyword primary with embedding fallback in `_embedding_router.py`. `fast_confirmation_tool()` uses negation guard.
|
||||
**Current fact (2026-07-03)**: `get_tools_for_query()` uses keyword primary with embedding fallback in `_embedding_router.py`. `fast_confirmation_tool()` uses negation guard.
|
||||
|
||||
## 10. Embedding Model Selection
|
||||
|
||||
@@ -69,6 +69,16 @@
|
||||
- `bge-small-en-v1.5` (130MB) — EN-only. Rejected.
|
||||
- `intfloat/multilingual-e5-small` (470MB) — best quality but heavier. Kept as configurable alternative.
|
||||
- `rubert-tiny2` (110MB) — RU-only, no EN. Rejected.
|
||||
**Current fact (2026-06-30)**: Model not yet deployed; planned in Phase 1 implementation of hybrid router.
|
||||
**Current fact (2026-07-03)**: `_embedding_router.py` is implemented with lazy model loading and graceful fallback. Runtime can degrade to keyword-only if `sentence-transformers` or model weights are unavailable.
|
||||
|
||||
## 11. Hidden Runtime Context and Operator Recovery
|
||||
|
||||
**Decision**: Runtime data needed by the agent (current ISO datetime, selected environment, maintenance duration rules, bounded dashboard prefetch) is injected into the LLM-facing message and stripped from persisted/user-visible text.
|
||||
**Rationale**: The agent needs operational context, but showing service blocks in the chat damages trust and makes users copy internal JSON back into the conversation.
|
||||
**Rejected**: Rendering prefetch/runtime blocks in user bubbles — confuses users and pollutes conversation titles.
|
||||
|
||||
**Decision**: `/agent` uses a first-activity watchdog and recovery card when Gradio/LLM produces no first event for 60 seconds.
|
||||
**Rationale**: A stream can hang before any token/tool event while the connection remains technically open; the UI must distinguish "thinking" from "no observable progress".
|
||||
**Rejected**: Infinite thinking indicator — misleading and not recoverable.
|
||||
|
||||
#endregion AgentChat.Research
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
@REJECTED @assistant_tool for new tools — native LangChain `@tool` is SSOT. Old registry → @DEPRECATED for FR-020.
|
||||
|
||||
**Feature Branch**: `033-gradio-agent-chat`
|
||||
**Created**: 2026-06-08 | **Status**: Implemented fact snapshot | **Last revised**: 2026-06-30 (hybrid router, negation guard, embedding fallback)
|
||||
**Created**: 2026-06-08 | **Status**: Implemented fact snapshot | **Last revised**: 2026-07-03 (operator UX recovery, hidden runtime context, environment sync)
|
||||
|
||||
## Current Implementation Facts (2026-06-30)
|
||||
## Current Implementation Facts (2026-07-03)
|
||||
|
||||
- `/agent` is served by SvelteKit `AgentChat.svelte` + `AgentChatModel.svelte.ts`; the browser connects through `@gradio/client` to absolute `${window.location.origin}/api/agent/gradio`.
|
||||
- `/agent` synchronizes the selected Superset environment from `environmentContextStore` and falls back to `localStorage.selected_env_id` after `initializeEnvironmentContext()` so Gradio receives `env_id` before every send.
|
||||
- The backend injects hidden `[RUNTIME CONTEXT]` into the LLM message: current ISO datetime, environment id, relative-time rules, and bounded dashboard prefetch. The persisted/user-visible message stays clean.
|
||||
- `DEV_MODE=true ./run.sh` starts FastAPI, frontend, and Gradio with explicit `BACKEND_URL`, `GRADIO_URL`, `GRADIO_SERVER_PORT`; Gradio no longer silently falls back to a random port unless `GRADIO_ALLOW_PORT_FALLBACK=true`.
|
||||
- `backend/src/agent/tools.py` is the agent tool SSOT. It exposes 24 native LangChain `@tool` functions: `show_capabilities`, `search_dashboards`, `get_health_summary`, `list_environments`, `get_task_status`, `list_llm_providers`, `get_llm_status`, `create_branch`, `commit_changes`, `deploy_dashboard`, `execute_migration`, `run_backup`, `run_llm_validation`, `run_llm_documentation`, `list_maintenance_events`, `start_maintenance`, `end_maintenance`, `superset_execute_sql`, `superset_explore_database`, `superset_audit_permissions`, `superset_create_dashboard`, `superset_copy_dashboard`, `superset_create_dataset`, `superset_format_sql`.
|
||||
- Deprecated `backend/src/api/routes/assistant/_tool_registry.py` is legacy only and MUST NOT be used by the agent runtime.
|
||||
@@ -20,8 +22,12 @@
|
||||
- **Negation guard** in `fast_confirmation_tool()`: regex pre-check for negation patterns (`\bне\b`, `\bno\b`, `\bdon't\b`) bypasses fast-track and routes to LLM. Prevents false-positive confirmations for negated requests like "не показывай схему".
|
||||
- **Smart prefetch trigger**: dashboard prefetch fires only for search/list intent, excludes creation/diagnostic patterns (`как создать`, `почему`, `speed`, `performance`). Capped by `AGENT_PREFETCH_DASHBOARD_LIMIT` (default `25`).
|
||||
- HITL guardrails use LangGraph `interrupt_before` for dangerous tools. Resume creates the agent with `interrupt_before=[]` so the confirmed checkpoint does not pause again before the same tool.
|
||||
- The `/agent` header includes a collapsible debug/reference block with `conv_id`, pending `thread_id`, connection state, streaming state, env/user, message count, last message id, active tool-call count, and current error.
|
||||
- Current verification: backend agent tests `375 passed`; frontend `AgentChatModel.test.ts` `73 passed`; frontend build passed with existing unrelated Svelte warnings.
|
||||
- The `/agent` header includes an operator status strip: user-facing state, environment context, and process steps (`Контекст`, `Понимание задачи`, `Инструменты`, `Подтверждение`, `Результат`). These steps are currently derived from client state and tool metadata; explicit backend progress events remain a future hardening target.
|
||||
- Debug/reference actions are behind a `Диагностика` menu (debug panel, copy JSON, check LLM) so service data does not compete with primary chat actions.
|
||||
- User-visible message rendering and conversation titles strip hidden context (`[PRE-FETCHED DATA]`, uploaded-file content blocks). System-only history titles such as `✅ list_environments` normalize to `Системное действие`.
|
||||
- First-activity timeout: if Gradio/LLM emits no token, tool call, confirmation, or thread id within 60s, the stream is converted into an actionable error/recovery state instead of endless "Думаю".
|
||||
- Recovery card offers `Повторить последний запрос`, `Проверить LLM`, `Скопировать debug`, and `Новый диалог`.
|
||||
- Current verification: targeted backend hidden-context test passed; frontend `AgentChatModel.test.ts` `88 passed`; frontend build passed with existing unrelated Svelte warnings.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
@@ -111,6 +117,7 @@ Browser (SvelteKit) Docker
|
||||
### Edge Cases
|
||||
- Gradio unreachable → reconnect 5×5s
|
||||
- LLM malformed → retry once, then error
|
||||
- LLM/Gradio accepts the request but emits no first event → first-activity timeout after 60s and recovery card
|
||||
- Message too long → truncate + warn
|
||||
- Tool >30s → async, result when ready
|
||||
- Multi-tab → REST gate: `GET /api/agent/conversations/{id}/active` rejects second tab
|
||||
@@ -183,6 +190,18 @@ Browser (SvelteKit) Docker
|
||||
|
||||
- **FR-036 — Embedding Router Infrastructure**: `backend/src/agent/_embedding_router.py` MUST provide: (a) tool description corpus (RU+EN, 1-3 sentences per tool) for embedding; (b) lazy-loaded `paraphrase-multilingual-MiniLM-L12-v2` model (configurable via `EMBEDDING_MODEL` env var); (c) pre-embedded tool descriptions at load time; (d) `embedding_top_k(query)` returning tool names above cosine threshold. Fails gracefully (returns `[]`) when `sentence-transformers` unavailable.
|
||||
|
||||
- **FR-037 — Hidden Runtime Context**: The agent handler MUST pass runtime context to the LLM without persisting or rendering it as user text. Runtime context includes current ISO datetime, current environment, relative-time interpretation rules, and compact dashboard prefetch when available. Saved conversation history and generated titles MUST use the clean user message.
|
||||
|
||||
- **FR-038 — Environment Sync**: `/agent` MUST sync `env_id` before each send from the selected environment store, with a localStorage fallback during initial route hydration. Debug info MUST show `env_id` so missing context is visible during diagnosis.
|
||||
|
||||
- **FR-039 — Operator Process Strip**: `/agent` MUST show user-facing operational status and process steps for agent work. Minimum steps: context readiness, task understanding/LLM wait, tool execution, confirmation, result. If backend lacks explicit progress metadata, the UI MAY derive step state from streaming/tool/confirmation/error state, but this must be documented as heuristic.
|
||||
|
||||
- **FR-040 — First-Activity Timeout Recovery**: If a stream enters `streaming` but no token, tool call, confirmation, or checkpoint/thread id arrives within 60 seconds, the frontend MUST cancel/return the submission where possible and show an actionable recovery panel.
|
||||
|
||||
- **FR-041 — Clean Visible Message Text**: UI history, current user bubbles, and conversation titles MUST strip hidden prefetch/runtime/upload blocks. System-only titles SHOULD normalize to a human-readable system label.
|
||||
|
||||
- **FR-042 — Diagnostics Menu**: Debug/reference actions MUST be grouped behind a diagnostics menu. The menu MUST expose debug-panel toggle, JSON copy, and LLM status check; it MUST be keyboard dismissible and not render raw JSON into chat.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- **SC-001**: Tool selection ≥92% accuracy (keyword 85% + embedding fallback covers synonym/typo remainder)
|
||||
@@ -193,6 +212,9 @@ Browser (SvelteKit) Docker
|
||||
- **SC-006**: Multi-step ≥30% faster than manual UI
|
||||
- **SC-007**: File upload ≤10MB, no degradation
|
||||
- **SC-008**: Hybrid router ensures 50-75% token reduction (5-10 tools vs 24 full catalog). Embedding model lazy-loaded on first fallback call (cold-start ~2s), subsequent calls 5-20ms.
|
||||
- **SC-009**: No hidden runtime/prefetch/upload context is visible in chat bubbles or conversation list titles.
|
||||
- **SC-010**: A no-first-event stream surfaces recovery controls within 60-65 seconds.
|
||||
- **SC-011**: Operator can copy diagnostics and verify LLM status from `/agent` without exposing debug fields as primary chat actions.
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#region AgentChat.Tasks [C:3] [TYPE ADR] [SEMANTICS tasks,implementation,agent-chat]
|
||||
@BRIEF Implementation tasks for Gradio Agent Chat — phased by user story, dependency-ordered, with contract inlining for C3+ functions.
|
||||
|
||||
## Current Fact Update — 2026-06-30
|
||||
## Current Fact Update — 2026-07-03
|
||||
|
||||
- [x] T097 [FACT] Replace deprecated assistant tool registry for agent runtime with 24 native LangChain `@tool` functions in `backend/src/agent/tools.py`.
|
||||
@POST Agent tools are sourced from `backend/src/agent/tools.py`; `_tool_registry.py` remains legacy/deprecated only.
|
||||
@@ -22,7 +22,7 @@
|
||||
@POST `DEV_MODE=true ./run.sh` passes deterministic backend/Gradio URLs, frontend connects to same-origin `/api/agent/gradio`, and Gradio port fallback is opt-in via `GRADIO_ALLOW_PORT_FALLBACK=true`.
|
||||
|
||||
- [x] T103 [FACT] Current verification snapshot.
|
||||
@POST Backend agent tests: `375 passed`. Frontend AgentChatModel tests: `73 passed`. Frontend build: passed with existing unrelated Svelte warnings.
|
||||
@POST Superseded by T111. Historical verification snapshot retained for chronology; current verification is T111.
|
||||
|
||||
- [x] T104 [FACT] Implement negation guard in `fast_confirmation_tool()`.
|
||||
@POST Regex `\b(?:не|нет|no|don't|do not|stop|отмена|отмени)\b` pre-check bypasses fast-track → routes to LLM. Prevents false-positive confirmations for negated requests.
|
||||
@@ -30,6 +30,24 @@
|
||||
- [x] T105 [FACT] Create `backend/src/agent/_embedding_router.py` — lazy-loaded embedding model, tool description corpus, `embedding_top_k()`.
|
||||
@POST Cosine similarity fallback when keyword matching <3 tools. Tool descriptions cover all 24 tools in RU+EN. Model: `paraphrase-multilingual-MiniLM-L12-v2` (configurable). Graceful degradation to `[]` if `sentence-transformers` unavailable.
|
||||
|
||||
- [x] T106 [FACT] Inject hidden runtime context for agent runs while persisting only the clean user message.
|
||||
@POST Backend sends current datetime, env id, relative-time rules, and bounded dashboard prefetch to the LLM; saved history/title input excludes `[RUNTIME CONTEXT]`.
|
||||
|
||||
- [x] T107 [FACT] Sync `/agent` environment context before each Gradio submit.
|
||||
@POST `initializeEnvironmentContext()` hydrates environment state; `syncModel()` uses selected environment with `localStorage.selected_env_id` fallback. Debug shows `env/user`.
|
||||
|
||||
- [x] T108 [FACT] Strip hidden context from visible chat/history UI.
|
||||
@POST Current user bubbles, loaded history, localStorage history, and conversation titles strip `[PRE-FETCHED DATA]` and uploaded-file content blocks. System-only tool titles normalize to `Системное действие`.
|
||||
|
||||
- [x] T109 [FACT] Add first-activity timeout and recovery UX.
|
||||
@POST If no token/tool/confirmation/checkpoint appears within 60 seconds, the frontend exits endless thinking and shows recovery actions: retry last request, check LLM, copy debug, new dialog.
|
||||
|
||||
- [x] T110 [FACT] Add operator process strip and diagnostics menu.
|
||||
@POST `/agent` shows user-facing process steps and moves debug actions into a keyboard-dismissible diagnostics menu.
|
||||
|
||||
- [x] T111 [FACT] Current verification snapshot.
|
||||
@POST Backend hidden-context regression: passed. Frontend AgentChatModel tests: `88 passed`. Frontend build: passed with existing unrelated Svelte warnings.
|
||||
|
||||
## Phase 1: Setup (shared infrastructure)
|
||||
|
||||
- [ ] T001 [P] Add `gradio>=5.0`, `langgraph>=0.2`, `langchain-core>=0.3`, `langchain-openai>=0.3`, `langgraph-checkpoint-postgres`, `pdfplumber` to `backend/requirements.txt`
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
| **US6**: File Upload | AssistantChatPanel | AgentChat.Model | FX_AgentChat.Document.ParsePdf.Valid | `submit("/chat", {text, files})` | T065, T066 | T068, T069 | Test.AgentChat.Document |
|
||||
| **US6**: PDF Encrypted | — | AgentChat.Document | FX_AgentChat.Document.ParsePdf.Encrypted | — | T065 | — | Test.AgentChat.Document |
|
||||
| **US6**: XLSX Password | — | AgentChat.Document | FX_AgentChat.Document.ParseXlsx.PasswordProtected | — | T065 | — | Test.AgentChat.Document |
|
||||
| **—**: Debug Info | `/agent` | AgentChat.Model | — | UI-derived state | T101 | T073 | Browser snapshot |
|
||||
| **—**: Hidden Runtime Context | `/agent` | AgentChat.Handler | — | `submit("/chat")` LLM-facing message | T106 | T108 | Test.Agent.App.HiddenContext |
|
||||
| **—**: Env Sync | `/agent` | AgentChat.Model | — | `submit("/chat", ..., env_id)` | T107 | T107 | Browser snapshot |
|
||||
| **—**: First Activity Recovery | `/agent` | AgentChat.Model | — | Gradio stream watchdog | T109 | T109 | Test.AgentChat.Model |
|
||||
| **—**: Operator Status Strip | `/agent` | AgentChat.Model | — | UI-derived state | T110 | T110 | Test.AgentChat.Model + Browser snapshot |
|
||||
| **—**: Diagnostics Menu | `/agent` | AgentChat.Model | — | UI-derived state | T101 | T110 | Browser snapshot |
|
||||
| **—**: DEV Mode Transport | `/agent` | AgentChat.GradioApp | — | `/api/agent/gradio` same-origin proxy | T102 | T073 | Browser snapshot + run tests |
|
||||
| **—**: Service Auth | — | — | FX_AgentChat.ServiceToken.Valid | `POST /api/auth/service-token` | T080 | — | Test.AgentChat.Api |
|
||||
| **—**: Rejected Paths | — | AgentChat.Model | FX_AgentChat.Model.RejectedPath | — | — | — | Test.AgentChat.Model.RejectedPaths |
|
||||
@@ -42,6 +46,9 @@
|
||||
| `fast_confirmation_tool()` negation guard | — | Test.IntentKeyword.Edges | Gradio agent handler |
|
||||
| Document parser | FX_AgentChat.Document.ParsePdf.Valid, ParseXlsx.Valid | Test.AgentChat.Document | Gradio agent handler |
|
||||
| `@gradio/client` transport | FX_AgentChat.Model.RejectedPath (WebSocket guardrail) | Test.AgentChat.Model.RejectedPaths | AssistantChatPanel |
|
||||
| Hidden runtime context / visible text cleaning | — | Test.Agent.App.HiddenContext, Test.AgentChat.Model | /agent, ConversationList |
|
||||
| First-activity timeout and recovery card | — | Test.AgentChat.Model + browser snapshot | /agent |
|
||||
| Diagnostics menu and process strip | — | Test.AgentChat.Model + browser snapshot | /agent |
|
||||
| `POST /api/auth/service-token` | FX_AgentChat.ServiceToken.Valid, InvalidSecret | Test.AgentChat.Api | Gradio container startup |
|
||||
|
||||
## Fixture Count
|
||||
@@ -52,12 +59,13 @@
|
||||
| Model fixtures | 5 |
|
||||
| **Total** | **19** |
|
||||
|
||||
## Current Verification Snapshot (2026-06-30)
|
||||
## Current Verification Snapshot (2026-07-03)
|
||||
|
||||
| Slice | Command | Result |
|
||||
|-------|---------|--------|
|
||||
| Backend agent | `cd backend && source .venv/bin/activate && python -m pytest tests/test_agent/ -v` | 375 passed, 18 warnings |
|
||||
| Frontend model | `cd frontend && npm run test -- src/lib/models/__tests__/AgentChatModel.test.ts --run` | 73 passed |
|
||||
| Backend hidden context | `AUTH_SECRET_KEY=test-secret JWT_SECRET=test-secret backend/.venv/bin/python -m pytest backend/tests/test_agent/test_app.py::TestAgentHandler::test_normal_send_hides_runtime_context_from_saved_history -q` | 1 passed |
|
||||
| Frontend model | `cd frontend && npm run test -- AgentChatModel.test.ts` | 88 passed |
|
||||
| Frontend build | `cd frontend && npm run build` | Passed; existing unrelated Svelte warnings |
|
||||
| Browser UX | Chrome DevTools snapshot/screenshot of `http://localhost:5173/agent` | Process strip visible; diagnostics menu opens; system titles normalized |
|
||||
|
||||
#endregion Traceability
|
||||
|
||||
@@ -1,31 +1,35 @@
|
||||
#region AgentChat.UxReference [C:3] [TYPE ADR] [SEMANTICS ux,reference,agent-chat,final]
|
||||
@BRIEF Final UX reference — Gradio `submit()`, LangChain v1 HITL, structured metadata, Archive UX.
|
||||
@BRIEF Final UX reference — Gradio `submit()`, LangChain v1 HITL, structured metadata, Archive UX, operator recovery.
|
||||
|
||||
## 1. Persona
|
||||
|
||||
Platform operator in superset-tools Svelte interface. Opens chat (drawer or `/agent` page). Streams responses, confirms dangerous ops via inline card, uploads files for analysis.
|
||||
Platform operator in superset-tools Svelte interface. Opens chat (drawer or `/agent` page). Streams responses, confirms dangerous ops via inline card, uploads files for analysis, and diagnoses LLM/Gradio stalls without leaving the page.
|
||||
|
||||
## 2. Happy Path
|
||||
|
||||
`submit("/chat", {message}, conversation_id)`. Tokens stream in real time. Tool cards appear from structured metadata. Confirmation card for dangerous ops — Confirm via second `submit()`. Archive conversations via delete (soft-delete).
|
||||
`submit("/chat", {message}, conversation_id, user_id, user_jwt, env_id)`. The UI synchronizes selected environment before each send. Tokens stream in real time. Tool cards appear from structured metadata. Confirmation card for dangerous ops — Confirm via second `submit()`. Archive conversations via delete (soft-delete).
|
||||
|
||||
## 3. Key Elements
|
||||
|
||||
- **Input**: Textarea + paperclip (PDF/XLSX/JSON/CSV/PNG/JPEG)
|
||||
- **Context indicator**: Header shows selected environment and process strip (`Контекст`, `Понимание задачи`, `Инструменты`, `Подтверждение`, `Результат`).
|
||||
- **Streaming**: Text character-by-character. Tool cards from `metadata.type`.
|
||||
- **Stop**: `submission.cancel()` — native.
|
||||
- **Confirmation**: Warning card from `metadata.type="confirm_required"`. Confirm → second `submit()`.
|
||||
- **Debug reference**: `/agent` header toggle shows `conv_id`, pending `thread_id`, connection/streaming state, env/user, message count, last message id, active tool calls, and current error. Copy exports JSON.
|
||||
- **Diagnostics**: `/agent` header menu exposes debug panel, copy JSON diagnostics, and LLM status check. Debug panel shows `conv_id`, pending `thread_id`, connection/streaming state, env/user, message count, last message id, active tool calls, and current error.
|
||||
- **Visible text hygiene**: User bubbles and conversation titles do not show hidden runtime/prefetch/upload context. System-only tool titles normalize to `Системное действие`.
|
||||
- **Conversation Switcher**: Search, date-grouped, infinite scroll.
|
||||
- **Connection**: Green/red dot.
|
||||
|
||||
## 4. Error Scenarios
|
||||
|
||||
- **Gradio down**: Dot red, retry 5×5s.
|
||||
- **LLM error**: Stream `metadata.type="error"` → card + retry.
|
||||
- **LLM error**: Stream `metadata.type="error"` → recovery card.
|
||||
- **Silent LLM/Gradio hang**: No first token/tool/confirmation/checkpoint for 60s → recovery card with retry/check LLM/copy debug/new dialog.
|
||||
- **Tool failure**: `metadata.type="tool_error"` → cross + detail.
|
||||
- **File parse**: Error chip: "Не удалось прочитать".
|
||||
- **Multi-tab**: REST gate rejects second tab.
|
||||
- **Relative maintenance request**: Hidden runtime context gives the LLM current ISO datetime and duration rules. The hidden block must not render in chat/history.
|
||||
|
||||
## 5. Gradual Transition
|
||||
|
||||
|
||||
Reference in New Issue
Block a user