mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 15:55:43 +00:00
fix(session): persist profile_name and route batch key by profile
Two follow-ups observed after deploying profile routing:
1. sessions.profile_name was NULL even when the agent ran inside the
routed profile scope. _insert_session_row never wrote it,
get_or_create_session / reset_session never passed it through, and
the agent-side _ensure_db_session fallback had no way to read it.
- Declare profile_name TEXT in SCHEMA_SQL so _reconcile_columns
auto-adds it on existing DBs.
- _insert_session_row takes profile_name and writes it.
- SessionStore passes source.profile (or old_entry.origin.profile
on reset) into db_create_kwargs.
- _ensure_db_session reads the active profile via
get_active_profile_name() inside _profile_runtime_scope.
2. DiscordAdapter._text_batch_key called build_session_key without
profile=, so the batch key always landed in agent:main even when
the routed profile differed — diverging from the agent session
key namespace (agent:crypto-trader, agent:ai-expert, ...).
Pass event.source.profile through so both namespaces agree.
Live verification (jth-server-2, 2026-06-28): a test message in a
routed #coin thread produced agent:crypto-trader:discord🧵...
in the batch log and profile_name=crypto-trader in the sessions
row. Default-routed chat still produced agent:main / NULL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d7993ab178
commit
e8b7ce8c19
4 changed files with 26 additions and 4 deletions
|
|
@ -1993,6 +1993,7 @@ class SessionStore:
|
|||
"chat_id": source.chat_id,
|
||||
"chat_type": source.chat_type,
|
||||
"thread_id": source.thread_id,
|
||||
"profile_name": source.profile,
|
||||
}
|
||||
|
||||
if _needs_save:
|
||||
|
|
@ -2273,6 +2274,7 @@ class SessionStore:
|
|||
"chat_id": old_entry.origin.chat_id if old_entry.origin else None,
|
||||
"chat_type": old_entry.origin.chat_type if old_entry.origin else None,
|
||||
"thread_id": old_entry.origin.thread_id if old_entry.origin else None,
|
||||
"profile_name": old_entry.origin.profile if old_entry.origin else None,
|
||||
}
|
||||
|
||||
if self._db and db_end_session_id:
|
||||
|
|
|
|||
|
|
@ -791,6 +791,7 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|||
compression_failure_cooldown_until REAL,
|
||||
compression_failure_error TEXT,
|
||||
compression_fallback_streak INTEGER NOT NULL DEFAULT 0,
|
||||
profile_name TEXT,
|
||||
rewind_count INTEGER NOT NULL DEFAULT 0,
|
||||
archived INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (parent_session_id) REFERENCES sessions(id)
|
||||
|
|
@ -1759,6 +1760,7 @@ class SessionDB:
|
|||
thread_id: str = None,
|
||||
parent_session_id: str = None,
|
||||
cwd: str = None,
|
||||
profile_name: str = None,
|
||||
) -> None:
|
||||
"""Insert a session row, enriching NULL metadata on conflict.
|
||||
|
||||
|
|
@ -1782,9 +1784,9 @@ class SessionDB:
|
|||
conn.execute(
|
||||
"""INSERT INTO sessions (
|
||||
id, source, user_id, session_key, chat_id, chat_type, thread_id,
|
||||
model, model_config, system_prompt, parent_session_id, cwd, started_at
|
||||
model, model_config, system_prompt, parent_session_id, cwd, profile_name, started_at
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
model = COALESCE(sessions.model, excluded.model),
|
||||
model_config = COALESCE(sessions.model_config, excluded.model_config),
|
||||
|
|
@ -1794,7 +1796,8 @@ class SessionDB:
|
|||
chat_type = COALESCE(sessions.chat_type, excluded.chat_type),
|
||||
thread_id = COALESCE(sessions.thread_id, excluded.thread_id),
|
||||
parent_session_id = COALESCE(sessions.parent_session_id, excluded.parent_session_id),
|
||||
cwd = COALESCE(sessions.cwd, excluded.cwd)""",
|
||||
cwd = COALESCE(sessions.cwd, excluded.cwd),
|
||||
profile_name = COALESCE(sessions.profile_name, excluded.profile_name)""",
|
||||
(
|
||||
session_id,
|
||||
source,
|
||||
|
|
@ -1808,6 +1811,7 @@ class SessionDB:
|
|||
system_prompt,
|
||||
parent_session_id,
|
||||
cwd,
|
||||
profile_name,
|
||||
time.time(),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6612,12 +6612,20 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
# ------------------------------------------------------------------
|
||||
|
||||
def _text_batch_key(self, event: MessageEvent) -> str:
|
||||
"""Session-scoped key for text message batching."""
|
||||
"""Session-scoped key for text message batching.
|
||||
|
||||
Passes ``event.source.profile`` through so routed messages batch
|
||||
under the same namespace the agent run will use (e.g.
|
||||
``agent:crypto-trader`` instead of ``agent:main``). Without this,
|
||||
the batch key would always land in ``agent:main`` even when the
|
||||
routed profile differs.
|
||||
"""
|
||||
from gateway.session import build_session_key
|
||||
return build_session_key(
|
||||
event.source,
|
||||
group_sessions_per_user=self.config.extra.get("group_sessions_per_user", True),
|
||||
thread_sessions_per_user=self.config.extra.get("thread_sessions_per_user", False),
|
||||
profile=event.source.profile,
|
||||
)
|
||||
|
||||
def _enqueue_text_event(self, event: MessageEvent) -> None:
|
||||
|
|
|
|||
|
|
@ -599,6 +599,13 @@ class AIAgent:
|
|||
return
|
||||
source = _session_source_for_agent(self.platform)
|
||||
try:
|
||||
try:
|
||||
from hermes_cli.profiles import get_active_profile_name
|
||||
_profile_for_session = get_active_profile_name()
|
||||
if _profile_for_session == "default":
|
||||
_profile_for_session = None
|
||||
except Exception:
|
||||
_profile_for_session = None
|
||||
self._session_db.create_session(
|
||||
session_id=self.session_id,
|
||||
source=source,
|
||||
|
|
@ -608,6 +615,7 @@ class AIAgent:
|
|||
user_id=None,
|
||||
parent_session_id=self._parent_session_id,
|
||||
cwd=_launch_cwd_for_session(source),
|
||||
profile_name=_profile_for_session,
|
||||
)
|
||||
self._session_db_created = True
|
||||
except Exception as e:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue