101 lines
3.2 KiB
Python
101 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
SEMANTIC_SCENE_SCHEMA: dict[str, Any] = {
|
|
"type": "object",
|
|
"additionalProperties": False,
|
|
"required": ["page", "intents", "widgets"],
|
|
"properties": {
|
|
"page": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
},
|
|
"intents": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
},
|
|
},
|
|
"widgets": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"additionalProperties": False,
|
|
"required": ["element_id", "purpose"],
|
|
"properties": {
|
|
"element_id": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
},
|
|
"purpose": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SemanticWidget:
|
|
element_id: str
|
|
purpose: str
|
|
|
|
def to_dict(self) -> dict[str, str]:
|
|
return {
|
|
"element_id": self.element_id,
|
|
"purpose": self.purpose,
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict[str, Any]) -> "SemanticWidget":
|
|
if not isinstance(data, dict):
|
|
raise TypeError("semantic widget must be a mapping")
|
|
element_id = data.get("element_id")
|
|
purpose = data.get("purpose")
|
|
if not isinstance(element_id, str) or not element_id.strip():
|
|
raise ValueError("semantic widget element_id must be a non-empty string")
|
|
if not isinstance(purpose, str) or not purpose.strip():
|
|
raise ValueError("semantic widget purpose must be a non-empty string")
|
|
return cls(element_id=element_id, purpose=purpose)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SemanticScene:
|
|
page: str
|
|
intents: list[str] = field(default_factory=list)
|
|
widgets: list[SemanticWidget] = field(default_factory=list)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"page": self.page,
|
|
"intents": list(self.intents),
|
|
"widgets": [widget.to_dict() for widget in self.widgets],
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict[str, Any]) -> "SemanticScene":
|
|
if not isinstance(data, dict):
|
|
raise TypeError("semantic scene must be a mapping")
|
|
page = data.get("page")
|
|
intents = data.get("intents")
|
|
widgets = data.get("widgets")
|
|
if not isinstance(page, str) or not page.strip():
|
|
raise ValueError("semantic scene page must be a non-empty string")
|
|
if not isinstance(intents, list) or not all(
|
|
isinstance(intent, str) and intent.strip() for intent in intents
|
|
):
|
|
raise ValueError("semantic scene intents must be non-empty strings")
|
|
if not isinstance(widgets, list):
|
|
raise ValueError("semantic scene widgets must be a list")
|
|
return cls(
|
|
page=page,
|
|
intents=list(intents),
|
|
widgets=[SemanticWidget.from_dict(widget) for widget in widgets],
|
|
)
|