Initial commit
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
"""Utilities for normalizing worker-facing message envelopes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
MESSAGE_CLASSES = {"chat", "protocol", "notification"}
|
||||
# Protocol types that messages may carry. Ten previously-declared values
|
||||
# (permission_*, plan_approval_*, dependency_*, approval_reply, cross_team_*)
|
||||
# were never actually written by any code path and have been removed along
|
||||
# with the matching CommsSemanticType enum values. See
|
||||
# plans/task-cleanup-dead-comms.md.
|
||||
_PROTOCOL_TYPES = {
|
||||
"approval_request",
|
||||
"shutdown_request",
|
||||
"shutdown_response",
|
||||
"ack",
|
||||
}
|
||||
_NOTIFICATION_KINDS = {
|
||||
"idle",
|
||||
"task_complete",
|
||||
"blocked",
|
||||
"handoff_ready",
|
||||
"completion",
|
||||
"status_digest",
|
||||
"permission_needed",
|
||||
"error",
|
||||
}
|
||||
|
||||
_SEMANTIC_PROTOCOL_MAP = {
|
||||
"approval_request": "approval_request",
|
||||
}
|
||||
|
||||
_SEMANTIC_NOTIFICATION_MAP = {
|
||||
"idle_notification": "idle",
|
||||
"handoff_ready": "handoff_ready",
|
||||
"work_item_result": "task_complete",
|
||||
"blocker": "blocked",
|
||||
"completion": "completion",
|
||||
"status_digest": "status_digest",
|
||||
}
|
||||
|
||||
|
||||
def _clean_text(value: Any) -> str:
|
||||
return str(value or "").strip()
|
||||
|
||||
|
||||
def _clean_choice(value: Any, allowed: set[str]) -> str:
|
||||
normalized = _clean_text(value).lower()
|
||||
return normalized if normalized in allowed else ""
|
||||
|
||||
|
||||
def _coerce_bool(value: Any) -> bool | None:
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
normalized = value.strip().lower()
|
||||
if normalized in {"true", "1", "yes", "y", "on"}:
|
||||
return True
|
||||
if normalized in {"false", "0", "no", "n", "off"}:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def normalize_worker_envelope_metadata(
|
||||
metadata: dict[str, Any] | None = None,
|
||||
*,
|
||||
msg_type: str = "",
|
||||
semantic_type: str = "",
|
||||
transport_kind: str = "",
|
||||
from_agent: str = "",
|
||||
reply_needed: bool = False,
|
||||
worker_id: str = "",
|
||||
task_id: str = "",
|
||||
projection_id: str = "",
|
||||
session_id: str = "",
|
||||
) -> dict[str, Any]:
|
||||
"""Return additive worker-envelope metadata for chat/protocol/notification routing."""
|
||||
merged = dict(metadata or {})
|
||||
|
||||
protocol_type = _clean_choice(merged.get("protocol_type"), _PROTOCOL_TYPES)
|
||||
if not protocol_type:
|
||||
protocol_type = _SEMANTIC_PROTOCOL_MAP.get(_clean_text(semantic_type).lower(), "")
|
||||
if not protocol_type and _clean_text(msg_type).lower() == "ack":
|
||||
protocol_type = "ack"
|
||||
|
||||
notification_kind = _clean_choice(merged.get("notification_kind"), _NOTIFICATION_KINDS)
|
||||
if not notification_kind:
|
||||
notification_kind = _SEMANTIC_NOTIFICATION_MAP.get(_clean_text(semantic_type).lower(), "")
|
||||
resident_status = _clean_text(merged.get("resident_status")).lower()
|
||||
if not notification_kind and resident_status in {"idle", "blocked"}:
|
||||
notification_kind = resident_status
|
||||
if not notification_kind and _clean_text(merged.get("status")).lower() in {"failed", "error"}:
|
||||
notification_kind = "error"
|
||||
|
||||
message_class = _clean_choice(merged.get("message_class"), MESSAGE_CLASSES)
|
||||
if not message_class:
|
||||
if protocol_type:
|
||||
message_class = "protocol"
|
||||
elif notification_kind:
|
||||
message_class = "notification"
|
||||
else:
|
||||
message_class = "chat"
|
||||
|
||||
actionable = _coerce_bool(merged.get("actionable"))
|
||||
if actionable is None:
|
||||
actionable = message_class != "notification"
|
||||
|
||||
resolved_worker_id = (
|
||||
_clean_text(merged.get("worker_id"))
|
||||
or _clean_text(worker_id)
|
||||
or _clean_text(merged.get("member_session_id"))
|
||||
or _clean_text(merged.get("runtime_session_id"))
|
||||
or _clean_text(from_agent)
|
||||
)
|
||||
|
||||
origin_task_id = _clean_text(merged.get("origin_task_id")) or _clean_text(task_id)
|
||||
origin_projection_id = _clean_text(merged.get("origin_projection_id")) or _clean_text(projection_id)
|
||||
origin_session_id = _clean_text(merged.get("origin_session_id")) or _clean_text(session_id)
|
||||
|
||||
merged.update(
|
||||
{
|
||||
"message_class": message_class,
|
||||
"protocol_type": protocol_type or None,
|
||||
"notification_kind": notification_kind or None,
|
||||
"actionable": bool(actionable),
|
||||
"worker_id": resolved_worker_id,
|
||||
"origin_task_id": origin_task_id or None,
|
||||
"origin_projection_id": origin_projection_id or None,
|
||||
"origin_session_id": origin_session_id or None,
|
||||
}
|
||||
)
|
||||
if reply_needed and message_class == "notification":
|
||||
merged["actionable"] = True
|
||||
if _clean_text(transport_kind):
|
||||
merged.setdefault("transport_kind", _clean_text(transport_kind).lower())
|
||||
if _clean_text(semantic_type):
|
||||
merged.setdefault("semantic_type", _clean_text(semantic_type).lower())
|
||||
return merged
|
||||
|
||||
|
||||
def envelope_fields_from_message(message: dict[str, Any]) -> dict[str, Any]:
|
||||
metadata = normalize_worker_envelope_metadata(
|
||||
dict(message.get("metadata", {}) or {}),
|
||||
msg_type=_clean_text(message.get("msg_type")),
|
||||
semantic_type=_clean_text(message.get("semantic_type")),
|
||||
transport_kind=_clean_text(message.get("transport_kind")),
|
||||
from_agent=_clean_text(message.get("from_agent") or message.get("from")),
|
||||
reply_needed=bool(message.get("reply_needed")),
|
||||
worker_id=_clean_text(message.get("worker_id")),
|
||||
task_id=_clean_text(message.get("origin_task_id") or message.get("task_id")),
|
||||
projection_id=_clean_text(message.get("origin_projection_id") or message.get("projection_id")),
|
||||
session_id=_clean_text(message.get("origin_session_id") or message.get("session_id")),
|
||||
)
|
||||
return {
|
||||
"message_class": metadata.get("message_class", "chat"),
|
||||
"protocol_type": metadata.get("protocol_type"),
|
||||
"notification_kind": metadata.get("notification_kind"),
|
||||
"actionable": bool(metadata.get("actionable", True)),
|
||||
"worker_id": metadata.get("worker_id"),
|
||||
"origin_task_id": metadata.get("origin_task_id"),
|
||||
"origin_projection_id": metadata.get("origin_projection_id"),
|
||||
"origin_session_id": metadata.get("origin_session_id"),
|
||||
"metadata": metadata,
|
||||
}
|
||||
|
||||
|
||||
def classify_worker_message(message: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Return a shallow copy with normalized worker-envelope fields."""
|
||||
merged = dict(message)
|
||||
envelope = envelope_fields_from_message(message)
|
||||
merged.update({k: v for k, v in envelope.items() if k != "metadata"})
|
||||
merged["metadata"] = envelope["metadata"]
|
||||
return merged
|
||||
|
||||
|
||||
def worker_message_is_actionable(message: dict[str, Any]) -> bool:
|
||||
envelope = envelope_fields_from_message(message)
|
||||
return bool(envelope.get("actionable", True)) and envelope.get("message_class") in {"chat", "protocol"}
|
||||
Reference in New Issue
Block a user