semantics

This commit is contained in:
2026-05-26 09:30:41 +03:00
parent 1e7bcecaea
commit 9ffa8af1dc
623 changed files with 28045 additions and 26557 deletions

View File

@@ -1,5 +1,5 @@
# region TestClientHeaders [TYPE Module]
# @RELATION: BELONGS_TO -> SrcRoot
# @RELATION BELONGS_TO -> SrcRoot
# @SEMANTICS: tests, llm-client, openrouter, headers
# @PURPOSE: Verify OpenRouter client initialization includes provider-specific headers.
@@ -8,10 +8,10 @@ from src.plugins.llm_analysis.service import LLMClient
# region test_openrouter_client_includes_referer_and_title_headers [TYPE Function]
# @RELATION: BINDS_TO -> TestClientHeaders
# @RELATION BINDS_TO -> TestClientHeaders
# @PURPOSE: OpenRouter requests should carry site/app attribution headers for compatibility.
# @PRE: Client is initialized for OPENROUTER provider.
# @POST: Async client headers include Authorization, HTTP-Referer, and X-Title.
# @PRE Client is initialized for OPENROUTER provider.
# @POST Async client headers include Authorization, HTTP-Referer, and X-Title.
def test_openrouter_client_includes_referer_and_title_headers(monkeypatch):
monkeypatch.setenv("OPENROUTER_SITE_URL", "http://localhost:8000")
monkeypatch.setenv("OPENROUTER_APP_NAME", "ss-tools-test")
@@ -31,10 +31,10 @@ def test_openrouter_client_includes_referer_and_title_headers(monkeypatch):
# region test_litellm_client_uses_default_bearer_auth [TYPE Function]
# @RELATION: BINDS_TO -> TestClientHeaders
# @RELATION BINDS_TO -> TestClientHeaders
# @PURPOSE: LiteLLM proxy uses standard OpenAI-compatible Bearer auth — no special headers needed.
# @PRE: Client is initialized for LITELLM provider.
# @POST: Async client headers include only Authorization — no extra provider-specific headers.
# @PRE Client is initialized for LITELLM provider.
# @POST Async client headers include only Authorization — no extra provider-specific headers.
def test_litellm_client_uses_default_bearer_auth():
"""Verify LiteLLM client initialization uses standard Bearer auth without extra headers."""
client = LLMClient(

View File

@@ -1,5 +1,5 @@
# region TestScreenshotService [TYPE Module]
# @RELATION: VERIFIES ->[src.plugins.llm_analysis.service.ScreenshotService]
# @RELATION BINDS_TO ->[ScreenshotService]
# @SEMANTICS: tests, screenshot-service, navigation, timeout-regression
# @PURPOSE: Protect dashboard screenshot navigation from brittle networkidle waits.
@@ -9,10 +9,10 @@ from src.plugins.llm_analysis.service import ScreenshotService
# region test_iter_login_roots_includes_child_frames [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Login discovery must search embedded auth frames, not only the main page.
# @PRE: Page exposes child frames list.
# @POST: Returned roots include page plus child frames in order.
# @PRE Page exposes child frames list.
# @POST Returned roots include page plus child frames in order.
def test_iter_login_roots_includes_child_frames():
frame_a = object()
frame_b = object()
@@ -28,10 +28,10 @@ def test_iter_login_roots_includes_child_frames():
# region test_response_looks_like_login_page_detects_login_markup [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Direct login fallback must reject responses that render the login screen again.
# @PRE: Response body contains stable login-page markers.
# @POST: Helper returns True so caller treats fallback as failed authentication.
# @PRE Response body contains stable login-page markers.
# @POST Helper returns True so caller treats fallback as failed authentication.
def test_response_looks_like_login_page_detects_login_markup():
service = ScreenshotService(env=type("Env", (), {})())
@@ -55,10 +55,10 @@ def test_response_looks_like_login_page_detects_login_markup():
# region test_find_first_visible_locator_skips_hidden_first_match [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Locator helper must not reject a selector collection just because its first element is hidden.
# @PRE: First matched element is hidden and second matched element is visible.
# @POST: Helper returns the second visible candidate.
# @PRE First matched element is hidden and second matched element is visible.
# @POST Helper returns the second visible candidate.
@pytest.mark.anyio
async def test_find_first_visible_locator_skips_hidden_first_match():
class _FakeElement:
@@ -96,10 +96,10 @@ async def test_find_first_visible_locator_skips_hidden_first_match():
# region test_submit_login_via_form_post_uses_browser_context_request [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Fallback login must submit hidden fields and credentials through the context request cookie jar.
# @PRE: Login DOM exposes csrf hidden field and request context returns authenticated HTML.
# @POST: Helper returns True and request payload contains csrf_token plus credentials plus request options.
# @PRE Login DOM exposes csrf hidden field and request context returns authenticated HTML.
# @POST Helper returns True and request payload contains csrf_token plus credentials plus request options.
@pytest.mark.anyio
async def test_submit_login_via_form_post_uses_browser_context_request():
class _FakeInput:
@@ -207,10 +207,10 @@ async def test_submit_login_via_form_post_uses_browser_context_request():
# region test_submit_login_via_form_post_accepts_authenticated_redirect [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Fallback login must treat non-login 302 redirect as success without waiting for redirect target.
# @PRE: Request response is 302 with Location outside login path.
# @POST: Helper returns True.
# @PRE Request response is 302 with Location outside login path.
# @POST Helper returns True.
@pytest.mark.anyio
async def test_submit_login_via_form_post_accepts_authenticated_redirect():
class _FakeInput:
@@ -282,10 +282,10 @@ async def test_submit_login_via_form_post_accepts_authenticated_redirect():
# region test_submit_login_via_form_post_rejects_login_markup_response [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Fallback login must fail when POST response still contains login form content.
# @PRE: Login DOM exposes csrf hidden field and request response renders login markup.
# @POST: Helper returns False.
# @PRE Login DOM exposes csrf hidden field and request response renders login markup.
# @POST Helper returns False.
@pytest.mark.anyio
async def test_submit_login_via_form_post_rejects_login_markup_response():
class _FakeInput:
@@ -365,10 +365,10 @@ async def test_submit_login_via_form_post_rejects_login_markup_response():
# region test_goto_resilient_falls_back_from_domcontentloaded_to_load [TYPE Function]
# @RELATION: BINDS_TO ->[TestScreenshotService]
# @RELATION BINDS_TO ->[EXT:frontend:TestScreenshotService]
# @PURPOSE: Pages with unstable primary wait must retry with fallback wait strategy.
# @PRE: First page.goto call raises; second succeeds.
# @POST: Helper returns second response and attempts both wait modes in order.
# @PRE First page.goto call raises; second succeeds.
# @POST Helper returns second response and attempts both wait modes in order.
@pytest.mark.anyio
async def test_goto_resilient_falls_back_from_domcontentloaded_to_load():
class _FakePage:

View File

@@ -1,5 +1,5 @@
# region TestService [TYPE Module]
# @RELATION: BELONGS_TO -> SrcRoot
# @RELATION BELONGS_TO -> SrcRoot
# @SEMANTICS: tests, llm-analysis, fallback, provider-error, unknown-status
# @PURPOSE: Verify LLM analysis transport/provider failures do not masquerade as dashboard FAIL results.
@@ -10,10 +10,10 @@ from src.plugins.llm_analysis.service import LLMClient
# region test_test_runtime_connection_uses_json_completion_transport [TYPE Function]
# @RELATION: BINDS_TO -> TestService
# @RELATION BINDS_TO -> TestService
# @PURPOSE: Provider self-test must exercise the same chat completion transport as runtime analysis.
# @PRE: get_json_completion is available on initialized client.
# @POST: Self-test forwards a lightweight user message into get_json_completion and returns its payload.
# @PRE get_json_completion is available on initialized client.
# @POST Self-test forwards a lightweight user message into get_json_completion and returns its payload.
@pytest.mark.anyio
async def test_test_runtime_connection_uses_json_completion_transport(monkeypatch):
client = LLMClient(
@@ -39,10 +39,10 @@ async def test_test_runtime_connection_uses_json_completion_transport(monkeypatc
# region test_analyze_dashboard_provider_error_maps_to_unknown [TYPE Function]
# @RELATION: BINDS_TO -> TestService
# @RELATION BINDS_TO -> TestService
# @PURPOSE: Infrastructure/provider failures must produce UNKNOWN analysis status rather than FAIL.
# @PRE: LLMClient.get_json_completion raises provider/auth exception.
# @POST: Returned payload uses status=UNKNOWN and issue severity UNKNOWN.
# @PRE LLMClient.get_json_completion raises provider/auth exception.
# @POST Returned payload uses status=UNKNOWN and issue severity UNKNOWN.
@pytest.mark.anyio
async def test_analyze_dashboard_provider_error_maps_to_unknown(monkeypatch, tmp_path):
screenshot_path = tmp_path / "shot.jpg"

View File

@@ -1,7 +1,7 @@
# #region LLMAnalysisModels [C:3] [TYPE Module] [SEMANTICS pydantic, llm, plugin, model, provider-type]
# @BRIEF Define Pydantic models for LLM Analysis plugin.
# @LAYER: Domain
# @RELATION DEPENDS_ON -> pydantic
# @LAYER Domain
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
from datetime import datetime
from enum import Enum

View File

@@ -1,13 +1,13 @@
# #region LLMAnalysisPlugin [C:5] [TYPE Module] [SEMANTICS llm, analysis, dashboard, validation, documentation]
# @BRIEF Implements DashboardValidationPlugin and DocumentationPlugin.
# @LAYER: Plugin
# @LAYER Plugin
# @RELATION INHERITS -> [PluginBase]
# @RELATION CALLS -> [ScreenshotService]
# @RELATION CALLS -> [LLMClient]
# @RELATION CALLS -> [LLMProviderService]
# @RELATION USES -> TaskContext
# @INVARIANT: All LLM interactions must be executed as asynchronous tasks.
# @DATA_CONTRACT: AnalysisRequest -> AnalysisResult
# @RELATION DEPENDS_ON -> [TaskContext]
# @INVARIANT All LLM interactions must be executed as asynchronous tasks.
# @DATA_CONTRACT AnalysisRequest -> AnalysisResult
from datetime import datetime, timedelta
import json
@@ -33,8 +33,8 @@ from .service import LLMClient, ScreenshotService
# #region _is_masked_or_invalid_api_key [TYPE Function]
# @BRIEF Guards against placeholder or malformed API keys in runtime.
# @PRE: value may be None.
# @POST: Returns True when value cannot be used for authenticated provider calls.
# @PRE value may be None.
# @POST Returns True when value cannot be used for authenticated provider calls.
def _is_masked_or_invalid_api_key(value: str | None) -> bool:
key = (value or "").strip()
if not key:
@@ -47,8 +47,8 @@ def _is_masked_or_invalid_api_key(value: str | None) -> bool:
# #region _json_safe_value [TYPE Function]
# @BRIEF Recursively normalize payload values for JSON serialization.
# @PRE: value may be nested dict/list with datetime values.
# @POST: datetime values are converted to ISO strings.
# @PRE value may be nested dict/list with datetime values.
# @POST datetime values are converted to ISO strings.
def _json_safe_value(value: Any):
if isinstance(value, datetime):
return value.isoformat()
@@ -61,7 +61,7 @@ def _json_safe_value(value: Any):
# #region DashboardValidationPlugin [TYPE Class]
# @BRIEF Plugin for automated dashboard health analysis using LLMs.
# @RELATION IMPLEMENTS -> backend.src.core.plugin_base.PluginBase
# @RELATION IMPLEMENTS -> [EXT:path:backend.src.core.plugin_base.PluginBase]
class DashboardValidationPlugin(PluginBase):
@property
def id(self) -> str:
@@ -92,11 +92,11 @@ class DashboardValidationPlugin(PluginBase):
# region DashboardValidationPlugin.execute [TYPE Function]
# @PURPOSE: Executes the dashboard validation task with TaskContext support.
# @PARAM: params (Dict[str, Any]) - Validation parameters.
# @PARAM: context (Optional[TaskContext]) - Task context for logging with source attribution.
# @PRE: params contains dashboard_id, environment_id, and provider_id.
# @POST: Returns a dictionary with validation results and persists them to the database.
# @SIDE_EFFECT: Captures a screenshot, calls LLM API, and writes to the database.
# @PARAM params (Dict[str, Any]) - Validation parameters.
# @PARAM context (Optional[TaskContext]) - Task context for logging with source attribution.
# @PRE params contains dashboard_id, environment_id, and provider_id.
# @POST Returns a dictionary with validation results and persists them to the database.
# @SIDE_EFFECT Captures a screenshot, calls LLM API, and writes to the database.
async def execute(self, params: dict[str, Any], context: TaskContext | None = None):
with belief_scope("execute", f"plugin_id={self.id}"):
validation_started_at = datetime.utcnow()
@@ -326,7 +326,7 @@ class DashboardValidationPlugin(PluginBase):
# #region DocumentationPlugin [TYPE Class]
# @BRIEF Plugin for automated dataset documentation using LLMs.
# @RELATION IMPLEMENTS -> backend.src.core.plugin_base.PluginBase
# @RELATION IMPLEMENTS -> [EXT:path:backend.src.core.plugin_base.PluginBase]
class DocumentationPlugin(PluginBase):
@property
def id(self) -> str:
@@ -357,11 +357,11 @@ class DocumentationPlugin(PluginBase):
# region DocumentationPlugin.execute [TYPE Function]
# @PURPOSE: Executes the dataset documentation task with TaskContext support.
# @PARAM: params (Dict[str, Any]) - Documentation parameters.
# @PARAM: context (Optional[TaskContext]) - Task context for logging with source attribution.
# @PRE: params contains dataset_id, environment_id, and provider_id.
# @POST: Returns generated documentation and updates the dataset in Superset.
# @SIDE_EFFECT: Calls LLM API and updates dataset metadata in Superset.
# @PARAM params (Dict[str, Any]) - Documentation parameters.
# @PARAM context (Optional[TaskContext]) - Task context for logging with source attribution.
# @PRE params contains dataset_id, environment_id, and provider_id.
# @POST Returns generated documentation and updates the dataset in Superset.
# @SIDE_EFFECT Calls LLM API and updates dataset metadata in Superset.
async def execute(self, params: dict[str, Any], context: TaskContext | None = None):
with belief_scope("execute", f"plugin_id={self.id}"):
# Use TaskContext logger if available, otherwise fall back to app logger

View File

@@ -1,6 +1,6 @@
# #region LLMAnalysisScheduler [C:3] [TYPE Module] [SEMANTICS scheduler, llm, validation, task, cron]
# @BRIEF Provides helper functions to schedule LLM-based validation tasks.
# @LAYER: Domain
# @LAYER Domain
# @RELATION DEPENDS_ON -> [SchedulerService]
from typing import Any
@@ -11,7 +11,7 @@ from ...dependencies import get_scheduler_service, get_task_manager
# #region schedule_dashboard_validation [TYPE Function]
# @BRIEF Schedules a recurring dashboard validation task.
# @SIDE_EFFECT: Adds a job to the scheduler service.
# @SIDE_EFFECT Adds a job to the scheduler service.
def schedule_dashboard_validation(dashboard_id: str, cron_expression: str, params: dict[str, Any]):
with belief_scope("schedule_dashboard_validation", f"dashboard_id={dashboard_id}"):
scheduler = get_scheduler_service()

View File

@@ -1,9 +1,9 @@
# #region LLMAnalysisService [C:5] [TYPE Module] [SEMANTICS llm, screenshot, playwright, openai, tenacity]
# @BRIEF Services for LLM interaction and dashboard screenshots.
# @LAYER Plugin
# @RELATION DEPENDS_ON -> tenacity
# @RELATION DEPENDS_ON -> tenacity
# @RELATION DEPENDS_ON -> tenacity
# @RELATION DEPENDS_ON -> [EXT:Library:tenacity]
# @RELATION DEPENDS_ON -> [EXT:Library:tenacity]
# @RELATION DEPENDS_ON -> [EXT:Library:tenacity]
# @INVARIANT Screenshots must be 1920px width and capture full page height.
# @DATA_CONTRACT DashboardSpec -> Screenshot + Analysis
# @RATIONALE Extracted all hardcoded timeouts into named module-level constants (PLAYWRIGHT_NAVIGATION_TIMEOUT_MS, PLAYWRIGHT_WAIT_TIMEOUT_MS, PLAYWRIGHT_SHORT_TIMEOUT_MS, HTTP_REQUEST_TIMEOUT_MS, SCREENSHOT_SERVICE_TIMEOUT_MS, LLM_HTTP_TIMEOUT_S) and DEFAULT_USER_AGENT. Zero remaining numeric timeout literals.