169 lines
9.6 KiB
Markdown
169 lines
9.6 KiB
Markdown
# Data Model: LLM Analysis Plugin v2
|
|
|
|
**Feature**: `017-llm-analysis-plugin`
|
|
**Updated**: 2026-06-07 — v2 implemented: ValidationPolicy expansion, ValidationSource, dual-path execution models
|
|
|
|
## Entities
|
|
|
|
### LLMProviderConfig
|
|
Stores configuration for different LLM providers.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| id | UUID | Yes | Unique identifier |
|
|
| provider_type | Enum | Yes | `openai`, `openrouter`, `kilo`, `litellm` |
|
|
| name | String | Yes | Display name (e.g., "Production GPT-4") |
|
|
| base_url | String | Yes | API Endpoint URL |
|
|
| api_key | String | Yes | Encrypted API Key (AES-256) |
|
|
| default_model | String | Yes | Model identifier (e.g., `gpt-4o`) |
|
|
| is_active | Boolean | Yes | Whether this provider is currently enabled |
|
|
| is_multimodal | Boolean | Yes | Whether model supports image input (required for Path A) |
|
|
|
|
### ValidationPolicy (v2 — expanded)
|
|
|
|
Stores configuration for a scheduled validation task. Uses ORM relationship to `ValidationSource` table — no JSON column denormalization.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| id | UUID | Yes | Unique identifier |
|
|
| name | String | Yes | Human-readable task name |
|
|
| description | String | No | Optional description |
|
|
| environment_id | String | Yes | Target Superset environment |
|
|
| sources | relationship → list[ValidationSource] | Yes | ORM relationship only. Dashboards to validate (1..N). Migrated from v1 `dashboard_ids` list via `ValidationSource` rows |
|
|
| source_snapshot | JSON | No | Read-only denormalized copy of sources at last edit time — for audit/diff only, never used as authoritative state |
|
|
| provider_id | String | Yes | LLM provider to use (per-task, not global) |
|
|
| prompt_template | String | No | Custom prompt; `null` = use path-specific default: `dashboard_validation_prompt_multimodal` for Path A, `dashboard_validation_prompt_text` for Path B. Must NOT fall back to v1 legacy `dashboard_validation_prompt` |
|
|
| screenshot_enabled | Boolean | Yes | `true` = Path A (Playwright + multimodal LLM), `false` = Path B (text-only) |
|
|
| logs_enabled | Boolean | Yes | Fetch Superset execution logs for LLM context |
|
|
| execute_chart_data | Boolean | Yes | Path B only: execute `POST /api/v1/chart/data` for each chart (default: false) |
|
|
| llm_batch_size | Integer | No | Path B only: dashboards per LLM call (default: 1 — full isolation, one call per dashboard). Values >1 are experimental. Path A always uses 1 (image token constraints). |
|
|
| policy_dashboard_concurrency_limit | Integer | No | Max dashboards validated in parallel within one policy run (default: 3). Global worker limit controlled by `global_validation_worker_limit` config |
|
|
| cron_expression | String | No | Cron schedule; null = manual trigger only |
|
|
| is_active | Boolean | Yes | Whether schedule is currently active |
|
|
| notification_channels | list[String] | No | Email/Pulse channel IDs for failure notifications |
|
|
| created_at | DateTime | Yes | Creation timestamp |
|
|
| updated_at | DateTime | Yes | Last modification timestamp |
|
|
|
|
### ValidationSource (new)
|
|
|
|
Represents a single dashboard to validate within a task — either by numeric ID or by full Superset URL.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| id | UUID | Yes | Unique identifier |
|
|
| policy_id | UUID | Yes | FK → ValidationPolicy |
|
|
| type | Enum | Yes | `dashboard_id` or `dashboard_url` |
|
|
| value | String | Yes | Numeric dashboard ID or full Superset URL |
|
|
| parsed_context | JSON | No | For `dashboard_url`: result of `SupersetContextExtractor.parse_superset_link()` — contains dashboard_id, native_filters, activeTabs, anchor |
|
|
| status | Enum | Yes | `valid`, `partial_recovery` (SupersetContextExtractor returned partial match), `stale` (permalink expired, URL parse warning), `invalid` (dashboard deleted or not found), `permission_denied`, `parse_failed` |
|
|
| resolved_dashboard_id | String | No | Resolved numeric dashboard ID (cached after URL parse for display) |
|
|
| title | String | No | Resolved dashboard title (cached for display) |
|
|
| last_error | String | No | Last resolution error message |
|
|
| last_checked_at | DateTime | No | When the source was last validated/resolved |
|
|
|
|
### Migration from v1 dashboard_ids:
|
|
|
|
```python
|
|
# Existing dashboard_ids: list[str] → ValidationSource rows (relational only, no JSON denormalization)
|
|
for dashboard_id in old_policy.dashboard_ids:
|
|
db.add(ValidationSource(
|
|
policy_id=policy.id,
|
|
type="dashboard_id",
|
|
value=dashboard_id,
|
|
is_valid=True,
|
|
))
|
|
# Optional: populate source_snapshot JSON for audit trail
|
|
policy.source_snapshot = {"dashboard_ids": list(old_policy.dashboard_ids)}
|
|
```
|
|
|
|
### ValidationRun (new — groups records from one execution)
|
|
|
|
A single task execution validates ALL dashboards in a policy. `ValidationRun` groups these per-dashboard `ValidationRecord`s into one logical unit.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| id | UUID | Yes | Unique run identifier |
|
|
| policy_id | UUID | Yes | FK → ValidationPolicy |
|
|
| task_id | String | No | Associated Task Manager task ID (one run = one task) |
|
|
| started_at | DateTime | Yes | When the run started |
|
|
| finished_at | DateTime | No | When all dashboards finished (null while running) |
|
|
| trigger | Enum | Yes | `manual` or `scheduled` |
|
|
| status | Enum | Yes | `completed` (all dashboards done), `partial` (some sources skipped/invalid), `failed` (execution error before any dashboard) |
|
|
| dashboard_count | Integer | Yes | Total dashboards attempted |
|
|
| pass_count | Integer | Yes | PASS results |
|
|
| warn_count | Integer | Yes | WARN results |
|
|
| fail_count | Integer | Yes | FAIL results |
|
|
| unknown_count | Integer | Yes | UNKNOWN results (LLM errors, timeouts) |
|
|
|
|
### ValidationRecord (v2 — expanded, linked to run)
|
|
|
|
Stores the outcome of one dashboard validation within a task run. Linked to `ValidationRun` via `run_id`.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| id | UUID | Yes | Unique identifier |
|
|
| run_id | UUID | Yes | FK → ValidationRun — groups per-dashboard results from one task execution |
|
|
| task_id | String | No | Deprecated in favor of run_id; kept for v1 backward compat |
|
|
| policy_id | UUID | No | FK → ValidationPolicy |
|
|
| source_id | UUID | No | FK → ValidationSource |
|
|
| dashboard_id | String | Yes | Resolved dashboard ID |
|
|
| status | Enum | Yes | `PASS`, `WARN`, `FAIL`, `UNKNOWN` |
|
|
| execution_path | Enum | Yes | `screenshot` (Path A) or `text_only` (Path B) |
|
|
| summary | String | Yes | LLM-generated summary |
|
|
| issues | JSON | Yes | `[{severity, message, location}]` |
|
|
| dataset_health | JSON | No | Path B only: per-dataset status `[{dataset_id, name, database, backend, healthy, error, affected_charts}]` |
|
|
| chart_data_results | JSON | No | Path B with `execute_chart_data=true`: `[{chart_id, executed, duration_ms, row_count, error}]` |
|
|
| screenshot_paths | list[String] | No | Path A: paths to archived WebP screenshots (one per tab). PNG/JPEG intermediates deleted after conversion |
|
|
| tab_screenshots | JSON | No | Path A: per-tab mapping `[{tab_name, webp_path, errored}]` |
|
|
| logs_sent_to_llm | list[String] | No | Redacted Superset execution logs sent to LLM |
|
|
| token_usage | JSON | No | LLM token usage `{prompt_tokens, completion_tokens, total_tokens}` (when provider reports it) |
|
|
| raw_response | Text | No | Full LLM response for debugging. Retention: 30 days per FR-029a |
|
|
| timings | JSON | No | Phase timings: `{dashboard_fetch_ms, dataset_fetch_ms, chart_fetch_ms, chart_data_exec_ms, screenshot_ms, llm_call_ms, total_ms}` |
|
|
| created_at | DateTime | Yes | Record creation timestamp |
|
|
|
|
### Path B — Dataset Health Check (runtime model, not persisted separately)
|
|
|
|
Built during Path B execution from API responses:
|
|
|
|
```python
|
|
class DatasetHealth:
|
|
dataset_id: int
|
|
dataset_name: str # e.g. "finance.kpi_revenue"
|
|
database_name: str # e.g. "PostgreSQL / analytics_prod"
|
|
backend: str # e.g. "postgresql", "trino"
|
|
kind: str # "virtual" or "physical"
|
|
is_accessible: bool # HTTP 200 from GET /api/v1/dataset/{id}
|
|
error: str | None # Connection / timeout error
|
|
affected_chart_ids: list[int] # Charts using this dataset
|
|
```
|
|
|
|
### Chart Execution Result (runtime model, Path B + execute_chart_data)
|
|
|
|
```python
|
|
class ChartDataResult:
|
|
chart_id: int
|
|
chart_name: str
|
|
viz_type: str
|
|
executed: bool # False if skipped
|
|
duration_ms: int | None
|
|
row_count: int | None
|
|
error: str | None # Timeout / query error
|
|
```
|
|
|
|
## API Contracts
|
|
|
|
See `contracts/` directory.
|
|
|
|
### Key Interactions — v2
|
|
|
|
1. **Configure Provider**: `POST /api/llm/providers` (unchanged)
|
|
2. **Create Validation Task**: `POST /api/validation-tasks` — creates `ValidationPolicy` + `ValidationSource`s
|
|
3. **List Tasks**: `GET /api/validation-tasks` — returns all policies with aggregated status
|
|
4. **Get Task Detail**: `GET /api/validation-tasks/{policy_id}` — returns policy + per-source status
|
|
5. **Trigger Run**: `POST /api/validation-tasks/{policy_id}/run` — manual trigger (also fires on cron)
|
|
6. **Get Run History**: `GET /api/validation-tasks/{policy_id}/runs` — paginated `ValidationRecord` list
|
|
7. **Get Run Detail**: `GET /api/validation-tasks/{policy_id}/runs/{record_id}` — full report
|
|
8. **Parse URL**: `POST /api/validation-tasks/parse-url` — returns `{dashboard_id, title, filters, tabs}` from URL
|
|
9. **Generate Documentation**: `POST /api/tasks` with `plugin_id=llm_documentation` (unchanged)
|
|
10. **Generate Commit Message**: `POST /api/git/generate-message` (unchanged)
|