feat: integrate safer vehicle CAN reasoning
This commit is contained in:
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
TARGETS = {
|
||||
"passenger": {"device": "seat", "zone": "front_right", "instance": "passenger"},
|
||||
"driver": {"device": "seat", "zone": "front_left", "instance": "driver"},
|
||||
}
|
||||
CAPABILITIES = {
|
||||
"ventilation": "seat.set_ventilation",
|
||||
@@ -29,8 +30,145 @@ def _now() -> str:
|
||||
return datetime.now(UTC).isoformat().replace("+00:00", "Z")
|
||||
|
||||
|
||||
def _source_span(utterance: str, source: Any = None) -> dict[str, Any]:
|
||||
text = source if isinstance(source, str) and source else utterance
|
||||
start = utterance.find(text)
|
||||
if start < 0:
|
||||
text = utterance
|
||||
start = 0
|
||||
return {"text": text, "start": start, "end": start + len(text)}
|
||||
|
||||
|
||||
def _semantic_target(domain: str, target: str) -> dict[str, str]:
|
||||
if domain == "seat" and target in TARGETS:
|
||||
return dict(TARGETS[target])
|
||||
return {"device": domain, "zone": target}
|
||||
|
||||
|
||||
def _canonical_semantic_actions(
|
||||
parsed: dict[str, Any],
|
||||
utterance: str,
|
||||
provider: str,
|
||||
capability_metadata: dict[str, dict[str, str]],
|
||||
) -> list[dict[str, Any]]:
|
||||
actions: list[dict[str, Any]] = []
|
||||
|
||||
def append(
|
||||
*,
|
||||
domain: str,
|
||||
capability: str,
|
||||
target: str,
|
||||
operation: str,
|
||||
parameters: dict[str, Any],
|
||||
source: dict[str, Any],
|
||||
) -> None:
|
||||
policy = capability_metadata.get(capability, {})
|
||||
confidence = source.get("confidence")
|
||||
if not isinstance(confidence, dict):
|
||||
confidence = {"overall": float(parsed.get("confidence", 0))}
|
||||
provenance = source.get("provenance")
|
||||
if not isinstance(provenance, dict):
|
||||
provenance = {"parser": provider, "rule_ids": []}
|
||||
semantic: dict[str, Any] = {
|
||||
"id": f"action_{len(actions) + 1}",
|
||||
"domain": domain,
|
||||
"capability": capability,
|
||||
"target": _semantic_target(domain, target),
|
||||
"operation": operation,
|
||||
"parameters": parameters,
|
||||
"source_span": _source_span(utterance, source.get("source_span")),
|
||||
"confidence": confidence,
|
||||
"provenance": provenance,
|
||||
"risk_level": str(policy.get("risk_level", "high")),
|
||||
"confirmation_policy": str(
|
||||
policy.get("confirmation_policy", "on_ambiguity")
|
||||
),
|
||||
}
|
||||
resolution = source.get("relative_resolution", source.get("motion_intent"))
|
||||
if isinstance(resolution, dict):
|
||||
semantic["resolution"] = dict(resolution)
|
||||
actions.append(semantic)
|
||||
|
||||
for action in parsed.get("actions", []):
|
||||
target = str(action["target"])
|
||||
if action.get("action_type") == "position":
|
||||
parameters = {"position_pct": action["position_pct"]}
|
||||
if action.get("angle_deg") is not None:
|
||||
parameters["angle_deg"] = action["angle_deg"]
|
||||
append(
|
||||
domain="seat",
|
||||
capability=MOTION_CAPABILITIES[str(action["axis"])],
|
||||
target=target,
|
||||
operation="set",
|
||||
parameters=parameters,
|
||||
source=action,
|
||||
)
|
||||
elif action.get("action_type") == "dynamic_mode":
|
||||
append(
|
||||
domain="seat",
|
||||
capability=DYNAMIC_MODE_CAPABILITY,
|
||||
target=target,
|
||||
operation="activate",
|
||||
parameters={"mode_id": action["mode_id"]},
|
||||
source=action,
|
||||
)
|
||||
elif action.get("action_type") == "dynamic_mode_stop":
|
||||
append(
|
||||
domain="seat",
|
||||
capability=DYNAMIC_MODE_STOP_CAPABILITY,
|
||||
target=target,
|
||||
operation="deactivate",
|
||||
parameters={},
|
||||
source=action,
|
||||
)
|
||||
else:
|
||||
parameters = {"state": action["state"]}
|
||||
if action.get("requested_level") is not None:
|
||||
parameters["level"] = action["requested_level"]
|
||||
append(
|
||||
domain="seat",
|
||||
capability=CAPABILITIES[str(action["function"])],
|
||||
target=target,
|
||||
operation="set",
|
||||
parameters=parameters,
|
||||
source=action,
|
||||
)
|
||||
|
||||
for action in [
|
||||
*parsed.get("domain_actions", []),
|
||||
*parsed.get("native_can_actions", []),
|
||||
]:
|
||||
append(
|
||||
domain=str(action["domain"]),
|
||||
capability=str(action["capability"]),
|
||||
target=str(action["target"]),
|
||||
operation=str(action.get("operation", "set")),
|
||||
parameters=dict(action.get("parameters", {})),
|
||||
source=action,
|
||||
)
|
||||
|
||||
unique: list[dict[str, Any]] = []
|
||||
signatures: set[str] = set()
|
||||
for action in actions:
|
||||
signature = repr(
|
||||
(
|
||||
action["domain"], action["capability"], action["target"],
|
||||
action["operation"], action["parameters"],
|
||||
)
|
||||
)
|
||||
if signature not in signatures:
|
||||
signatures.add(signature)
|
||||
action["id"] = f"action_{len(unique) + 1}"
|
||||
unique.append(action)
|
||||
return unique
|
||||
|
||||
|
||||
def build_intent_ir(
|
||||
parsed: dict[str, Any], utterance: str, request_id: str, provider: str
|
||||
parsed: dict[str, Any],
|
||||
utterance: str,
|
||||
request_id: str,
|
||||
provider: str,
|
||||
capability_metadata: dict[str, dict[str, str]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
requests = []
|
||||
for action in parsed["actions"]:
|
||||
@@ -154,7 +292,57 @@ def build_intent_ir(
|
||||
}
|
||||
)
|
||||
|
||||
detected_domains = parsed.get("detected_domains", ["seat"])
|
||||
semantic_actions = _canonical_semantic_actions(
|
||||
parsed, utterance, provider, capability_metadata or {}
|
||||
)
|
||||
constraints = []
|
||||
for index, raw in enumerate(parsed.get("semantic_constraints", []), start=1):
|
||||
constraint = dict(raw)
|
||||
constraint["id"] = str(constraint.get("id") or f"constraint_{index}")
|
||||
if not isinstance(constraint.get("source_span"), dict):
|
||||
constraint["source_span"] = _source_span(
|
||||
utterance, constraint.get("source_span")
|
||||
)
|
||||
if isinstance(constraint.get("target"), str):
|
||||
constraint["target"] = _semantic_target(
|
||||
str(constraint.get("domain", "seat")), constraint["target"]
|
||||
)
|
||||
constraints.append(constraint)
|
||||
|
||||
unresolved_spans = []
|
||||
for raw in parsed.get("unresolved_spans", []):
|
||||
item = {"text": raw} if isinstance(raw, str) else dict(raw)
|
||||
unresolved_spans.append(
|
||||
{
|
||||
**_source_span(utterance, item.get("text")),
|
||||
"reason": str(
|
||||
item.get("reason", "not_mapped_to_registered_capability")
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
covered_spans = []
|
||||
covered_signatures: set[tuple[int, int]] = set()
|
||||
for item in [*semantic_actions, *constraints]:
|
||||
span = item["source_span"]
|
||||
signature = (int(span["start"]), int(span["end"]))
|
||||
if signature not in covered_signatures:
|
||||
covered_signatures.add(signature)
|
||||
covered_spans.append(dict(span))
|
||||
|
||||
detected_domains = list(parsed.get("detected_domains", ["seat"]))
|
||||
for action in semantic_actions:
|
||||
if action["domain"] not in detected_domains:
|
||||
detected_domains.append(action["domain"])
|
||||
ambiguities = list(parsed.get("ambiguities", []))
|
||||
requires_confirmation = bool(
|
||||
ambiguities
|
||||
or unresolved_spans
|
||||
or any(
|
||||
action["confirmation_policy"] == "always"
|
||||
for action in semantic_actions
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"apiVersion": "cognitive-agent/v1alpha1",
|
||||
@@ -173,9 +361,17 @@ def build_intent_ir(
|
||||
"mode": "cross_domain" if len(detected_domains) > 1 else "single_domain",
|
||||
"detected_domains": detected_domains,
|
||||
"requests": requests,
|
||||
"ambiguities": parsed["ambiguities"],
|
||||
"actions": semantic_actions,
|
||||
"constraints": constraints,
|
||||
"ambiguities": ambiguities,
|
||||
"unresolved_spans": unresolved_spans,
|
||||
"coverage": {
|
||||
"complete": not unresolved_spans,
|
||||
"covered_spans": covered_spans,
|
||||
},
|
||||
"confidence": parsed["confidence"],
|
||||
"requires_confirmation": bool(parsed["ambiguities"]),
|
||||
"requires_confirmation": requires_confirmation,
|
||||
"execution_eligible": bool(semantic_actions) and not requires_confirmation,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user