Merge commit 'refs/pull/6056/head' of github.com:nesquena/hermes-webui into stage/6056-delegtitles

This commit is contained in:
nesquena-hermes committed 2026-07-15 20:46:34 +00:00
commit b96405ef29
2 files changed
+250

No files matched your search

+41
View File
@@ -5256,13 +5256,16 @@ def _read_state_db_sidebar_overrides(
has_messages_table = cur.fetchone() is not None
messages_has_session_id = False
messages_has_timestamp = False
messages_has_title_fields = False
if has_messages_table:
cur.execute("PRAGMA table_info(messages)")
message_cols = {str(row[1]) for row in cur.fetchall()}
messages_has_session_id = 'session_id' in message_cols
messages_has_timestamp = 'timestamp' in message_cols
messages_has_title_fields = {'session_id', 'role', 'content', 'timestamp'}.issubset(message_cols)
overrides: dict[str, dict] = {}
delegated_title_ids: set[str] = set()
ids = list(wanted)
chunk_size = 500
for i in range(0, len(ids), chunk_size):
@@ -5283,6 +5286,12 @@ def _read_state_db_sidebar_overrides(
if state_title:
entry['_state_db_title'] = state_title
state_source = str(row['source'] or '').strip().lower()
if (
state_source == 'subagent'
and sid in count_wanted
and ' '.join(state_title.split()) == 'Subagent Session'
):
delegated_title_ids.add(sid)
if state_source:
entry['_state_db_source'] = state_source
source_meta = normalize_agent_session_source(state_source)
@@ -5327,6 +5336,29 @@ def _read_state_db_sidebar_overrides(
entry['_state_db_last_message_at'] = float(row['last_message_at'] or 0)
except (TypeError, ValueError):
pass
if messages_has_title_fields and delegated_title_ids:
seen_user_messages: set[str] = set()
delegated_title_ids = list(delegated_title_ids)
for i in range(0, len(delegated_title_ids), chunk_size):
chunk = delegated_title_ids[i:i + chunk_size]
placeholders = ','.join('?' * len(chunk))
cur.execute(
f"""
SELECT session_id, role, content, timestamp
FROM messages
WHERE session_id IN ({placeholders}) AND role = 'user'
ORDER BY session_id, timestamp ASC
""",
chunk,
)
for row in cur.fetchall():
sid = str(row['session_id'])
if sid in seen_user_messages:
continue
display_title = title_from([dict(row)], fallback='')
if display_title:
seen_user_messages.add(sid)
overrides.setdefault(sid, {})['_state_db_display_title'] = display_title
return overrides
except Exception:
return {}
@@ -5381,6 +5413,7 @@ def _apply_sidebar_state_db_override_metadata(sessions: list[dict], metadata: di
state_db_source_label = entry.pop('_state_db_source_label', None)
state_db_message_count = entry.pop('_state_db_message_count', None)
state_db_last_message_at = entry.pop('_state_db_last_message_at', None)
state_db_display_title = entry.pop('_state_db_display_title', None)
if state_db_source == 'webui':
session['source_tag'] = state_db_source_tag
session['raw_source'] = state_db_raw_source
@@ -5440,6 +5473,12 @@ def _apply_sidebar_state_db_override_metadata(sessions: list[dict], metadata: di
):
session['_state_db_title'] = state_db_title
session['display_title'] = state_db_title
if (
state_db_display_title
and state_db_source == 'subagent'
and ' '.join(str(title or '').split()) == 'Subagent Session'
):
session['display_title'] = state_db_display_title
def _enrich_sidebar_lineage_metadata(sessions: list[dict]) -> None:
@@ -5711,6 +5750,8 @@ def title_from(messages, fallback: str='Untitled'):
for m in messages:
if m.get('role') == 'user':
c = m.get('content', '')
if c is None:
continue
if isinstance(c, list):
c = ' '.join(p.get('text', '') for p in c if isinstance(p, dict) and p.get('type') == 'text')
text = _strip_attached_files_marker(str(c))
+209
View File
@@ -58,6 +58,27 @@ def _ensure_state_db(path):
return conn
def _ensure_messages_table(conn):
conn.execute(
"""
CREATE TABLE messages (
session_id TEXT,
role TEXT,
content TEXT,
timestamp REAL
)
"""
)
def _insert_state_message(conn, sid, *, role, content, timestamp):
conn.execute(
"INSERT INTO messages (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)",
(sid, role, content, timestamp),
)
conn.commit()
def _insert_state_row(conn, sid, *, title=None, parent=None, ended_at=None, end_reason=None, started_at=None, source='webui', session_source=None):
conn.execute(
"""
@@ -391,6 +412,194 @@ def test_generic_webui_title_gets_read_only_state_db_display_title(_isolate):
conn.close()
def test_generic_subagent_title_gets_goal_display_title(_isolate):
conn = _ensure_state_db(_isolate)
_ensure_messages_table(conn)
t0 = time.time() - 100
try:
_save_webui_session("lineage_api_subagent_goal", title="Subagent Session", updated_at=t0)
_insert_state_row(
conn,
"lineage_api_subagent_goal",
title="Subagent Session",
source="subagent",
started_at=t0,
)
_insert_state_message(
conn,
"lineage_api_subagent_goal",
role="user",
content="Find the root cause of the failing sidebar test",
timestamp=t0 + 1,
)
row = {row["session_id"]: row for row in all_sessions(include_lineage_metadata=False)}["lineage_api_subagent_goal"]
assert row["title"] == "Subagent Session"
assert row["display_title"] == "Find the root cause of the failing sidebar test"
finally:
conn.close()
def test_custom_subagent_title_stays_authoritative(_isolate):
conn = _ensure_state_db(_isolate)
_ensure_messages_table(conn)
t0 = time.time() - 100
try:
_save_webui_session("lineage_api_subagent_custom", title="Investigate auth", updated_at=t0)
_insert_state_row(
conn,
"lineage_api_subagent_custom",
title="Investigate auth",
source="subagent",
started_at=t0,
)
_insert_state_message(
conn,
"lineage_api_subagent_custom",
role="user",
content="A different goal",
timestamp=t0 + 1,
)
row = {row["session_id"]: row for row in all_sessions(include_lineage_metadata=False)}["lineage_api_subagent_custom"]
assert row["title"] == "Investigate auth"
assert "display_title" not in row
finally:
conn.close()
def test_generic_subagent_title_falls_back_without_first_user_message(_isolate):
conn = _ensure_state_db(_isolate)
_ensure_messages_table(conn)
t0 = time.time() - 100
try:
_save_webui_session("lineage_api_subagent_empty", title="Subagent Session", updated_at=t0)
_insert_state_row(
conn,
"lineage_api_subagent_empty",
title="Subagent Session",
source="subagent",
started_at=t0,
)
_insert_state_message(
conn,
"lineage_api_subagent_empty",
role="assistant",
content="Only assistant output",
timestamp=t0 + 1,
)
row = {row["session_id"]: row for row in all_sessions(include_lineage_metadata=False)}["lineage_api_subagent_empty"]
assert row["title"] == "Subagent Session"
assert "display_title" not in row
finally:
conn.close()
def test_generic_subagent_title_skips_null_first_user_message(_isolate):
conn = _ensure_state_db(_isolate)
_ensure_messages_table(conn)
t0 = time.time() - 100
try:
_save_webui_session("lineage_api_subagent_null_first", title="Subagent Session", updated_at=t0)
_insert_state_row(
conn,
"lineage_api_subagent_null_first",
title="Subagent Session",
source="subagent",
started_at=t0,
)
_insert_state_message(
conn,
"lineage_api_subagent_null_first",
role="user",
content=None,
timestamp=t0 + 1,
)
_insert_state_message(
conn,
"lineage_api_subagent_null_first",
role="user",
content="Recover the next usable delegated title",
timestamp=t0 + 2,
)
row = {row["session_id"]: row for row in all_sessions(include_lineage_metadata=False)}["lineage_api_subagent_null_first"]
assert row["title"] == "Subagent Session"
assert row["display_title"] == "Recover the next usable delegated title"
finally:
conn.close()
def test_generic_subagent_title_respects_sidebar_override_cap(_isolate, monkeypatch):
conn = _ensure_state_db(_isolate)
_ensure_messages_table(conn)
older = time.time() - 200
newer = time.time() - 100
try:
monkeypatch.setenv("HERMES_WEBUI_STATE_DB_OVERRIDE_TOP_N", "1")
_save_webui_session("lineage_api_subagent_old", title="Subagent Session", updated_at=older)
_save_webui_session("lineage_api_subagent_new", title="Subagent Session", updated_at=newer)
_insert_state_row(
conn,
"lineage_api_subagent_old",
title="Subagent Session",
source="subagent",
started_at=older,
)
_insert_state_row(
conn,
"lineage_api_subagent_new",
title="Subagent Session",
source="subagent",
started_at=newer,
)
_insert_state_message(
conn,
"lineage_api_subagent_old",
role="user",
content="Older delegated title",
timestamp=older + 1,
)
_insert_state_message(
conn,
"lineage_api_subagent_new",
role="user",
content="Newest delegated title",
timestamp=newer + 1,
)
rows = {row["session_id"]: row for row in all_sessions(include_lineage_metadata=False)}
assert rows["lineage_api_subagent_new"]["display_title"] == "Newest delegated title"
assert "display_title" not in rows["lineage_api_subagent_old"]
finally:
conn.close()
def test_generic_subagent_title_falls_back_without_messages_table(_isolate):
conn = _ensure_state_db(_isolate)
t0 = time.time() - 100
try:
_save_webui_session("lineage_api_subagent_no_messages", title="Subagent Session", updated_at=t0)
_insert_state_row(
conn,
"lineage_api_subagent_no_messages",
title="Subagent Session",
source="subagent",
started_at=t0,
)
row = {row["session_id"]: row for row in all_sessions(include_lineage_metadata=False)}["lineage_api_subagent_no_messages"]
assert row["title"] == "Subagent Session"
assert "display_title" not in row
finally:
conn.close()
def test_state_db_display_title_does_not_override_custom_json_title(_isolate):
"""Manual/custom JSON titles stay authoritative even when state.db differs."""
conn = _ensure_state_db(_isolate)