* fix(tui): call maybe_auto_title for TUI sessions (#15961) The maybe_auto_title() helper is called from cli.py and gateway/run.py but was never wired into tui_gateway/server.py, so every session started via 'hermes --tui' landed in state.db with an empty title. Evidence from the issue reporter: 0/154 TUI sessions titled vs 91/383 CLI. Mirror the CLI/Gateway pattern: after emitting message.complete, when the turn finished cleanly, fire-and-forget title generation using the session key, user prompt, agent response, and current history. Fixes #15949. Co-authored-by: math0r-be <math0r-be@github.com> * chore(release): map math0r-be placeholder email in AUTHOR_MAP --------- Co-authored-by: math0r-be <math0r-be@github.com>
This commit is contained in:
parent
0824ba6a9d
commit
9662e3218a
@ -71,6 +71,7 @@ AUTHOR_MAP = {
|
||||
"16443023+stablegenius49@users.noreply.github.com": "stablegenius49",
|
||||
"fqsy1416@gmail.com": "EKKOLearnAI",
|
||||
"octo-patch@github.com": "octo-patch",
|
||||
"math0r-be@github.com": "math0r-be",
|
||||
"simbamax99@gmail.com": "simbam99",
|
||||
"iris@growthpillars.co": "irispillars",
|
||||
"185121704+stablegenius49@users.noreply.github.com": "stablegenius49",
|
||||
|
||||
@ -1807,3 +1807,112 @@ def test_model_options_propagates_list_exception(monkeypatch):
|
||||
assert "error" in resp
|
||||
assert resp["error"]["code"] == 5033
|
||||
assert "catalog blew up" in resp["error"]["message"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# prompt.submit — auto-title
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class _ImmediateThread:
|
||||
"""Runs the target callable synchronously so assertions can follow."""
|
||||
|
||||
def __init__(self, target=None, daemon=None):
|
||||
self._target = target
|
||||
|
||||
def start(self):
|
||||
self._target()
|
||||
|
||||
|
||||
def test_prompt_submit_auto_titles_session_on_complete(monkeypatch):
|
||||
"""maybe_auto_title is called after a successful (complete) prompt."""
|
||||
|
||||
class _Agent:
|
||||
def run_conversation(self, prompt, conversation_history=None, stream_callback=None):
|
||||
return {
|
||||
"final_response": "Rome was founded in 753 BC.",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Tell me about Rome"},
|
||||
{"role": "assistant", "content": "Rome was founded in 753 BC."},
|
||||
],
|
||||
}
|
||||
|
||||
server._sessions["sid"] = _session(agent=_Agent())
|
||||
monkeypatch.setattr(server.threading, "Thread", _ImmediateThread)
|
||||
monkeypatch.setattr(server, "_emit", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(server, "make_stream_renderer", lambda cols: None)
|
||||
monkeypatch.setattr(server, "render_message", lambda raw, cols: None)
|
||||
monkeypatch.setattr(server, "_get_db", lambda: None)
|
||||
|
||||
with patch("agent.title_generator.maybe_auto_title") as mock_title:
|
||||
server.handle_request(
|
||||
{
|
||||
"id": "1",
|
||||
"method": "prompt.submit",
|
||||
"params": {"session_id": "sid", "text": "Tell me about Rome"},
|
||||
}
|
||||
)
|
||||
|
||||
mock_title.assert_called_once()
|
||||
args = mock_title.call_args.args
|
||||
assert args[1] == "session-key"
|
||||
assert args[2] == "Tell me about Rome"
|
||||
assert args[3] == "Rome was founded in 753 BC."
|
||||
|
||||
|
||||
def test_prompt_submit_skips_auto_title_when_interrupted(monkeypatch):
|
||||
"""maybe_auto_title must NOT be called when the agent was interrupted."""
|
||||
|
||||
class _Agent:
|
||||
def run_conversation(self, prompt, conversation_history=None, stream_callback=None):
|
||||
return {
|
||||
"final_response": "partial answer",
|
||||
"interrupted": True,
|
||||
"messages": [],
|
||||
}
|
||||
|
||||
server._sessions["sid"] = _session(agent=_Agent())
|
||||
monkeypatch.setattr(server.threading, "Thread", _ImmediateThread)
|
||||
monkeypatch.setattr(server, "_emit", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(server, "make_stream_renderer", lambda cols: None)
|
||||
monkeypatch.setattr(server, "render_message", lambda raw, cols: None)
|
||||
monkeypatch.setattr(server, "_get_db", lambda: None)
|
||||
|
||||
with patch("agent.title_generator.maybe_auto_title") as mock_title:
|
||||
server.handle_request(
|
||||
{
|
||||
"id": "1",
|
||||
"method": "prompt.submit",
|
||||
"params": {"session_id": "sid", "text": "Tell me about Rome"},
|
||||
}
|
||||
)
|
||||
|
||||
mock_title.assert_not_called()
|
||||
|
||||
|
||||
def test_prompt_submit_skips_auto_title_when_response_empty(monkeypatch):
|
||||
"""maybe_auto_title must NOT be called when the agent returns an empty reply."""
|
||||
|
||||
class _Agent:
|
||||
def run_conversation(self, prompt, conversation_history=None, stream_callback=None):
|
||||
return {
|
||||
"final_response": "",
|
||||
"messages": [],
|
||||
}
|
||||
|
||||
server._sessions["sid"] = _session(agent=_Agent())
|
||||
monkeypatch.setattr(server.threading, "Thread", _ImmediateThread)
|
||||
monkeypatch.setattr(server, "_emit", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(server, "make_stream_renderer", lambda cols: None)
|
||||
monkeypatch.setattr(server, "render_message", lambda raw, cols: None)
|
||||
monkeypatch.setattr(server, "_get_db", lambda: None)
|
||||
|
||||
with patch("agent.title_generator.maybe_auto_title") as mock_title:
|
||||
server.handle_request(
|
||||
{
|
||||
"id": "1",
|
||||
"method": "prompt.submit",
|
||||
"params": {"session_id": "sid", "text": "Tell me about Rome"},
|
||||
}
|
||||
)
|
||||
|
||||
mock_title.assert_not_called()
|
||||
|
||||
@ -2321,6 +2321,26 @@ def _(rid, params: dict) -> dict:
|
||||
payload["rendered"] = rendered
|
||||
_emit("message.complete", sid, payload)
|
||||
|
||||
if (
|
||||
status == "complete"
|
||||
and isinstance(raw, str)
|
||||
and raw.strip()
|
||||
and isinstance(text, str)
|
||||
and text.strip()
|
||||
):
|
||||
try:
|
||||
from agent.title_generator import maybe_auto_title
|
||||
|
||||
maybe_auto_title(
|
||||
_get_db(),
|
||||
session.get("session_key") or sid,
|
||||
text,
|
||||
raw,
|
||||
session.get("history", []),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# CLI parity: when voice-mode TTS is on, speak the agent reply
|
||||
# (cli.py:_voice_speak_response). Only the final text — tool
|
||||
# calls / reasoning already stream separately and would be
|
||||
|
||||
Loading…
Reference in New Issue
Block a user