mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
Merge pull request #20942 from NousResearch/austin/fix/personality
fix(tui): preserve session when switching personality
This commit is contained in:
commit
7f92e5506e
3 changed files with 55 additions and 24 deletions
|
|
@ -1863,13 +1863,15 @@ def test_config_set_personality_rejects_unknown_name(monkeypatch):
|
||||||
assert "Unknown personality" in resp["error"]["message"]
|
assert "Unknown personality" in resp["error"]["message"]
|
||||||
|
|
||||||
|
|
||||||
def test_config_set_personality_resets_history_and_returns_info(monkeypatch):
|
def test_config_set_personality_preserves_history_and_returns_info(monkeypatch):
|
||||||
|
agent = types.SimpleNamespace(
|
||||||
|
ephemeral_system_prompt=None, _cached_system_prompt="old"
|
||||||
|
)
|
||||||
session = _session(
|
session = _session(
|
||||||
agent=types.SimpleNamespace(),
|
agent=agent,
|
||||||
history=[{"role": "user", "text": "hi"}],
|
history=[{"role": "user", "text": "hi"}],
|
||||||
history_version=4,
|
history_version=4,
|
||||||
)
|
)
|
||||||
new_agent = types.SimpleNamespace(model="x")
|
|
||||||
emits = []
|
emits = []
|
||||||
|
|
||||||
server._sessions["sid"] = session
|
server._sessions["sid"] = session
|
||||||
|
|
@ -1878,13 +1880,9 @@ def test_config_set_personality_resets_history_and_returns_info(monkeypatch):
|
||||||
"_available_personalities",
|
"_available_personalities",
|
||||||
lambda cfg=None: {"helpful": "You are helpful."},
|
lambda cfg=None: {"helpful": "You are helpful."},
|
||||||
)
|
)
|
||||||
monkeypatch.setattr(
|
|
||||||
server, "_make_agent", lambda sid, key, session_id=None: new_agent
|
|
||||||
)
|
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
server, "_session_info", lambda agent: {"model": getattr(agent, "model", "?")}
|
server, "_session_info", lambda agent: {"model": getattr(agent, "model", "?")}
|
||||||
)
|
)
|
||||||
monkeypatch.setattr(server, "_restart_slash_worker", lambda session: None)
|
|
||||||
monkeypatch.setattr(server, "_emit", lambda *args: emits.append(args))
|
monkeypatch.setattr(server, "_emit", lambda *args: emits.append(args))
|
||||||
monkeypatch.setattr(server, "_write_config_key", lambda path, value: None)
|
monkeypatch.setattr(server, "_write_config_key", lambda path, value: None)
|
||||||
|
|
||||||
|
|
@ -1896,11 +1894,19 @@ def test_config_set_personality_resets_history_and_returns_info(monkeypatch):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert resp["result"]["history_reset"] is True
|
assert resp["result"]["history_reset"] is False
|
||||||
assert resp["result"]["info"] == {"model": "x"}
|
assert resp["result"]["info"] == {"model": "?"}
|
||||||
assert session["history"] == []
|
# History is preserved with a pivot marker appended
|
||||||
|
assert len(session["history"]) == 2
|
||||||
|
assert session["history"][0] == {"role": "user", "text": "hi"}
|
||||||
|
assert session["history"][1]["role"] == "user"
|
||||||
|
assert "personality" in session["history"][1]["content"].lower()
|
||||||
|
assert "You are helpful." in session["history"][1]["content"]
|
||||||
assert session["history_version"] == 5
|
assert session["history_version"] == 5
|
||||||
assert ("session.info", "sid", {"model": "x"}) in emits
|
# Agent's system prompt was updated in-place; cached prompt untouched
|
||||||
|
assert agent.ephemeral_system_prompt == "You are helpful."
|
||||||
|
assert agent._cached_system_prompt == "old"
|
||||||
|
assert ("session.info", "sid", {"model": "?"}) in emits
|
||||||
|
|
||||||
|
|
||||||
def test_session_compress_uses_compress_helper(monkeypatch):
|
def test_session_compress_uses_compress_helper(monkeypatch):
|
||||||
|
|
|
||||||
|
|
@ -1726,17 +1726,42 @@ def _validate_personality(value: str, cfg: dict | None = None) -> tuple[str, str
|
||||||
def _apply_personality_to_session(
|
def _apply_personality_to_session(
|
||||||
sid: str, session: dict, new_prompt: str
|
sid: str, session: dict, new_prompt: str
|
||||||
) -> tuple[bool, dict | None]:
|
) -> tuple[bool, dict | None]:
|
||||||
|
"""Apply a personality change to an existing session without resetting history.
|
||||||
|
|
||||||
|
Updates the agent's ephemeral system prompt in-place so the new personality
|
||||||
|
takes effect on the next turn. The cached base system prompt is left intact
|
||||||
|
(ephemeral_system_prompt is appended at API-call time, not baked into the
|
||||||
|
cache), which preserves prompt-cache hits.
|
||||||
|
|
||||||
|
Also injects a system-role marker into the conversation history so the model
|
||||||
|
knows to pivot its style from this point forward (without this, LLMs tend to
|
||||||
|
continue the tone established by earlier messages in the transcript).
|
||||||
|
|
||||||
|
Returns (history_reset, info) — history_reset is always False since we
|
||||||
|
preserve the conversation.
|
||||||
|
"""
|
||||||
if not session:
|
if not session:
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
try:
|
agent = session.get("agent")
|
||||||
info = _reset_session_agent(sid, session)
|
if agent:
|
||||||
return True, info
|
|
||||||
except Exception:
|
|
||||||
if session.get("agent"):
|
|
||||||
agent = session["agent"]
|
|
||||||
agent.ephemeral_system_prompt = new_prompt or None
|
agent.ephemeral_system_prompt = new_prompt or None
|
||||||
agent._cached_system_prompt = None
|
# Inject a pivot marker into history so the model sees the change point.
|
||||||
|
# This prevents it from pattern-matching its prior style.
|
||||||
|
if new_prompt:
|
||||||
|
marker = (
|
||||||
|
"[System: The user has changed the assistant's personality. "
|
||||||
|
"From this point forward, adopt the following persona and respond "
|
||||||
|
f"accordingly: {new_prompt}]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
marker = (
|
||||||
|
"[System: The user has cleared the personality overlay. "
|
||||||
|
"From this point forward, respond in your normal default style.]"
|
||||||
|
)
|
||||||
|
with session["history_lock"]:
|
||||||
|
session["history"].append({"role": "user", "content": marker})
|
||||||
|
session["history_version"] = int(session.get("history_version", 0)) + 1
|
||||||
info = _session_info(agent)
|
info = _session_info(agent)
|
||||||
_emit("session.info", sid, info)
|
_emit("session.info", sid, info)
|
||||||
return False, info
|
return False, info
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ export const sessionCommands: SlashCommand[] = [
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
help: 'switch or reset personality (history reset on set)',
|
help: 'switch personality for this session',
|
||||||
name: 'personality',
|
name: 'personality',
|
||||||
run: (arg, ctx) => {
|
run: (arg, ctx) => {
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue