"""Name↔int codecs for enum-like columns stored as ``SMALLINT``. Several low-cardinality closed-set columns (``conversations.kind``, ``conversation_items.type``/``status``, ``comments.status``, ``account_tokens.kind``, ``policies.type``, ``policies.scope``, ``hosts.status``, ``agents.kind``) are stored as integer codes rather than their string names — smaller rows and a tighter ``CHECK`` than a free ``VARCHAR``. The string names remain the contract for entities, the HTTP API, the web client, and the SDKs; the integer form never leaves the store row↔entity boundary. These codecs are the single place that translates between the two. Codes are STABLE and append-only: never renumber or reuse a shipped code, and leave gaps rather than reordering, so old rows keep their meaning. This mirrors :data:`omnigent.server.auth.LEVEL_READ` and friends, the existing int-coded ``session_permissions.level``. """ from __future__ import annotations from omnigent.entities.conversation import ITEM_TYPE_TO_DATA_CLS # ── Code tables (name → stable int code) ─────────────── CONVERSATION_KIND: dict[str, int] = { "default": 1, "sub_agent": 2, } # Item type codes. The key set is kept in lock-step with # ITEM_TYPE_TO_DATA_CLS (the app-layer source of truth) by # _assert_item_type_codes_cover_data_classes below, so a newly added item # type cannot ship without a code. Codes are append-only. ITEM_TYPE: dict[str, int] = { "message": 1, "function_call": 2, "function_call_output": 3, "reasoning": 4, "error": 5, "compaction": 6, "native_tool": 7, "resource_event": 8, "routing_decision": 9, "slash_command": 10, "terminal_command": 11, } # Item status codes. Only "completed" is written today (items are final on # append), but the field is semantically an OpenAI-style status that may # widen, so codes for the rest of that vocabulary are reserved up front and # the column CHECK admits all of them. ITEM_STATUS: dict[str, int] = { "completed": 1, "in_progress": 2, "incomplete": 3, "failed": 4, } COMMENT_STATUS: dict[str, int] = { "draft": 1, "addressed": 2, } ACCOUNT_TOKEN_KIND: dict[str, int] = { "invite": 1, "magic": 2, } POLICY_TYPE: dict[str, int] = { "python": 1, "url": 2, } HOST_STATUS: dict[str, int] = { "online": 1, "offline": 2, } AGENT_KIND: dict[str, int] = { "template": 1, "session": 2, } POLICY_SCOPE: dict[str, int] = { "default": 1, "session": 2, } def _assert_item_type_codes_cover_data_classes() -> None: """ Guard that :data:`ITEM_TYPE` matches the app's item-type registry. Raised at import time (and asserted by a unit test) so a new item type added to ``ITEM_TYPE_TO_DATA_CLS`` without a corresponding code fails loudly instead of silently breaking persistence. :raises RuntimeError: If the two key sets diverge. """ missing = set(ITEM_TYPE_TO_DATA_CLS) - set(ITEM_TYPE) extra = set(ITEM_TYPE) - set(ITEM_TYPE_TO_DATA_CLS) if missing or extra: raise RuntimeError( "ITEM_TYPE codes are out of sync with ITEM_TYPE_TO_DATA_CLS " f"(missing codes for {sorted(missing)}, " f"unknown types {sorted(extra)})." ) _assert_item_type_codes_cover_data_classes() # ── Encode / decode ──────────────────────────────────── def _invert(table: dict[str, int]) -> dict[int, str]: """Return the code→name inverse of a name→code table.""" return {code: name for name, code in table.items()} _CODE_TO_NAME: dict[int, dict[int, str]] = {} def _encode(table: dict[str, int], name: str, *, field: str) -> int: """ Map an enum *name* to its stable integer code. :param table: The name→code table for the field. :param name: The string enum name, e.g. ``"sub_agent"``. :param field: Field label used in the error message, e.g. ``"conversations.kind"``. :returns: The integer code. :raises ValueError: If *name* is not a known value for the field. """ try: return table[name] except KeyError: raise ValueError(f"unknown {field} value: {name!r}") from None def _decode(table: dict[str, int], code: int, *, field: str) -> str: """ Map an integer *code* back to its enum name. :param table: The name→code table for the field. :param code: The stored integer code. :param field: Field label used in the error message, e.g. ``"conversations.kind"``. :returns: The string enum name. :raises ValueError: If *code* is not a known code for the field. """ inverse = _CODE_TO_NAME.get(id(table)) if inverse is None: inverse = _invert(table) _CODE_TO_NAME[id(table)] = inverse try: return inverse[code] except KeyError: raise ValueError(f"unknown {field} code: {code!r}") from None def encode_conversation_kind(name: str) -> int: """Encode a ``conversations.kind`` name to its int code.""" return _encode(CONVERSATION_KIND, name, field="conversations.kind") def decode_conversation_kind(code: int) -> str: """Decode a ``conversations.kind`` int code to its name.""" return _decode(CONVERSATION_KIND, code, field="conversations.kind") def encode_item_type(name: str) -> int: """Encode a ``conversation_items.type`` name to its int code.""" return _encode(ITEM_TYPE, name, field="conversation_items.type") def decode_item_type(code: int) -> str: """Decode a ``conversation_items.type`` int code to its name.""" return _decode(ITEM_TYPE, code, field="conversation_items.type") def encode_item_status(name: str) -> int: """Encode a ``conversation_items.status`` name to its int code.""" return _encode(ITEM_STATUS, name, field="conversation_items.status") def decode_item_status(code: int) -> str: """Decode a ``conversation_items.status`` int code to its name.""" return _decode(ITEM_STATUS, code, field="conversation_items.status") def encode_comment_status(name: str) -> int: """Encode a ``comments.status`` name to its int code.""" return _encode(COMMENT_STATUS, name, field="comments.status") def decode_comment_status(code: int) -> str: """Decode a ``comments.status`` int code to its name.""" return _decode(COMMENT_STATUS, code, field="comments.status") def encode_account_token_kind(name: str) -> int: """Encode an ``account_tokens.kind`` name to its int code.""" return _encode(ACCOUNT_TOKEN_KIND, name, field="account_tokens.kind") def decode_account_token_kind(code: int) -> str: """Decode an ``account_tokens.kind`` int code to its name.""" return _decode(ACCOUNT_TOKEN_KIND, code, field="account_tokens.kind") def encode_policy_type(name: str) -> int: """Encode a ``policies.type`` name to its int code.""" return _encode(POLICY_TYPE, name, field="policies.type") def decode_policy_type(code: int) -> str: """Decode a ``policies.type`` int code to its name.""" return _decode(POLICY_TYPE, code, field="policies.type") def encode_host_status(name: str) -> int: """Encode a ``hosts.status`` name to its int code.""" return _encode(HOST_STATUS, name, field="hosts.status") def decode_host_status(code: int) -> str: """Decode a ``hosts.status`` int code to its name.""" return _decode(HOST_STATUS, code, field="hosts.status") def encode_agent_kind(name: str) -> int: """Encode an ``agents.kind`` name to its int code.""" return _encode(AGENT_KIND, name, field="agents.kind") def decode_agent_kind(code: int) -> str: """Decode an ``agents.kind`` int code to its name.""" return _decode(AGENT_KIND, code, field="agents.kind") def encode_policy_scope(name: str) -> int: """Encode a ``policies.scope`` name to its int code.""" return _encode(POLICY_SCOPE, name, field="policies.scope") def decode_policy_scope(code: int) -> str: """Decode a ``policies.scope`` int code to its name.""" return _decode(POLICY_SCOPE, code, field="policies.scope")