Files
ss-tools/specs/035-agent-chat-context/data-model.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.3 KiB
Raw Blame History

#region DataModel [C:3] [TYPE ADR] [SEMANTICS data-model,agent-chat,schema] @defgroup DataModel Data structures for 035-agent-chat-context — frontend TypeScript DTOs and backend Python counterparts. @RATIONALE Updated for Path B (/agent page), Gradio payload safety, two-layer RBAC, and permission_denied event type.

1. UIContext — Navigation Context Payload

Source

Derived from URL query parameters on /agent page entry. Static for visit duration. EnvId syncable via updateActiveEnv.

TypeScript (frontend/src/lib/models/AgentChatTypes.ts)

export interface UIContext {
  objectType: "dashboard" | "dataset" | "migration" | null;
  objectId: string | null;
  objectName: string | null;       // max 256 chars
  envId: string | null;           // synced with EnvSelector via updateActiveEnv
  route: string;                   // source page route (e.g. "/dashboards/42")
  contextVersion: number;          // currently 1
}

Python validation (backend/src/agent/_context.py — NEW)

# UIContext validation rules:
# objectType: must be one of ("dashboard","dataset","migration",None)
# objectId: must be numeric string or None
# objectName: max 256 chars
# envId: string or None
# route: string, max 512 chars
# contextVersion: positive integer ≤ 1
# Max payload size: 4 KB (413 Payload Too Large if exceeded)

2. ConfirmMetadataV2 — SSE confirm_required Event

{
  "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",
    "prompt": "⚠️ Изменение данных в production",
    "requires_confirmation": true
  }
}

Env resolution rule (canonical):

  1. tool_args.env_id (tool-targeted environment)
  2. Submit-time envId parameter (position 5 in Gradio args)
  3. UIContext.envId (from URL params)
  4. null (no environment)

Env normalization: env_id containing "prod"/"production" → "prod"; "stag"/"test" → "staging"; "dev"/"local" → "dev". Unknown values → null.


3. PermissionDeniedMetadata — SSE permission_denied Event (NEW)

{
  "metadata": {
    "type": "permission_denied",
    "tool_name": "deploy_dashboard",
    "required_role": "admin",
    "user_role": "analyst",
    "alternatives": [
      {"action": "get_health_summary", "prompt": "Запросить отчёт о состоянии дашборда"}
    ]
  }
}

Distinct from confirm_required: This event BYPASSES HITL checkpoint. Frontend renders permission-denied card with «Закрыть» button — no confirm. Emitted at invocation-time guard (AgentChat.ToolFilter.InvocationGuard).


4. New SSE Event Types

Event When Fields
permission_denied Tool invocation rejected by RBAC guard tool_name, required_role, user_role, alternatives
tool_retry Read-only tool auto-retrying tool, attempt, max_attempts
tool_timeout Tool exceeded 30s timeout tool, timeout_seconds, is_write_tool, retryable

tool_timeout (extended)

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

For write tools (is_write_tool=true): retryable=false, frontend shows "Operation status unknown — check Superset" (no retry button).


5. AgentChatModel — New Atoms (v2)

// ADD to AgentChatModel class:
uiContext: UIContext | null = $state(null);
dangerousCountdown: number = $state(0);
dangerousCountdownActive: boolean = $state(false);
_activeEnvId: string | null = $state(null);
permissionAlternatives: Array<{ action: string; prompt: string }> | null = $state(null);

// ADD derived:
isProdContext: boolean = $derived(...);
showProdWarning: boolean = $derived(...);
contextPillLabel: string = $derived(...);

// updateActiveEnv now syncs BOTH atoms:
updateActiveEnv(envId: string | null): void {
  this._activeEnvId = envId;
  if (this.uiContext) this.uiContext.envId = envId;
}

REMOVED: toolAffinity atom (debug data must come from backend SSE, not frontend reconstruction).


6. Existing DB Models — No Changes

No new tables, no schema migrations. Context not persisted in conversations.

#endregion DataModel