tasks
This commit is contained in:
@@ -1,107 +1,286 @@
|
||||
# Module Contracts: LLM Analysis Plugin
|
||||
# Module Contracts: LLM Analysis Plugin v2
|
||||
|
||||
**Feature**: `017-llm-analysis-plugin`
|
||||
**Protocol**: `semantic_protocol.md`
|
||||
**Updated**: 2026-05-31 — v2: dual-path contracts, URL parsing, dataset health checking
|
||||
**Protocol**: GRACE-Poly v2.6
|
||||
|
||||
## Backend Modules
|
||||
|
||||
### Plugin Entry Point
|
||||
### Validation Task Manager (new)
|
||||
|
||||
```python
|
||||
# [DEF:backend/src/plugins/llm_analysis/plugin.py:Module]
|
||||
# @TIER: STANDARD
|
||||
# @SEMANTICS: plugin, task_handler, validation, documentation
|
||||
# @PURPOSE: Orchestrate LLM-based analysis tasks (Dashboard Validation, Documentation) within the Plugin architecture.
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> backend/src/plugins/llm_analysis/service.py
|
||||
# @RELATION: IMPLEMENTS -> backend/src/core/plugin_base.py
|
||||
# @INVARIANT: All tasks must report status updates via WebSocket.
|
||||
# #region ValidationTaskService [C:4] [TYPE Module] [SEMANTICS validation, task, schedule, policy, CRUD]
|
||||
# @BRIEF CRUD and lifecycle management for ValidationPolicy and ValidationSource.
|
||||
# @LAYER Service
|
||||
# @RELATION DEPENDS_ON -> [ValidationPolicy]
|
||||
# @RELATION DEPENDS_ON -> [ValidationSource]
|
||||
# @RELATION CALLS -> [SupersetContextExtractor]
|
||||
|
||||
# [DEF:validate_dashboard:Function]
|
||||
# @PURPOSE: Analyze dashboard health using screenshot and logs.
|
||||
# @PRE: Dashboard ID exists and provider is configured.
|
||||
# @POST: ValidationResult is persisted and notification sent if failed.
|
||||
# [/DEF:validate_dashboard:Function]
|
||||
# #region create_task [TYPE Function]
|
||||
# @PURPOSE Create a new validation task with sources (IDs + URLs), provider, prompt, schedule.
|
||||
# @PRE environment_id resolves to existing environment.
|
||||
# @POST ValidationPolicy + ValidationSource rows persisted. URL sources parsed via SupersetContextExtractor for preview/validation.
|
||||
# @SIDE_EFFECT Calls Superset API to resolve dashboard slugs and validate URLs.
|
||||
# @RATIONALE URL sources are parsed at creation time for validation and preview — user immediately sees dashboard title, filters, tabs. Re-parsing on every execution (per FR-057) guarantees up-to-date permalinks and filter state.
|
||||
# @REJECTED URL parsing only at creation time (without execution-time re-parse) rejected — permalinks expire, dashboards get renamed, filters change. Creation-time parse = preview; execution-time re-parse = authoritative resolution.
|
||||
# #endregion create_task
|
||||
|
||||
# [DEF:generate_documentation:Function]
|
||||
# @PURPOSE: Generate and apply metadata descriptions for datasets.
|
||||
# @PRE: Dataset is accessible and schema is valid.
|
||||
# @POST: Dataset metadata is updated in the database.
|
||||
# [/DEF:generate_documentation:Function]
|
||||
|
||||
# [/DEF:backend/src/plugins/llm_analysis/plugin.py:Module]
|
||||
# #region parse_dashboard_url [TYPE Function]
|
||||
# @PURPOSE Resolve a Superset dashboard URL to structured context using SupersetContextExtractor.
|
||||
# @PRE url is a valid absolute Superset dashboard URL.
|
||||
# @POST Returns {dashboard_id, title, native_filters, activeTabs, anchor, partial_recovery, warnings}.
|
||||
# @RELATION CALLS -> [SupersetContextExtractor.parse_superset_link]
|
||||
# @SIDE_EFFECT Calls Superset API for permalink resolution and slug→ID lookup.
|
||||
# #endregion parse_dashboard_url
|
||||
# #endregion ValidationTaskService
|
||||
```
|
||||
|
||||
### LLM Service
|
||||
### Dashboard Validation Plugin (refactored)
|
||||
|
||||
```python
|
||||
# [DEF:backend/src/plugins/llm_analysis/service.py:Module]
|
||||
# @TIER: CRITICAL
|
||||
# @SEMANTICS: llm, api, client, retry, prompt
|
||||
# @PURPOSE: Execute reliable interactions with external LLM providers (OpenAI, etc.) with retry logic and prompt management.
|
||||
# @LAYER: Infrastructure
|
||||
# @RELATION: CALLS -> external_api
|
||||
# @RELATION: DEPENDS_ON -> backend/src/services/llm_provider.py
|
||||
# @INVARIANT: API Keys must never be logged in plain text.
|
||||
# #region DashboardValidationPlugin [C:5] [TYPE Module] [SEMANTICS validation, plugin, dual-path, llm, screenshot, text-only]
|
||||
# @BRIEF Orchestrates dashboard validation via Path A (Playwright screenshot) or Path B (text-only API).
|
||||
# @LAYER Plugin
|
||||
# @RELATION IMPLEMENTS -> [PluginBase]
|
||||
# @RELATION CALLS -> [ScreenshotService] # Path A only
|
||||
# @RELATION CALLS -> [LLMClient] # Both paths
|
||||
# @RELATION CALLS -> [SupersetClient] # Both paths
|
||||
# @RELATION CALLS -> [DatasetHealthChecker] # Path B only
|
||||
# @RELATION DEPENDS_ON -> [TaskContext]
|
||||
# @INVARIANT Execution path (A/B) is determined by `screenshot_enabled` flag — never mixed within one run.
|
||||
# @INVARIANT Path B MUST check every unique dataset referenced by dashboard charts.
|
||||
# @RATIONALE Two paths with different prompts and different data sources for LLM: screenshot path for visual issues, text path for data/KXD issues. Both paths serve different use cases.
|
||||
# @REJECTED Single «screenshot + text» prompt rejected — mixes two different analysis modalities, increases request size, complicates LLM response parsing.
|
||||
|
||||
# [DEF:analyze_multimodal:Function]
|
||||
# @PURPOSE: Send image and text context to LLM for analysis.
|
||||
# @PRE: Provider is active and quota is available.
|
||||
# @POST: Returns structured analysis or raises RetryError.
|
||||
# @SIDE_EFFECT: Makes external HTTP request.
|
||||
# [/DEF:analyze_multimodal:Function]
|
||||
# #region execute [TYPE Function] [C:4]
|
||||
# @PURPOSE Execute one validation run for a dashboard from ValidationPolicy.
|
||||
# @PRE params contains dashboard_id, provider_id, screenshot_enabled, prompt_template, logs_enabled.
|
||||
# @POST ValidationRecord persisted with status, issues, timings.
|
||||
# @SIDE_EFFECT Launches browser (Path A), calls Superset API (both paths), calls LLM API.
|
||||
# #endregion execute
|
||||
|
||||
# [/DEF:backend/src/plugins/llm_analysis/service.py:Module]
|
||||
# #region _execute_path_a [TYPE Function] [C:3]
|
||||
# @PURPOSE Path A: Playwright screenshot → multi-chunk capture → multimodal LLM.
|
||||
# @PRE dashboard_id is valid, provider is multimodal, screenshot_enabled=true.
|
||||
# @POST Returns {status, summary, issues, screenshot_paths, tab_screenshots, token_usage}.
|
||||
# @SIDE_EFFECT Launches browser, logs in, switches tabs, captures CDP screenshots.
|
||||
# @RATIONALE Multi-chunk screenshots (one per tab) replace single full-page — LLM sees each tab at high resolution instead of one compressed full-dashboard image.
|
||||
# @REJECTED Single full-page screenshot rejected — long dashboards (5000+px) compress to unreadable size (D9 in spec.md).
|
||||
# #endregion _execute_path_a
|
||||
|
||||
# #region _execute_path_b [TYPE Function] [C:3]
|
||||
# @PURPOSE Path B: API calls → dashboard structure + chart params + dataset health + logs → text-only LLM.
|
||||
# @PRE screenshot_enabled=false.
|
||||
# @POST Returns {status, summary, issues, dataset_health, chart_data_results, token_usage}.
|
||||
# @SIDE_EFFECT Calls Superset API (dashboard, chart, dataset, chart/data, log endpoints).
|
||||
# @RATIONALE Dataset health checking is mandatory — the purpose of validation is to verify the dashboard opens correctly with KXD data. Without dataset checks, silent KXD errors (connection refused, timeout) are invisible.
|
||||
# @REJECTED Logs-only checking without dataset health rejected — logs show past errors but don't guarantee current KXD connectivity state.
|
||||
# #endregion _execute_path_b
|
||||
# #endregion DashboardValidationPlugin
|
||||
```
|
||||
|
||||
### Provider Management Service
|
||||
### Screenshot Service (Path A — refactored for multi-chunk)
|
||||
|
||||
```python
|
||||
# [DEF:backend/src/services/llm_provider.py:Module]
|
||||
# @TIER: CRITICAL
|
||||
# @SEMANTICS: security, config, encryption, provider
|
||||
# @PURPOSE: Manage lifecycle and secure storage of LLM Provider configurations.
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> backend/src/core/database.py
|
||||
# @INVARIANT: Stored API keys must be encrypted at rest.
|
||||
# #region ScreenshotService [C:4] [TYPE Module] [SEMANTICS screenshot, playwright, cdp, tab-switching, multi-chunk]
|
||||
# @BRIEF Captures per-tab dashboard screenshots via Playwright + CDP.
|
||||
# @LAYER Plugin
|
||||
# @INVARIANT Each tab produces a separate high-resolution screenshot (max 1920×1200 per chunk).
|
||||
# @INVARIANT Multi-chunk: one screenshot per tab, NOT one full-page.
|
||||
# @RATIONALE Многочанковый подход даёт LLM детальные скриншоты каждого таба вместо одного сжатого full-page.
|
||||
# @REJECTED Single full-page CDP screenshot отвергнут — D9: длинные дашборды теряют читаемость.
|
||||
|
||||
# [DEF:get_provider_client:Function]
|
||||
# @PURPOSE: Instantiate an authenticated LLM client for a specific provider.
|
||||
# @PRE: Provider ID exists and is active.
|
||||
# @POST: Returns configured client instance with decrypted key.
|
||||
# [/DEF:get_provider_client:Function]
|
||||
|
||||
# [/DEF:backend/src/services/llm_provider.py:Module]
|
||||
# #region capture_dashboard_chunks [TYPE Function] [C:3]
|
||||
# @PURPOSE Capture per-tab screenshots: login → navigate → switch tabs → per-tab CDP screenshots.
|
||||
# @PRE dashboard_id is valid, browser available.
|
||||
# @POST Returns list of {tab_name, screenshot_path}.
|
||||
# @SIDE_EFFECT Launches browser, logs in, switches tabs.
|
||||
# #endregion capture_dashboard_chunks
|
||||
# #endregion ScreenshotService
|
||||
```
|
||||
|
||||
### Data Models
|
||||
### Dataset Health Checker (Path B — new)
|
||||
|
||||
```python
|
||||
# [DEF:backend/src/plugins/llm_analysis/models.py:Module]
|
||||
# @TIER: TRIVIAL
|
||||
# @SEMANTICS: dto, pydantic, schema
|
||||
# @PURPOSE: Define Pydantic models for configuration, requests, and results.
|
||||
# @LAYER: Domain/Data
|
||||
# [/DEF:backend/src/plugins/llm_analysis/models.py:Module]
|
||||
# #region DatasetHealthChecker [C:3] [TYPE Module] [SEMANTICS dataset, health, kxd, api, validation]
|
||||
# @BRIEF Checks dataset accessibility and KXD connectivity via Superset API.
|
||||
# @LAYER Service
|
||||
# @RELATION CALLS -> [SupersetClient]
|
||||
# @INVARIANT Every unique dataset referenced by dashboard charts is checked.
|
||||
# @RATIONALE Без проверки датасетов невозможно определить, может ли дашборд корректно открыться с данными из КХД. Скриншот показывает визуал, но не показывает, что данные пришли (а не кэш).
|
||||
|
||||
# #region check_dataset_health [TYPE Function]
|
||||
# @PURPOSE Fetch dataset schema and verify accessibility.
|
||||
# @PRE dataset_id is a valid Superset dataset ID.
|
||||
# @POST Returns DatasetHealth: {accessible, database, backend, kind, error}.
|
||||
# @SIDE_EFFECT Calls GET /api/v1/dataset/{id}.
|
||||
# #endregion check_dataset_health
|
||||
|
||||
# #region check_dashboard_datasets [TYPE Function]
|
||||
# @PURPOSE For every unique dataset in dashboard charts, check health and optionally execute chart data.
|
||||
# @PRE dashboard_id is valid, chart list available.
|
||||
# @POST Returns {datasets: [DatasetHealth], chart_data: [ChartDataResult]}.
|
||||
# @SIDE_EFFECT Calls GET /api/v1/dataset/{id} × N, optionally POST /api/v1/chart/data × M.
|
||||
# #endregion check_dashboard_datasets
|
||||
# #endregion DatasetHealthChecker
|
||||
```
|
||||
|
||||
### LLM Client (updated for dual-path)
|
||||
|
||||
```python
|
||||
# #region LLMClient [C:4] [TYPE Module] [SEMANTICS llm, api, multimodal, text-only, json-mode]
|
||||
# @BRIEF LLM provider wrapper supporting both multimodal (Path A) and text-only (Path B) analysis.
|
||||
# @LAYER Infrastructure
|
||||
# @RELATION DEPENDS_ON -> [EXT:Library:openai]
|
||||
# @RELATION DEPENDS_ON -> [EXT:Library:tenacity]
|
||||
# @INVARIANT API keys never logged in plain text.
|
||||
# @RATIONALE Единый клиент для обоих путей — различия только в формате messages (multimodal vs text-only) и промпте.
|
||||
|
||||
# #region analyze_dashboard_multimodal [TYPE Function]
|
||||
# @PURPOSE Path A: send multi-chunk screenshots + logs to multimodal LLM.
|
||||
# @PRE screenshot_paths is non-empty list.
|
||||
# @POST Returns structured JSON {status, summary, issues}.
|
||||
# #endregion analyze_dashboard_multimodal
|
||||
|
||||
# #region analyze_dashboard_text [TYPE Function]
|
||||
# @PURPOSE Path B: send dashboard topology + dataset health + logs to text-only LLM.
|
||||
# @PRE dashboard_structure and dataset_health are populated.
|
||||
# @POST Returns structured JSON {status, summary, issues}.
|
||||
# @RATIONALE Текстовый промпт включает: описание топологии дашборда (табы, чарты, параметры), статус датасетов (доступность, бэкенд, ошибки), логи выполнения. LLM анализирует консистентность данных, а не визуал.
|
||||
# #endregion analyze_dashboard_text
|
||||
# #endregion LLMClient
|
||||
```
|
||||
|
||||
### Provider Bindings (updated)
|
||||
|
||||
```python
|
||||
# #region DEFAULT_LLM_PROVIDER_BINDINGS [C:1] [TYPE Constant]
|
||||
# @BRIEF Provider bindings retained: documentation, git_commit, assistant_planner.
|
||||
# @RATIONALE dashboard_validation binding удалён — провайдер выбирается per-task в форме создания задачи (D6, FR-047).
|
||||
# #endregion DEFAULT_LLM_PROVIDER_BINDINGS
|
||||
```
|
||||
|
||||
### Prompt Defaults (updated)
|
||||
|
||||
```python
|
||||
# #region DEFAULT_LLM_PROMPTS [C:2] [TYPE Constant]
|
||||
# @BRIEF Path-specific validation prompts: multimodal (Path A) and text (Path B).
|
||||
# Legacy dashboard_validation_prompt retained for v1 compat, MUST NOT be used as default for new tasks.
|
||||
# @RATIONALE FR-048: null prompt_template → auto-select path-specific default.
|
||||
# Single prompt field per task avoids the risk of Path A using text prompt or vice versa.
|
||||
# #endregion DEFAULT_LLM_PROMPTS
|
||||
```
|
||||
|
||||
## Frontend Components
|
||||
|
||||
### Configuration UI
|
||||
### Validation Task List Page (new)
|
||||
|
||||
```html
|
||||
<!-- [DEF:frontend/src/components/llm/ProviderConfig.svelte:Component] -->
|
||||
<!-- @TIER: STANDARD -->
|
||||
<!-- @PURPOSE: Form for adding/editing LLM providers with connection testing. -->
|
||||
<!-- @RELATION: CALLS -> /api/settings/llm/providers -->
|
||||
<!-- [/DEF:frontend/src/components/llm/ProviderConfig.svelte:Component] -->
|
||||
<!-- #region ValidationTasksPage [C:3] [TYPE Page] [SEMANTICS sveltekit, validation, task-list, schedule]
|
||||
<!-- @BRIEF Page: /validation-tasks — list of all validation tasks with status, schedule, last run.
|
||||
<!-- @LAYER Page -->
|
||||
<!-- @RELATION CALLS -> /api/validation-tasks -->
|
||||
<!-- @UX_STATE Loading -> Skeleton table rows -->
|
||||
<!-- @UX_STATE Loaded -> Task cards/rows with status badges, schedule, dashboard count -->
|
||||
<!-- @UX_STATE Empty -> "No validation tasks yet" + "Create First Task" CTA -->
|
||||
```
|
||||
|
||||
### Validation Report
|
||||
### Validation Task Create/Edit Form (new)
|
||||
|
||||
```html
|
||||
<!-- [DEF:frontend/src/components/llm/ValidationReport.svelte:Component] -->
|
||||
<!-- @TIER: STANDARD -->
|
||||
<!-- @PURPOSE: Display formatted analysis results including screenshot and issues list. -->
|
||||
<!-- @INVARIANT: Must handle missing screenshots gracefully. -->
|
||||
<!-- [/DEF:frontend/src/components/llm/ValidationReport.svelte:Component] -->
|
||||
<!-- #region ValidationTaskForm [C:4] [TYPE Component] [SEMANTICS svelte, form, validation, task-create, url-parser]
|
||||
<!-- @BRIEF Multi-step form: name → sources (IDs + URLs) → provider + prompt → schedule.
|
||||
<!-- @LAYER Component -->
|
||||
<!-- @RELATION CALLS -> /api/llm/providers (list multimodal providers) -->
|
||||
<!-- @RELATION CALLS -> /api/validation-tasks/parse-url (URL validation) -->
|
||||
<!-- @UX_STATE Idle -> Empty form -->
|
||||
<!-- @UX_STATE Parsing URL -> Spinner on URL input; result banner (dashboard title, filters count) -->
|
||||
<!-- @UX_STATE Submitting -> All fields disabled, progress bar -->
|
||||
<!-- @UX_STATE Error -> Field-level errors, URL parse failure banner -->
|
||||
<!-- @UX_FEEDBACK URL parse success: green banner with dashboard title, native_filters count, activeTabs -->
|
||||
<!-- @UX_FEEDBACK Provider selector filters multimodal-only when screenshot_enabled=true -->
|
||||
```
|
||||
|
||||
### Validation Run (new — groups per-dashboard results)
|
||||
|
||||
```python
|
||||
# #region ValidationRun [C:3] [TYPE Model] [SEMANTICS run, grouping, aggregate, execution]
|
||||
# @BRIEF Groups ValidationRecords from one task execution (manual or scheduled).
|
||||
# One run = N dashboards validated. Aggregate counts computed on finalize.
|
||||
# @INVARIANT Every ValidationRecord belongs to exactly one ValidationRun.
|
||||
# @RATIONALE Without run grouping, a 15-dashboard task produces 15 disconnected records — no way to see aggregate status or link them as one execution unit.
|
||||
# Run groups them for reporting: "3 PASS, 1 WARN, 0 FAIL across 4 dashboards".
|
||||
# #endregion ValidationRun
|
||||
```
|
||||
|
||||
### Validation Run Detail Page (updated for multi-dashboard)
|
||||
|
||||
```html
|
||||
<!-- #region ValidationRunDetailPage [C:3] [TYPE Page] [SEMANTICS sveltekit, run, report, multi-dashboard, aggregate]
|
||||
<!-- @BRIEF Page: /validation-tasks/{policyId}/runs/{runId} — multi-dashboard run report.
|
||||
<!-- Run header with aggregate counts → collapsible dashboard list → per-dashboard detail (issues, screenshots/dataset-health, logs).
|
||||
<!-- @LAYER Page -->
|
||||
<!-- @RELATION CALLS -> GET /api/validation-tasks/{policy_id}/runs/{run_id} -->
|
||||
<!-- @UX_STATE Loading -> Skeleton placeholders -->
|
||||
<!-- @UX_STATE Loaded -> Run header + dashboard list with expand/collapse -->
|
||||
<!-- @UX_STATE Expanded -> Per-dashboard: Path A screenshots (tab selector) or Path B dataset health table -->
|
||||
<!-- @UX_STATE Mixed -> Single run may contain both Path A and Path B dashboards — sections render per dashboard -->
|
||||
<!-- @RATIONALE 15-dashboard run: collapse keeps page scannable. Expand for drill-down. Aggregate counts at top give instant health overview.
|
||||
```
|
||||
|
||||
### Legacy Report Redirect
|
||||
|
||||
```python
|
||||
# #region LegacyReportRedirect [C:1] [TYPE Route]
|
||||
# @BRIEF GET /reports/llm/{taskId} → 302 redirect to canonical /validation-tasks/{policy_id}/runs/{record_id}.
|
||||
# @RATIONALE Backward compat for v1 notification links, external bookmarks, and assistant-generated URLs.
|
||||
# Canonical URL is per FR-055b. Returns 404 if taskId has no matching ValidationRecord.
|
||||
# #endregion LegacyReportRedirect
|
||||
```
|
||||
|
||||
### Dashboard Validation Indicator (new)
|
||||
|
||||
```html
|
||||
<!-- #region DashboardValidationIndicator [C:2] [TYPE Component] [SEMANTICS svelte, dashboard, validation, indicator, batch]
|
||||
<!-- @BRIEF Colored dot indicator (PASS/WARN/FAIL/UNKNOWN) next to dashboard title in hub rows.
|
||||
<!-- Click → dropdown: last 3 runs (date, status, task name) + "View all" link.
|
||||
<!-- @LAYER Component -->
|
||||
<!-- @RELATION CALLS -> GET /api/dashboards/{env_id}/validation-status?dashboard_ids=... -->
|
||||
<!-- @RATIONALE Replaces removed LLM column without adding visual noise.
|
||||
<!-- Batch endpoint avoids N+1 requests for dashboard hub with 50+ rows.
|
||||
```
|
||||
|
||||
### ValidationTaskReport (new component)
|
||||
|
||||
```html
|
||||
<!-- #region ValidationTaskReport [C:3] [TYPE Component] [SEMANTICS svelte, report, validation, issues, dataset-health]
|
||||
<!-- @BRIEF Shared report component: check result badge, summary, issues table, execution path indicator, dataset health (Path B), screenshots (Path A).
|
||||
<!-- @LAYER Component -->
|
||||
```
|
||||
|
||||
### Dashboard Hub (simplified)
|
||||
|
||||
```html
|
||||
<!-- #region DashboardHub [C:4] [TYPE Page] [SEMANTICS sveltekit, dashboard, hub]
|
||||
<!-- @BRIEF LLM validation column REMOVED. "Validate" action REMOVED from action dropdown.
|
||||
<!-- Added "Create Validation Task" button in toolbar that pre-fills selected dashboard IDs.
|
||||
<!-- @RATIONALE D1, FR-051, FR-052: validation moves to task-based flow; ad-hoc "Validate" is deprecated.
|
||||
```
|
||||
|
||||
### LLM Settings (simplified)
|
||||
|
||||
```html
|
||||
<!-- #region LLMSettingsPage [C:3] [TYPE Page] [SEMANTICS sveltekit, settings, llm, provider]
|
||||
<!-- @BRIEF Provider bindings for dashboard_validation REMOVED.
|
||||
<!-- Retained: documentation, git_commit, assistant_planner provider bindings.
|
||||
<!-- @RATIONALE D8, FR-049: dashboard validation provider is per-task, not global.
|
||||
```
|
||||
|
||||
## ADR Continuity
|
||||
|
||||
| ADR | Impact |
|
||||
|-----|--------|
|
||||
| ADR-0001 (module layout) | v2 changes stay within `backend/src/plugins/llm_analysis/` and `frontend/src/routes/validation-tasks/` |
|
||||
| ADR-0004 (plugin architecture) | `DashboardValidationPlugin` remains a PluginBase implementation; new `DatasetHealthChecker` is a service, not a plugin |
|
||||
| ADR-0009 (SSL) | Unchanged — LLM client SSL configuration applies to both Path A and Path B |
|
||||
| ADR-0008 (assistant tool registry) | `_tool_llm_validation.py` — assistant tool should be updated to create task instead of triggering ad-hoc validation |
|
||||
|
||||
Reference in New Issue
Block a user