feat(gateway): interactive /profile picker (parity with /model) - #69441
feat(gateway): interactive /profile picker (parity with /model)#69441rarf wants to merge 7 commits into
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for building the live-routing UX; current main still only reports the serving profile, so the capability remains useful.
Problems
gateway/platforms/base.py:5771resolves persisted overrides without themultiplex_profilesgate. Current main's route resolver deliberately returnsNonewhile multiplexing is off (gateway/run.py:20748-20767) to avoid profile-stamped session/batch state without a profile-scoped runtime. A saved pin must be ignored when multiplexing is disabled.- The mutations beginning at PR
gateway/slash_commands.py:386have no admin check. A configureduser_allowed_commandslist can permit non-admin users to run/profile(gateway/slash_access.py:79-88), whereas existing persistent security mutations recheck admin authority (gateway/slash_commands.py:3655-3661). Retargeting a chat changes its profile-scoped config, skills, memory, and secrets. gateway/profile_overrides.py:49-54uses an unlocked read-modify-write cycle and one shared.json.tmppath. Picker callbacks write from worker threads, so concurrent selections can lose an update or race during rename.
Suggested changes
- Gate override lookup on multiplexing and cover the disabled-after-pin case.
- Require admin authority for set/clear and picker selections; retain read-only status/list behavior.
- Serialize persistent updates with unique temporary files and test concurrent pins.
Automated hermes-sweeper review.
ef475a4 to
1194876
Compare
|
Reconciled with the current main in 1194876.
Local verification: 55 targeted gateway tests passed; ruff, py_compile, uv lock --check, and git diff --check passed. The PR is now reported as mergeable; GitHub checks are running. |
Preserve the shared profile command executor while adding live picker overrides, authorization checks, multiplex gating, and serialized persistence.
|
🔄 Updated the PR branch with two enhancements:
Commit: on This makes live profile switching truly live + shows exactly which model will handle the next message. |
|
🔄 Updated the PR branch with two enhancements:
Commit: This makes live profile switching truly live + shows exactly which model will handle the next message. |
|
Addressed all three points from the sweeper review in 1194876:\n\n- base.py:5771 — build_source now applies a persisted override only when the injected runner config has multiplex_profiles enabled (ignored when disabled-after-pin).\n- slash_commands.py:407 — set/clear now recheck policy_for_source(...).is_admin(...), and the picker callback rechecks the same authority at selection time.\n- profile_overrides.py:52 — read-modify-write is now guarded by a process-safe lock and atomic_json_write (unique temp files + atomic replace).\n\nRequesting another pass when you have a moment. Thanks! |
Preserve the shared profile command executor while adding live picker overrides, authorization checks, multiplex gating, and serialized persistence.
Mirrors the /model cost-confirm cancel: selecting Cancel in the picker leaves the current profile pin untouched (no-op), giving users an explicit escape from the switch without persisting anything.
…er/home) The /profile command referenced gateway.profile.picker_title, .header and .home but those keys were never in the locale catalogs, so the picker title and status card rendered the raw key path. Add human-readable strings to en.yaml and pt.yaml (the two languages in use).
…NameError) The cancel-option edit nested the choices.append(cancel) and the _on_profile_selected function definition INSIDE the for info in infos loop, leaving the function body (which references 'value') dangling at loop level. Calling /profile hit 'name value is not defined'. Re-indent so the cancel choice and the full callback are outside the loop.
…tribution
- Add gateway.profile.picker_title to the 15 locales missing it (was only
in en/pt), restoring test_catalog_*_match_english parity. Uses the same
{current} placeholder as English.
- Update test_no_args_opens_picker_and_set to expect the explicit Cancel
escape hatch (value 'cancel') appended after the profile choices.
- Map hermes@local (Hermes Local) -> rarf via contributors/emails so the
check-attribution gate passes.
…t /new) When a user runs or picks a profile from the interactive picker, immediately reset the session so the new profile takes effect for the very next message — no manual required. Applies to both the direct subcommand path and the picker callback.
… in response - Adds immediate session reset when switching profiles via /profile set or picker - Adds get_profile_model_info() to read each profile's configured model - Response now includes: '✅ This chat now serves profile **casa** (model: **nous/tencent/hy3:free**) (live — session reset, next message runs under it.)' - Works for both direct /profile set <name> and interactive picker selection
6566170 to
f8baf6d
Compare
Summary
Adds support for interactive profile switching in the gateway via the new
/profileslash command (with picker support, parity with/model).This allows changing which profile serves a specific chat or thread at runtime, without restarting the gateway.
Motivation
Previously, to route a chat or thread to a different profile, users had to manually edit
profile_overrides.jsonor restart everything. There was no first-class, user-friendly way to do live profile changes from within the chat.What the PR does
New
/profilecommand with subcommands:/profile→ shows the current profile serving this source (chat/thread)/profile set <name>→ pins the current chat/thread to the given profile (live)/profile set(no name) → opens an interactive picker (same experience as/model)/profile clear→ removes the override for this chat/thread/profile list→ lists available profilesThe picker is available on platforms that support rich interactions (Telegram, Discord, Matrix) and falls back gracefully on others.
Thread-level overrides are supported (thread-specific overrides take precedence over chat-level).
When
gateway.multiplex_profilesis disabled (the default), plain/profilenow shows a helpful message explaining the requirement instead of only showing the current profile.Requirements
Live profile switching requires
gateway.multiplex_profiles: truein the gateway configuration.When this setting is off, the command still works for viewing the current profile and listing profiles, but switching is blocked with clear guidance on how to enable it.
Usage example
From that point on, messages in that chat will be handled by the
casaprofile (no restart needed).Additional improvements in this PR
multiplex_profilesis not enabled.Testing
Notes
Only @Teknium needs to review and merge.