feat: integrate production cockpit AI assistant
This commit is contained in:
@@ -30,7 +30,7 @@ MAX_PLAN_REQUESTS = 12
|
||||
MAX_PLAN_PARAMETERS = 12
|
||||
|
||||
_OPERATIONS = frozenset({"activate", "deactivate", "increase", "decrease", "set", "save", "apply"})
|
||||
_SCENE_COMPOSABLE_CAPABILITIES = frozenset(
|
||||
SCENE_COMPOSABLE_CAPABILITIES = frozenset(
|
||||
{
|
||||
"seat.set_longitudinal_position",
|
||||
"seat.set_cushion_tilt",
|
||||
@@ -68,12 +68,18 @@ _SCENE_COMPOSABLE_CAPABILITIES = frozenset(
|
||||
# particular, fan level 0 is a virtual shorthand for "off", while the native
|
||||
# operation is explicitly 1-7; changing it to HVAC power-off here would alter
|
||||
# the model's proposed intent.
|
||||
_SCENE_PARAMETER_BOUNDS: dict[tuple[str, str], tuple[float | None, float | None]] = {
|
||||
SCENE_PARAMETER_BOUNDS: dict[tuple[str, str], tuple[float | None, float | None]] = {
|
||||
("climate.set_fan_level", "level"): (1, 7),
|
||||
}
|
||||
_ROOT_FIELDS = frozenset(
|
||||
{"detected_domains", "requests", "ambiguities", "decision_summary", "confidence"}
|
||||
)
|
||||
_OPTIONAL_ROOT_FIELDS = frozenset({"metadata"})
|
||||
_SCENE_TEMPLATE_METADATA_FIELDS = frozenset(
|
||||
{"source", "template_id", "template_revision"}
|
||||
)
|
||||
_SCENE_TEMPLATE_ID = re.compile(r"^[a-z0-9][a-z0-9_-]{0,63}$")
|
||||
_SCENE_TEMPLATE_REVISION = re.compile(r"^[a-f0-9]{16}$")
|
||||
_REQUEST_FIELDS = frozenset(
|
||||
{
|
||||
"domain",
|
||||
@@ -540,7 +546,7 @@ def _scene_contract_error(
|
||||
return f"参数 {name} 的类型必须是 {expected_type}"
|
||||
minimum = spec.get("minimum")
|
||||
maximum = spec.get("maximum")
|
||||
scene_bounds = _SCENE_PARAMETER_BOUNDS.get((capability, name))
|
||||
scene_bounds = SCENE_PARAMETER_BOUNDS.get((capability, name))
|
||||
if scene_bounds is not None:
|
||||
minimum, maximum = scene_bounds
|
||||
if numeric and (
|
||||
@@ -1090,7 +1096,11 @@ def guard_assistant_plan(
|
||||
or the user's explicit save confirmation.
|
||||
"""
|
||||
|
||||
if not isinstance(raw_plan, Mapping) or set(raw_plan) != _ROOT_FIELDS:
|
||||
if (
|
||||
not isinstance(raw_plan, Mapping)
|
||||
or not _ROOT_FIELDS.issubset(raw_plan)
|
||||
or set(raw_plan) - _ROOT_FIELDS - _OPTIONAL_ROOT_FIELDS
|
||||
):
|
||||
raise ValueError("assistant plan must be a structured intent object")
|
||||
try:
|
||||
encoded = json.dumps(raw_plan, ensure_ascii=False, allow_nan=False).encode("utf-8")
|
||||
@@ -1099,6 +1109,29 @@ def guard_assistant_plan(
|
||||
if len(encoded) > MAX_PLAN_BYTES:
|
||||
raise ValueError("assistant plan is too large")
|
||||
|
||||
plan_metadata: dict[str, str] | None = None
|
||||
if "metadata" in raw_plan:
|
||||
raw_metadata = raw_plan.get("metadata")
|
||||
if (
|
||||
not isinstance(raw_metadata, Mapping)
|
||||
or set(raw_metadata) != _SCENE_TEMPLATE_METADATA_FIELDS
|
||||
or raw_metadata.get("source") != "scene_template"
|
||||
or not _SCENE_TEMPLATE_ID.fullmatch(
|
||||
str(raw_metadata.get("template_id") or "")
|
||||
)
|
||||
or not _SCENE_TEMPLATE_REVISION.fullmatch(
|
||||
str(raw_metadata.get("template_revision") or "")
|
||||
)
|
||||
):
|
||||
raise ValueError("assistant plan metadata is invalid")
|
||||
if not allow_scene_composition:
|
||||
raise ValueError("assistant plan metadata is only valid for scene composition")
|
||||
plan_metadata = {
|
||||
"source": "scene_template",
|
||||
"template_id": str(raw_metadata["template_id"]),
|
||||
"template_revision": str(raw_metadata["template_revision"]),
|
||||
}
|
||||
|
||||
requests = raw_plan.get("requests", [])
|
||||
if not isinstance(requests, Sequence) or isinstance(requests, (str, bytes, bytearray)):
|
||||
raise ValueError("assistant plan requests must be an array") # noqa: TRY004
|
||||
@@ -1155,7 +1188,7 @@ def guard_assistant_plan(
|
||||
}
|
||||
)
|
||||
continue
|
||||
if allow_scene_composition and capability not in _SCENE_COMPOSABLE_CAPABILITIES:
|
||||
if allow_scene_composition and capability not in SCENE_COMPOSABLE_CAPABILITIES:
|
||||
rejected.append(
|
||||
{
|
||||
"field": f"requests[{index}].capability",
|
||||
@@ -1320,9 +1353,11 @@ def guard_assistant_plan(
|
||||
"detected_domains": detected_domains,
|
||||
"requests": accepted,
|
||||
"ambiguities": [*rejected],
|
||||
"decision_summary": decision_summary or ["Codex 计划已通过原始请求能力约束。"],
|
||||
"decision_summary": decision_summary or ["小迪生成的计划已通过原始请求能力约束。"],
|
||||
"confidence": confidence,
|
||||
}
|
||||
if plan_metadata is not None:
|
||||
normalized["metadata"] = plan_metadata
|
||||
return GuardedAssistantPlan(normalized, tuple(rejected))
|
||||
|
||||
|
||||
@@ -1330,6 +1365,8 @@ __all__ = [
|
||||
"MAX_PLAN_BYTES",
|
||||
"MAX_PLAN_PARAMETERS",
|
||||
"MAX_PLAN_REQUESTS",
|
||||
"SCENE_COMPOSABLE_CAPABILITIES",
|
||||
"SCENE_PARAMETER_BOUNDS",
|
||||
"GuardedAssistantPlan",
|
||||
"guard_assistant_plan",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user