feat: add seat-scoped static and dynamic assistant modes
This commit is contained in:
@@ -24,11 +24,13 @@ from cognitive_agent.application.assistant_plan_guard import (
|
||||
SCENE_COMPOSABLE_CAPABILITIES,
|
||||
)
|
||||
from cognitive_agent.application.mode_profile import ModeOperation
|
||||
from cognitive_agent.application.mode_profile_intent import mode_creation_source_name
|
||||
from cognitive_agent.application.mode_schedule import ModeSchedule
|
||||
|
||||
_TEMPLATE_ID = re.compile(r"^[a-z0-9][a-z0-9_-]{0,63}$")
|
||||
_SURROUNDING_PUNCTUATION = " \t\r\n,。,.!!??;;::'\"‘’“”《》<>【】[]()()"
|
||||
_DOCUMENT_FIELDS = frozenset(
|
||||
{"id", "name", "aliases", "description", "icon", "operations"}
|
||||
{"id", "name", "aliases", "description", "icon", "operations", "schedule"}
|
||||
)
|
||||
|
||||
|
||||
@@ -66,6 +68,7 @@ class SceneTemplate:
|
||||
operations: tuple[ModeOperation, ...]
|
||||
source: str
|
||||
revision: str
|
||||
schedule: ModeSchedule | None = None
|
||||
|
||||
@classmethod
|
||||
def from_yaml(cls, path: Path) -> "SceneTemplate":
|
||||
@@ -100,7 +103,13 @@ class SceneTemplate:
|
||||
alias_keys.add(key)
|
||||
aliases.append(alias)
|
||||
|
||||
schedule = (ModeSchedule.from_dict(document["schedule"])
|
||||
if document.get("schedule") is not None else None)
|
||||
raw_operations = document.get("operations")
|
||||
if schedule is not None:
|
||||
if raw_operations:
|
||||
raise ValueError(f"{path} cannot mix static operations and a schedule")
|
||||
raw_operations = list(schedule.phases[0].operations)
|
||||
if not isinstance(raw_operations, list) or not raw_operations:
|
||||
raise ValueError(f"{path} must contain at least one operation")
|
||||
if len(raw_operations) > MAX_PLAN_REQUESTS:
|
||||
@@ -155,6 +164,8 @@ class SceneTemplate:
|
||||
),
|
||||
),
|
||||
}
|
||||
if schedule is not None:
|
||||
semantic_document["schedule"] = schedule.to_dict()
|
||||
canonical = json.dumps(
|
||||
semantic_document,
|
||||
ensure_ascii=False,
|
||||
@@ -171,6 +182,7 @@ class SceneTemplate:
|
||||
operations=tuple(operations),
|
||||
source=str(path),
|
||||
revision=revision,
|
||||
schedule=schedule,
|
||||
)
|
||||
|
||||
def plan(self) -> dict[str, Any]:
|
||||
@@ -179,7 +191,7 @@ class SceneTemplate:
|
||||
request = operation.to_request(self.name)
|
||||
request["duration_ms"] = None
|
||||
requests.append(request)
|
||||
return {
|
||||
plan = {
|
||||
"detected_domains": list(
|
||||
dict.fromkeys(operation.domain for operation in self.operations)
|
||||
),
|
||||
@@ -195,6 +207,9 @@ class SceneTemplate:
|
||||
"template_revision": self.revision,
|
||||
},
|
||||
}
|
||||
if self.schedule is not None:
|
||||
plan["schedule"] = self.schedule.to_dict()
|
||||
return plan
|
||||
|
||||
def public_dict(self, *, include_plan: bool = False) -> dict[str, Any]:
|
||||
result: dict[str, Any] = {
|
||||
@@ -204,13 +219,16 @@ class SceneTemplate:
|
||||
"description": self.description,
|
||||
"icon": self.icon,
|
||||
"domains": list(dict.fromkeys(item.domain for item in self.operations)),
|
||||
"operation_count": len(self.operations),
|
||||
"operation_count": self.schedule.operation_count if self.schedule else len(self.operations),
|
||||
"template_revision": self.revision,
|
||||
"origin": "builtin_scene_template",
|
||||
"read_only": True,
|
||||
}
|
||||
if include_plan:
|
||||
result["plan"] = self.plan()
|
||||
if self.schedule is not None:
|
||||
result["schedule"] = self.schedule.to_dict()
|
||||
result["domains"] = sorted({op["domain"] for phase in self.schedule.phases for op in phase.operations})
|
||||
return result
|
||||
|
||||
|
||||
@@ -257,7 +275,11 @@ class SceneTemplateRegistry:
|
||||
|
||||
def resolve_exact_name(self, value: object) -> SceneTemplate | None:
|
||||
key = normalize_scene_template_name(value)
|
||||
return self._by_name.get(key) if key else None
|
||||
exact = self._by_name.get(key) if key else None
|
||||
if exact is not None:
|
||||
return exact
|
||||
subject = normalize_scene_template_name(mode_creation_source_name(value))
|
||||
return self._by_name.get(subject) if subject and subject != key else None
|
||||
|
||||
def get_template(self, template_id: str) -> SceneTemplate:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user