from __future__ import annotations import logging from typing import Any, Protocol from core.models import Scene from semantic.config import SemanticConfig, load_config from semantic.llm_client import AnthropicSemanticClient, EnrichmentUnavailable from semantic.models import SemanticScene logger = logging.getLogger(__name__) class SemanticLLMClient(Protocol): def enrich(self, scene_json: dict[str, Any], *, timeout: float) -> dict[str, Any]: ... def enrich_scene( scene: Scene, *, client: SemanticLLMClient | None = None, config: SemanticConfig | None = None, ) -> SemanticScene | None: settings = config or load_config() if not settings.enabled: return None llm_client = client or AnthropicSemanticClient(model=settings.model) try: payload = llm_client.enrich(scene.to_dict(), timeout=settings.timeout) semantic_scene = SemanticScene.from_dict(payload) return _with_known_widgets_only(scene, semantic_scene) except EnrichmentUnavailable as exc: logger.info("semantic enrichment unavailable: %s", exc) return None except Exception as exc: logger.info("semantic enrichment failed: %s", exc) return None def _with_known_widgets_only( scene: Scene, semantic_scene: SemanticScene, ) -> SemanticScene | None: known_element_ids = {element.id for element in scene.elements} if not known_element_ids: return semantic_scene widgets = [ widget for widget in semantic_scene.widgets if widget.element_id in known_element_ids ] if not widgets: return None return SemanticScene( page=semantic_scene.page, intents=semantic_scene.intents, widgets=widgets, )