72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
"""Routing helpers for Home Assistant user-aware OpenClaw chat sessions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
DEFAULT_CHAT_AGENT_ID = "tetra-home"
|
|
SYSTEM_HA_USER_ID = "system"
|
|
|
|
HA_USER_AGENT_MAP: dict[str, str] = {
|
|
"bd3f666f43f941e886ef007a794eefe8": "main",
|
|
"1d2be9a622584cf0abb1c243d12a9959": "family-krzysztof",
|
|
"eca5887898544556840878ab37a35fbc": "family-ewa",
|
|
"2f5bf9f09478400082380bf7f26966d9": "family-tosia",
|
|
"f613d90efe8b4afea63c4a5a7ef7b559": "family-domi",
|
|
}
|
|
|
|
|
|
def normalize_optional_text(value: Any) -> str | None:
|
|
"""Return a stripped string, or None for blank/non-string values."""
|
|
if not isinstance(value, str):
|
|
return None
|
|
cleaned = value.strip()
|
|
return cleaned or None
|
|
|
|
|
|
def normalize_ha_user_id(value: Any) -> str:
|
|
"""Return a stable HA user id for routing/history scope."""
|
|
return normalize_optional_text(value) or SYSTEM_HA_USER_ID
|
|
|
|
|
|
def resolve_agent_id(
|
|
ha_user_id: str | None,
|
|
explicit_agent_id: str | None = None,
|
|
) -> str:
|
|
"""Resolve the OpenClaw agent for a Home Assistant user."""
|
|
if explicit_agent_id:
|
|
return explicit_agent_id
|
|
normalized_user_id = normalize_ha_user_id(ha_user_id)
|
|
return HA_USER_AGENT_MAP.get(normalized_user_id, DEFAULT_CHAT_AGENT_ID)
|
|
|
|
|
|
def resolve_model_override(value: Any) -> str | None:
|
|
"""Return a provider model override, excluding OpenClaw pseudo-model ids."""
|
|
model = normalize_optional_text(value)
|
|
if model and model.startswith("openclaw/"):
|
|
return None
|
|
return model
|
|
|
|
|
|
def build_scoped_session_id(
|
|
raw_session_id: str | None,
|
|
ha_user_id: str | None,
|
|
agent_id: str | None,
|
|
) -> str:
|
|
"""Build the backend/gateway session id scoped by user, agent, and raw session."""
|
|
raw_session = normalize_optional_text(raw_session_id) or "default"
|
|
user = normalize_ha_user_id(ha_user_id)
|
|
agent = normalize_optional_text(agent_id) or DEFAULT_CHAT_AGENT_ID
|
|
return (
|
|
f"ha_user_{_sanitize_session_part(user)}"
|
|
f"__agent_{_sanitize_session_part(agent)}"
|
|
f"__session_{_sanitize_session_part(raw_session)}"
|
|
)
|
|
|
|
|
|
def _sanitize_session_part(value: str) -> str:
|
|
"""Keep scoped session ids header-safe and readable."""
|
|
cleaned = re.sub(r"[^A-Za-z0-9_.-]+", "_", value.strip())
|
|
return cleaned.strip("_") or "default"
|