Files
ss-tools/specs/017-llm-analysis-plugin/research.md
2026-06-08 14:14:38 +03:00

143 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research: LLM Analysis & Documentation Plugins v2
**Feature**: `017-llm-analysis-plugin`
**Updated**: 2026-06-07 — v2 implemented: dual-path execution, multi-chunk screenshots, dataset health checking, URL-based dashboards
## v2 Research Decisions
### 8. Task-Based Flow (replaces ad-hoc Validate)
**Decision**: Remove one-click «Validate» button; replace with «Create Validation Task» flow (name → sources → provider+prompt → schedule).
**Rationale**: Ad-hoc validation doesn't scale — users need repeatable, scheduled checks with full configuration control. The task list provides visibility into what's running and historical trends.
**Alternatives Considered**:
- Keep Validate button + add scheduling separately — rejected: two modes cause confusion; task-based is cleaner.
- Inline validation from dashboard detail — rejected: no scheduling, no multi-dashboard runs.
### 9. Dual Execution Paths (Screenshot vs Text-Only)
**Decision**: `screenshot_enabled` flag controls execution path:
- **Path A** (screenshot_enabled=true): Playwright → multi-chunk CDP screenshots → multimodal LLM
- **Path B** (screenshot_enabled=false): API calls → dashboard topology + chart params + dataset health + logs → text-only LLM
**Rationale**:
- Screenshot catches visual bugs (empty charts, overlap, rendering) but costs image tokens and requires a browser.
- Text-only catches data/KXD errors (dataset inaccessible, chart query timeout) and is lightweight.
- Together they cover both visual and data-layer failure modes. Users choose per task based on what matters.
**Alternatives Considered**:
- Always screenshot — rejected: expensive, slow, doesn't check dataset backend health.
- Always text-only — rejected: misses visual rendering bugs (broken charts with no error in logs).
- Hybrid: screenshot + dataset check in one call — rejected: mixes modalities, increases LLM context size, complicates prompt.
### 10. Multi-Chunk Screenshots (Path A)
**Decision**: Instead of one compressed full-page screenshot, capture each dashboard tab as a separate high-resolution screenshot (max 1920×1200 per chunk). Send all chunks in a single multimodal LLM request.
**Rationale**: Long dashboards (5000+ px) compressed to 1024×2048 lose readability — small chart labels become unreadable. Per-tab chunks preserve resolution, and LLM sees the dashboard in the same logical sections a human would.
**Alternatives Considered**:
- Single full-page at 4096px width — rejected: token cost too high for image processing.
- Overlapping chunks with stitching — rejected: complex, fragile to viewport changes.
- Per-chart screenshots — rejected: too many images (30+ charts → excessive tokens), loses layout context.
### 11. Dataset Health Checking (Path B)
**Decision**: For Path B, call `GET /api/v1/dataset/{id}` for every unique dataset used by dashboard charts. Verify: HTTP 200, database backend, kind (virtual/physical). Optionally execute `POST /api/v1/chart/data` (off by default).
**Rationale**: The primary purpose of LLM validation is verifying the KXD→dataset→chart→dashboard chain works. Screenshot-only misses silent data failures (empty results, partial data, wrong time range). Dataset health check catches KXD connection errors that produce no visual error but return empty/incomplete data.
**Alternatives Considered**:
- Logs-only (no dataset API calls) — rejected: logs show past failures, not current KXD connectivity. A dataset can be broken right now with no recent log entry.
- Always execute chart data — rejected: overloads Superset (15 charts × 30 dashboards = 450 queries). Optional toggle lets users enable selectively.
### 12. URL-Based Dashboard Selection
**Decision**: Support pasting full Superset dashboard URLs as task sources. Parse via `SupersetContextExtractor` (same code path as dataset review feature). Extract: dashboard_id, native_filters, activeTabs, anchor.
**Rationale**: Users copy dashboard URLs from browser with filters already applied. Parsing the URL preserves the exact filter/tab state — the validation checks the dashboard in the same state the user sees. Reusing `SupersetContextExtractor` avoids duplicating URL parsing logic.
**URL Formats Supported** (same as dataset review):
- `https://superset.example.com/superset/dashboard/42/` — numeric ID
- `https://superset.example.com/superset/dashboard/my-dashboard/` — slug
- `https://superset.example.com/superset/dashboard/p/abc123/` — permalink
- `https://superset.example.com/superset/dashboard/42/?native_filters_key=xyz` — filter state key
**Alternatives Considered**:
- Dashboard ID only — rejected: loses filter context, user must manually re-apply filters.
- Custom URL format — rejected: users already have Superset URLs in browser; pasting them is natural.
### 13. Provider & Prompt Per-Task
**Decision**: Provider selection and prompt template configuration move from global LLM settings to per-task configuration in the task creation form. Global LLM settings retain provider bindings only for: documentation, git_commit, assistant_planner.
**Rationale**: Different dashboards may need different models (e.g., GPT-4o for complex visual analysis, a cheaper model for text-only). Different teams may want different prompt emphasis (performance vs. data quality). Global settings are too coarse.
**Alternatives Considered**:
- Global binding + per-task override — rejected: adds complexity; per-task is simpler and more flexible.
- Environment-level provider binding — rejected: too coarse; same environment may have different validation needs per dashboard group.
## v1 Research Decisions (preserved)
### 1. LLM Provider Integration
**Decision**: Use a unified `LLMProviderService` that abstracts OpenAI-compatible APIs.
**Rationale**: OpenRouter, Kilo, LiteLLM, and OpenAI all support the standard OpenAI API format.
### 2. Dashboard Screenshot Capture
**Decision**: `ScreenshotService` with Playwright + CDP for Path A.
**Rationale**: Provides accurate «user-view» render; CDP avoids font loading timeouts in headless mode.
### 3. Multimodal Analysis Prompting
**Decision**: Structured prompt template accepting base64-encoded images + text logs.
**Rationale**: GPT-4o / Claude / Gemini models support this natively.
### 4. Documentation Persistence
**Decision**: Update Dataset/Column models in metadata database.
**Rationale**: Documentation co-located with assets.
### 5. Git Commit Integration
**Decision**: REST endpoint `/api/git/generate-message`.
**Rationale**: Heavy lifting (LLM, diff processing) on backend.
### 6. Security & Storage
**Decision**: AES-256 encrypted API keys.
**Rationale**: Keys must not be stored in plain text.
### 7. Retry Logic
**Decision**: `tenacity` with exponential backoff (5 attempts, 5-60s wait).
**Rationale**: Standard, robust retry for transient LLM API failures.
### 14. Image Pipeline: PNG → JPEG (LLM) + WebP (Archive)
**Decision**: CDP captures PNG (browser-native, lossless). Convert to JPEG quality=60 for LLM (universal provider compatibility). Convert to WebP quality=80 for archive (93% disk savings vs PNG). Delete PNG and JPEG intermediates after successful save.
**Rationale**: JPEG universally supported by all providers. WebP has silent failure risk on unknown/closed providers (model accepts request but doesn't process image → false PASS). Pipeline decoupled: LLM failure doesn't affect archive; archive failure falls back to PNG with WARN.
**Alternatives Considered**:
- All WebP (LLM + archive) — rejected: silent failure risk.
- All JPEG (LLM + archive) — rejected: no archive space savings, re-encoding artifacts.
- WebP for LLM with `_supports_webp()` per-provider check — rejected: adds complexity; JPEG is simpler and universally safe.
**Impact**: T017a, T017b, T017c in task plan. FR-059 in spec. D10 design decision. ~93% disk savings vs PNG archive.
### 15. Path B Batching (experimental v2.1)
**Decision**: Default `llm_batch_size=1` (full isolation, one LLM call per dashboard). Values >1 are experimental and reduce cost at risk of cross-dashboard context contamination.
**Rationale**: Multiple dashboards in one LLM call saves API calls (5→1) but risks the LLM confusing which issue belongs to which dashboard. Structured prompt with per-dashboard labeled sections + strict JSON `{dashboards: [{dashboard_id, ...}]}` schema mitigates but does not eliminate this risk. For validation (where accuracy > cost), isolation is the safer default.
**Alternatives Considered**:
- Always batch (default 5) — rejected: accuracy risk for validation feature. Can't guarantee LLMs won't cross-contaminate context.
- Never batch (always 1) — rejected: legitimate use case for large fleet monitoring where cost matters more than per-dashboard precision.
- Per-policy toggle only — accepted as the right balance: default safe, user opts into batching when they understand the tradeoff.
**Impact**: FR-045a in spec. `llm_batch_size` field in ValidationPolicy, default 1. Batch_size >1 is documented as experimental with known accuracy risk.