refactor(hermes_state): AST-neutral line packing across state mixins
This commit is contained in:
@@ -115,8 +115,7 @@ class SessionPortabilityMixin:
|
||||
if not include_archived:
|
||||
where += " AND archived = 0"
|
||||
rows = self._locked_rows(
|
||||
"SELECT cwd AS cwd, COUNT(*) AS sessions, "
|
||||
"MAX(COALESCE(ended_at, started_at, 0)) AS last_active "
|
||||
"SELECT cwd AS cwd, COUNT(*) AS sessions, MAX(COALESCE(ended_at, started_at, 0)) AS last_active "
|
||||
f"FROM sessions WHERE {where} GROUP BY cwd"
|
||||
)
|
||||
return [
|
||||
@@ -267,8 +266,7 @@ class SessionPortabilityMixin:
|
||||
adopted = result.get("ok", False) and (imported + skipped) == len(segments)
|
||||
if not adopted:
|
||||
logger.warning(
|
||||
"adoption of %s did not complete: imported=%s skipped=%s "
|
||||
"of %s segment(s); errors=%s",
|
||||
"adoption of %s did not complete: imported=%s skipped=%s of %s segment(s); errors=%s",
|
||||
session_id, imported, skipped, len(segments), result.get("errors"),
|
||||
)
|
||||
|
||||
@@ -291,8 +289,7 @@ class SessionPortabilityMixin:
|
||||
local_now = len(self.get_messages(seg_id))
|
||||
if donor_now > local_now:
|
||||
logger.warning(
|
||||
"adoption divergence at retire time: donor "
|
||||
"segment %s grew to %d messages (local %d) — "
|
||||
"adoption divergence at retire time: donor segment %s grew to %d messages (local %d) — "
|
||||
"leaving donor unretired",
|
||||
seg_id, donor_now, local_now,
|
||||
)
|
||||
|
||||
+14
-29
@@ -113,8 +113,7 @@ _SESSION_MODEL_USAGE_V20_SEED_SQL = """INSERT OR IGNORE INTO session_model_usage
|
||||
+ COALESCE(cache_write_tokens, 0)
|
||||
+ COALESCE(reasoning_tokens, 0) > 0"""
|
||||
_TITLE_UNIQUE_INDEX_SQL = (
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_title_unique "
|
||||
"ON sessions(title) WHERE title IS NOT NULL"
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_title_unique ON sessions(title) WHERE title IS NOT NULL"
|
||||
)
|
||||
_STALE_KEY_UPSERT_SQL = (
|
||||
"INSERT INTO state_meta (key, value) VALUES (?, '1') "
|
||||
@@ -203,8 +202,7 @@ class SessionSchemaMixin:
|
||||
return 0 # "name IN ()" is a SQLite syntax error
|
||||
placeholders = ",".join("?" for _ in names)
|
||||
row = cursor.execute(
|
||||
f"SELECT COUNT(*) FROM sqlite_master "
|
||||
f"WHERE type = 'trigger' AND name IN ({placeholders})",
|
||||
f"SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name IN ({placeholders})",
|
||||
tuple(names),
|
||||
).fetchone()
|
||||
return int(row[0])
|
||||
@@ -234,8 +232,7 @@ class SessionSchemaMixin:
|
||||
update_names += ("messages_fts_cjk_update",)
|
||||
placeholders = ", ".join("?" for _ in update_names)
|
||||
rows = cursor.execute(
|
||||
"SELECT name, sql FROM sqlite_master "
|
||||
f"WHERE type = 'trigger' AND name IN ({placeholders})",
|
||||
f"SELECT name, sql FROM sqlite_master WHERE type = 'trigger' AND name IN ({placeholders})",
|
||||
update_names,
|
||||
).fetchall()
|
||||
to_drop = [name for name, sql in rows if self._fts_update_trigger_needs_narrowing(sql)]
|
||||
@@ -266,16 +263,14 @@ class SessionSchemaMixin:
|
||||
"UPDATE OF migration; marked stale and unavailable"
|
||||
)
|
||||
logger.info(
|
||||
"Migrated %d broad FTS UPDATE trigger(s) to AFTER UPDATE OF " "(no rebuild required)",
|
||||
len(to_drop),
|
||||
"Migrated %d broad FTS UPDATE trigger(s) to AFTER UPDATE OF (no rebuild required)", len(to_drop),
|
||||
)
|
||||
return len(to_drop)
|
||||
|
||||
def _cjk_update_trigger_is_narrowed(self, cursor: sqlite3.Cursor) -> bool:
|
||||
"""True when messages_fts_cjk_update exists with AFTER UPDATE OF."""
|
||||
row = cursor.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = ?",
|
||||
("messages_fts_cjk_update",),
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = ?", ("messages_fts_cjk_update",),
|
||||
).fetchone()
|
||||
return bool(row) and not self._fts_update_trigger_needs_narrowing(row[0])
|
||||
|
||||
@@ -427,8 +422,7 @@ class SessionSchemaMixin:
|
||||
if not admitted:
|
||||
logger.warning(
|
||||
"Deferred stale state.db FTS rebuild: another process "
|
||||
"holds the rebuild authority; canonical writes and LIKE "
|
||||
"search remain available."
|
||||
"holds the rebuild authority; canonical writes and LIKE search remain available."
|
||||
)
|
||||
return False
|
||||
return self._recover_stale_fts_locked(cursor, legacy=legacy)
|
||||
@@ -472,8 +466,7 @@ class SessionSchemaMixin:
|
||||
return recovered
|
||||
except Exception: # noqa: BLE001 - background retry must never raise
|
||||
logger.warning(
|
||||
"In-process retry of the deferred stale state.db FTS rebuild "
|
||||
"failed; will retry later.",
|
||||
"In-process retry of the deferred stale state.db FTS rebuild failed; will retry later.",
|
||||
exc_info=True,
|
||||
)
|
||||
return False
|
||||
@@ -693,8 +686,7 @@ class SessionSchemaMixin:
|
||||
if pk_cols is None or pk_cols == ["scope", "session_key"]:
|
||||
return
|
||||
logger.info(
|
||||
"gateway_routing has legacy primary key %r; rebuilding with "
|
||||
"composite (scope, session_key) key",
|
||||
"gateway_routing has legacy primary key %r; rebuilding with composite (scope, session_key) key",
|
||||
pk_cols,
|
||||
)
|
||||
self._rebuild_table(
|
||||
@@ -706,8 +698,7 @@ class SessionSchemaMixin:
|
||||
updated_at REAL NOT NULL,
|
||||
PRIMARY KEY (scope, session_key)
|
||||
)""",
|
||||
"INSERT OR REPLACE INTO gateway_routing "
|
||||
"(scope, session_key, entry_json, updated_at) "
|
||||
"INSERT OR REPLACE INTO gateway_routing (scope, session_key, entry_json, updated_at) "
|
||||
"SELECT COALESCE(scope, ''), session_key, entry_json, updated_at "
|
||||
"FROM gateway_routing_legacy_pk ORDER BY updated_at ASC",
|
||||
)
|
||||
@@ -786,8 +777,7 @@ class SessionSchemaMixin:
|
||||
try:
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_messages_platform_msg_id "
|
||||
"ON messages(session_id, platform_message_id) "
|
||||
"WHERE platform_message_id IS NOT NULL"
|
||||
"ON messages(session_id, platform_message_id) WHERE platform_message_id IS NOT NULL"
|
||||
)
|
||||
except sqlite3.OperationalError as exc:
|
||||
logger.debug("idx_messages_platform_msg_id create skipped: %s", exc)
|
||||
@@ -804,8 +794,7 @@ class SessionSchemaMixin:
|
||||
|
||||
fts5_available = self._sqlite_supports_fts5(cursor)
|
||||
self._fts_stale = cursor.execute(
|
||||
"SELECT 1 FROM state_meta WHERE key = ? LIMIT 1",
|
||||
(FTS_STALE_KEY,),
|
||||
"SELECT 1 FROM state_meta WHERE key = ? LIMIT 1", (FTS_STALE_KEY,)
|
||||
).fetchone() is not None
|
||||
if self._fts_stale:
|
||||
# A prior process detached FTS after corruption; keep every FTS writer
|
||||
@@ -859,9 +848,7 @@ class SessionSchemaMixin:
|
||||
"WHERE parent_session_id IS NULL "
|
||||
"AND json_extract(COALESCE(model_config, '{}'), '$._delegate_from') IS NULL "
|
||||
"AND json_extract(COALESCE(model_config, '{}'), '$._branched_from') IS NULL "
|
||||
"AND title IS NULL "
|
||||
"AND message_count <= 25 "
|
||||
"AND EXISTS (SELECT 1 FROM messages m "
|
||||
"AND title IS NULL AND message_count <= 25 AND EXISTS (SELECT 1 FROM messages m "
|
||||
" WHERE m.session_id = sessions.id AND m.role = 'tool') "
|
||||
"AND NOT EXISTS (SELECT 1 FROM sessions ch "
|
||||
" WHERE ch.parent_session_id = sessions.id)"
|
||||
@@ -926,8 +913,7 @@ class SessionSchemaMixin:
|
||||
existing rows are main-loop accounting → task=''."""
|
||||
try:
|
||||
legacy_pk = cursor.execute(
|
||||
"SELECT COUNT(*) FROM pragma_table_info('session_model_usage') "
|
||||
"WHERE name = 'task' AND pk > 0"
|
||||
"SELECT COUNT(*) FROM pragma_table_info('session_model_usage') WHERE name = 'task' AND pk > 0"
|
||||
).fetchone()[0]
|
||||
if legacy_pk:
|
||||
return
|
||||
@@ -970,8 +956,7 @@ class SessionSchemaMixin:
|
||||
)"""
|
||||
)
|
||||
logger.warning(
|
||||
"Cleared %d duplicate session title(s) while restoring the unique index",
|
||||
cursor.rowcount,
|
||||
"Cleared %d duplicate session title(s) while restoring the unique index", cursor.rowcount,
|
||||
)
|
||||
cursor.execute(_TITLE_UNIQUE_INDEX_SQL)
|
||||
except sqlite3.Error:
|
||||
|
||||
+16
-23
@@ -35,8 +35,7 @@ _LIKE_TOKEN_RE = re.compile(r'"[^"]+"|\S+')
|
||||
_SEARCH_SELECT_TAIL = "m.timestamp, m.tool_name, s.source, s.model, s.started_at AS session_started"
|
||||
_LIKE_SNIPPET_SQL = "substr(m.content, max(1, instr(m.content, ?) - 40), 120) AS snippet"
|
||||
_LIKE_ANY_COLUMN_SQL = (
|
||||
"(m.content LIKE ? ESCAPE '\\' OR m.tool_name LIKE ? ESCAPE '\\' "
|
||||
"OR m.tool_calls LIKE ? ESCAPE '\\')"
|
||||
"(m.content LIKE ? ESCAPE '\\' OR m.tool_name LIKE ? ESCAPE '\\' OR m.tool_calls LIKE ? ESCAPE '\\')"
|
||||
)
|
||||
_LIKE_COALESCED_COLUMN_SQL = (
|
||||
"(COALESCE(m.content, '') LIKE ? ESCAPE '\\' OR "
|
||||
@@ -227,15 +226,13 @@ class SessionSearchMixin:
|
||||
# per indexed doc, so the anti-join is exact.
|
||||
_BOUNDARY_SWEEP_SQL = (
|
||||
"INSERT INTO {table}(rowid, content, tool_name, tool_calls) "
|
||||
"SELECT m.id, m.content, m.tool_name, m.tool_calls "
|
||||
"FROM messages m "
|
||||
"SELECT m.id, m.content, m.tool_name, m.tool_calls FROM messages m "
|
||||
"WHERE m.id > ? AND m.id <= ? {extra}"
|
||||
"AND NOT EXISTS (SELECT 1 FROM {table}_docsize d WHERE d.id = m.id)"
|
||||
)
|
||||
_CHUNK_INSERT_SQL = (
|
||||
"INSERT INTO {table}(rowid, content, tool_name, tool_calls) "
|
||||
"SELECT id, content, tool_name, tool_calls FROM messages "
|
||||
"WHERE id > ? AND id <= ?{extra}"
|
||||
"SELECT id, content, tool_name, tool_calls FROM messages WHERE id > ? AND id <= ?{extra}"
|
||||
)
|
||||
|
||||
def _fts_rebuild_finish(self) -> None:
|
||||
@@ -496,19 +493,17 @@ class SessionSearchMixin:
|
||||
return False
|
||||
with self._lock:
|
||||
conn = self._conn
|
||||
if self._db_has_legacy_inline_fts(conn):
|
||||
return True
|
||||
if _meta_row(conn, "fts_rebuild_high_water") is not None:
|
||||
return True # interrupted optimize: demoted but unfinished
|
||||
# CJK work is only offerable when THIS process can tokenize.
|
||||
if self._fts_cjk_loaded and (
|
||||
_meta_row(conn, "fts_cjk_rebuild_high_water") is not None
|
||||
or _meta_row(conn, FTS_CJK_STALE_KEY) is not None
|
||||
):
|
||||
return True
|
||||
if self._has_fts_trash(conn):
|
||||
return True
|
||||
return self._fts_external_index_empty_with_messages(conn)
|
||||
return (
|
||||
self._db_has_legacy_inline_fts(conn)
|
||||
or _meta_row(conn, "fts_rebuild_high_water") is not None # interrupted optimize
|
||||
# CJK work is only offerable when THIS process can tokenize.
|
||||
or (self._fts_cjk_loaded and (
|
||||
_meta_row(conn, "fts_cjk_rebuild_high_water") is not None
|
||||
or _meta_row(conn, FTS_CJK_STALE_KEY) is not None
|
||||
))
|
||||
or self._has_fts_trash(conn)
|
||||
or self._fts_external_index_empty_with_messages(conn)
|
||||
)
|
||||
|
||||
def _demote_legacy_fts_to_trash(self) -> int:
|
||||
"""Demote the legacy inline FTS vtables and stage their shadow tables for chunked
|
||||
@@ -608,8 +603,7 @@ class SessionSearchMixin:
|
||||
self.set_meta("fts_storage_version", str(FTS_STORAGE_VERSION), cursor=conn)
|
||||
_delete_meta(conn, "fts_optimize_available")
|
||||
conn.execute(
|
||||
"UPDATE schema_version SET version = ? WHERE version < ?",
|
||||
(SCHEMA_VERSION, SCHEMA_VERSION),
|
||||
"UPDATE schema_version SET version = ? WHERE version < ?", (SCHEMA_VERSION, SCHEMA_VERSION),
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -768,8 +762,7 @@ class SessionSearchMixin:
|
||||
fetch_limit = int(limit) * 2 + 5
|
||||
with self._lock:
|
||||
rows = self._conn.execute(
|
||||
"SELECT id, timestamp, content FROM messages "
|
||||
"WHERE session_id = ? AND role = 'user'"
|
||||
"SELECT id, timestamp, content FROM messages WHERE session_id = ? AND role = 'user'"
|
||||
f"{active_clause}{display_clause} "
|
||||
"ORDER BY id DESC LIMIT ?",
|
||||
(session_id, fetch_limit),
|
||||
|
||||
@@ -206,8 +206,7 @@ class SessionTelegramTopicsMixin:
|
||||
)
|
||||
if clear_bindings:
|
||||
conn.execute(
|
||||
"DELETE FROM telegram_dm_topic_bindings "
|
||||
"WHERE profile_name = ? AND chat_id = ?",
|
||||
"DELETE FROM telegram_dm_topic_bindings WHERE profile_name = ? AND chat_id = ?",
|
||||
(profile_name, str(chat_id)),
|
||||
)
|
||||
except sqlite3.OperationalError:
|
||||
@@ -248,8 +247,7 @@ class SessionTelegramTopicsMixin:
|
||||
"""All bindings for one chat, newest first ([] when the table is absent)."""
|
||||
profile_name = _normalize_telegram_topic_profile_name(profile_name)
|
||||
rows = self._topic_read_all(
|
||||
"SELECT * FROM telegram_dm_topic_bindings "
|
||||
"WHERE profile_name = ? AND chat_id = ? "
|
||||
"SELECT * FROM telegram_dm_topic_bindings WHERE profile_name = ? AND chat_id = ? "
|
||||
"ORDER BY updated_at DESC",
|
||||
(profile_name, str(chat_id)),
|
||||
)
|
||||
@@ -306,8 +304,7 @@ class SessionTelegramTopicsMixin:
|
||||
).fetchone()
|
||||
if remaining is None:
|
||||
conn.execute(
|
||||
"UPDATE telegram_dm_topic_mode "
|
||||
"SET enabled = 0, updated_at = ? "
|
||||
"UPDATE telegram_dm_topic_mode SET enabled = 0, updated_at = ? "
|
||||
"WHERE profile_name = ? AND chat_id = ?",
|
||||
(time.time(), profile_name, chat_id),
|
||||
)
|
||||
@@ -392,7 +389,6 @@ class SessionTelegramTopicsMixin:
|
||||
).fetchall()
|
||||
except sqlite3.OperationalError:
|
||||
rows = conn.execute(
|
||||
_UNLINKED_SELECT_HEAD + _UNLINKED_SELECT_TAIL,
|
||||
(str(user_id), int(limit)),
|
||||
_UNLINKED_SELECT_HEAD + _UNLINKED_SELECT_TAIL, (str(user_id), int(limit)),
|
||||
).fetchall()
|
||||
return [self._rich_row(row) for row in rows]
|
||||
|
||||
@@ -161,8 +161,7 @@ class SessionTitlesMixin:
|
||||
if source not in self._TITLE_SOURCE_RANK:
|
||||
raise ValueError(f"invalid title source: {source!r}")
|
||||
return self._write_rowcount(
|
||||
"UPDATE sessions SET title_source = ? WHERE id = ? AND title IS NOT NULL",
|
||||
(source, session_id),
|
||||
"UPDATE sessions SET title_source = ? WHERE id = ? AND title IS NOT NULL", (source, session_id)
|
||||
) > 0
|
||||
|
||||
def get_session_by_title(self, title: str) -> Optional[Dict[str, Any]]:
|
||||
|
||||
Reference in New Issue
Block a user