Split adapter.py monolith into focused modules; restore Ruff complexity defaults (issue #12)
adapter.py was a 3,493-line monolith. Split it into focused modules with clear separation of responsibilities, bringing it down to ~857 lines: - Module-level helpers: hooks, classify, pickers, commands, setup, defaults, secrets - Frame-handler mixins: inbound, tool_frames, push_frames, media_frames, picker_frames, channel_frames, query_frames - mixin_base: IrisAdapterBase (declaration-only base for shared attrs) - adapter.py now holds only IrisAdapter (the composition of the 7 mixins + BasePlatformAdapter), register(), and test-facing re-exports The mixins come before BasePlatformAdapter in the MRO so their methods override the base; super() calls (e.g. send_image) still resolve to BasePlatformAdapter. No circular imports; dispatch.py and http_server.py (instance-method callers) are unaffected. Ruff complexity ceilings (PLR0911/0912/0913/0915) restored to Ruff's built-in defaults (12/50/6/5) instead of "just above the current maxima", which ratchets the bar down as code grows. The existing genuinely-complex functions (frame builders mirroring the wire schema, the QR matrix builder, the dispatch table) carry an explicit `# noqa: PLR09xx` marking them as reviewed, frozen exceptions; new code is held to the default ceilings. All 125 tests green (94 test_android + 31 test_android_http); no new ruff errors introduced.
This commit is contained in:
1 parent
7faaf2aa1c
commit
b8e756c3dd
26 files changed
+3101
-2775
No files matched your search
@@ -0,0 +1,83 @@
|
||||
"""Choice-picker callbacks: clarify + exec approval.
|
||||
|
||||
Build the ``on_choice_selected`` closures the adapter stores in
|
||||
``_pending_pickers``; a ``picker.select`` from the app runs the closure and
|
||||
delivers its reply text as a normal message.
|
||||
"""
|
||||
|
||||
def _clarify_is_multi(clarify_id: str) -> bool:
|
||||
"""True when the pending clarify [clarify_id] allows multiple selections.
|
||||
|
||||
The flag lives on the gateway's pending entry; a missing/expired entry (or
|
||||
any lookup error) is treated as single-select.
|
||||
"""
|
||||
try:
|
||||
from tools import clarify_gateway as _cg
|
||||
|
||||
with _cg._lock:
|
||||
_entry = _cg._entries.get(clarify_id)
|
||||
return bool(_entry and getattr(_entry, "multi_select", False))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _clarify_picker_callback(clarify_id: str, choices: list[str]):
|
||||
"""Build the ``on_choice_selected`` callback for a clarify picker.
|
||||
|
||||
Option values are positional (``c0``..``cN``) plus an ``other`` sentinel;
|
||||
the closure maps them back to the real choice strings. A real pick resolves
|
||||
the clarify (the agent then continues and replies); "Other" flips the entry
|
||||
to text-capture so the next typed message is the answer. An unmappable value
|
||||
also flips to text so a clarify never dead-ends.
|
||||
"""
|
||||
|
||||
async def on_choice_selected(chat_id: str, value: str) -> str | None:
|
||||
from tools.clarify_gateway import mark_awaiting_text, resolve_gateway_clarify
|
||||
|
||||
if value == "other":
|
||||
mark_awaiting_text(clarify_id)
|
||||
return "✏️ Type your answer:"
|
||||
try:
|
||||
idx = int(value[1:]) if value.startswith("c") else -1
|
||||
except ValueError:
|
||||
idx = -1
|
||||
if 0 <= idx < len(choices):
|
||||
resolve_gateway_clarify(clarify_id, choices[idx])
|
||||
return None
|
||||
mark_awaiting_text(clarify_id)
|
||||
return "✏️ Type your answer:"
|
||||
|
||||
return on_choice_selected
|
||||
|
||||
|
||||
# The four exec-approval outcomes hermes understands (tools.approval).
|
||||
_APPROVAL_CHOICES = ("once", "session", "always", "deny")
|
||||
|
||||
|
||||
def _approval_picker_callback(session_key: str):
|
||||
"""Build the ``on_choice_selected`` callback for an exec-approval picker.
|
||||
|
||||
The option values are the raw hermes approval outcomes (``once`` /
|
||||
``session`` / ``always`` / ``deny``); a tap resolves the waiting agent
|
||||
thread via ``resolve_gateway_approval`` (the same primitive the text
|
||||
``/approve`` / ``/deny`` handlers use) and returns a short confirmation
|
||||
label, which the picker handler delivers as a normal message. An unknown
|
||||
value is treated as a deny so a stray tap never approves a command.
|
||||
"""
|
||||
|
||||
async def on_choice_selected(chat_id: str, value: str) -> str | None:
|
||||
from tools.approval import resolve_gateway_approval
|
||||
|
||||
choice = value if value in _APPROVAL_CHOICES else "deny"
|
||||
count = resolve_gateway_approval(session_key, choice)
|
||||
label = {
|
||||
"once": "✅ Approved once",
|
||||
"session": "✅ Approved for this session",
|
||||
"always": "✅ Approved permanently",
|
||||
"deny": "❌ Denied",
|
||||
}[choice]
|
||||
if not count:
|
||||
label = "⌛ Approval expired — no command was waiting."
|
||||
return label
|
||||
|
||||
return on_choice_selected
|
||||
Reference in new issue
Block a user