- _with_known_widgets_only used to discard the entire SemanticScene (returning None) when filtering dangling-element-id widgets left an empty list, losing valid page/intents. Now returns a SemanticScene with widgets=[] instead, matching design.md's 'dropped, not a hard failure' intent. - Removed _SDK_UNAVAILABLE_EXCEPTION_NAMES, dead code left over from an abandoned name-based exception-matching approach; the blanket except Exception already maps every SDK failure correctly. openspec: semantic-scene capability, archived change semantic-scene-runtime
61 lines
1.7 KiB
Python
61 lines
1.7 KiB
Python
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:
|
|
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
|
|
]
|
|
|
|
return SemanticScene(
|
|
page=semantic_scene.page,
|
|
intents=semantic_scene.intents,
|
|
widgets=widgets,
|
|
)
|