feat(telegram): /profile picker with cross-restart persistence - #72957
feat(telegram): /profile picker with cross-restart persistence#72957antunes-hq wants to merge 1 commit into
Conversation
Adds an interactive inline-keyboard picker to the /profile slash command, letting users switch the multiplexed gateway between Hermes profiles without restarting. The choice is persisted in session metadata and rehydrated on the next inbound event, so it survives gateway restarts. - Telegram adapter: send_profile_picker + _handle_profile_picker_callback (one-tap = one profile, current profile marked, authorization gate) - slash_commands: /profile lists profiles via list_profiles(), invokes picker when available, falls back to text list otherwise - gateway/run: _rehydrate_profile_from_session looks up the saved profile in session_store metadata before the multiplex resolves - session-store callback writes metadata['profile'] on tap Requires gateway.multiplex_profiles: true. 27 new tests, no regressions in the model_picker suite. Existing test_profile_resolution + thread fallback suites remain green (the 3 thread_fallback failures are pre-existing test pollution unrelated to this change).
Related to #69441: both add interactive multiplex |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the profile-picker work. The feature is not already present on current main, but this implementation needs a contract-level rework.
Problems
gateway/run.py:20133callsorigin.get(...); realSessionStoreentries retainorigin=source(gateway/session.py:2389-2402), so this raises onSessionSourceand the surrounding resolver silently falls back without rehydrating.gateway/run.py:20132-20150selects the first matching session metadata value. Multiple profile-namespaced sessions can match one chat after repeated switches, so this cannot reliably represent the latest selection.- Main already has the generic picker seam in
gateway/slash_commands.py:3290-3326and Telegram'ssend_choice_pickerinplugins/platforms/telegram/adapter.py:5611-5730; the new Telegram-only picker duplicates it.
Suggested changes
- First align the persistent source-routing contract with #69441, as the maintainer comment requests.
- Use the generic picker and test persistence through real
SessionStore/SessionSourceobjects, including repeated switches and thread scope.
Automated hermes-sweeper review.
| ) | ||
|
|
||
| for entry in entries.values(): | ||
| origin = getattr(entry, "origin", None) or {} |
There was a problem hiding this comment.
SessionStore stores SessionEntry.origin as a SessionSource, not a dict (gateway/session.py:2389-2402 on current main). This .get() raises for every real entry, then the caller catches it and silently disables cross-restart rehydration. Use the typed source attributes or a dedicated persistent override record.
| meta = getattr(entry, "metadata", None) or {} | ||
| picked = meta.get("profile") | ||
| if isinstance(picked, str) and picked.strip(): | ||
| return picked.strip() |
There was a problem hiding this comment.
Returning the first matching metadata record is not a stable source-level override: profile multiplexing creates separate session keys per selected profile, so repeated selections can leave several matching entries and an older entry may win by insertion order. Persist one authoritative value keyed by the full source scope instead.
| logger.warning("[%s] send_model_picker failed: %s", self.name, _redact_telegram_error_text(e)) | ||
| return SendResult(success=False, error=_redact_telegram_error_text(e)) | ||
|
|
||
| async def send_profile_picker( |
There was a problem hiding this comment.
Main already exposes the generic send_choice_picker adapter contract and gateway _try_send_choice_picker helper. Reuse that capability rather than adding a Telegram-only parallel picker and callback namespace, so the profile command can share behavior with other interactive platforms.
Summary
Adds a /profile slash command to the Telegram adapter that lets users pick an active Hermes profile and persists the choice in session.metadata["profile"] so it survives restarts.
Changes
Closes: #profile-picker