fix: follow-up for salvaged #60347/#43653/#47437
- derive the compact_rows projection from SCHEMA_SQL (parse once, cache) instead of a hardcoded column list: the original #47437 list was cut against a June schema and silently dropped session_key/chat_id/chat_type/ thread_id/display_name/origin_json/expiry_finalized/git_branch/ git_repo_root/compression_failure_* — including desktop sidebar fields. Schema-derived means declaratively reconciled new columns are included automatically; only system_prompt is excluded. - guard test pinning the schema<->projection contract (mutation-verified: dropping a column from the projection fails it) - wire compact_rows=(not full) into /api/sessions and /api/profiles/sessions so the SQL projection pairs with the API-level field strip (?full=1 still returns complete rows end-to-end) - pass compact_rows at the remaining hot list callers: /api/status active count, _session_latest_descendant fallback, /api/sessions/stats by-source - thread compact_rows through the compression-tip projection (_get_session_rich_row) so projected tips can't reintroduce the blob - add pagination tests for get_messages (#60347 shipped none): paging order, offset-past-end, active-flag interaction; add tip-projection compact test - AUTHOR_MAP entries for mahdiwafy + CodeForgeNet (plain emails)
This commit is contained in:
+25
-18
@@ -2947,20 +2947,27 @@ class SessionDB:
|
||||
current = child_id
|
||||
return current
|
||||
|
||||
# All sessions columns except the large system_prompt blob, each prefixed
|
||||
# with the "s" table alias used in list_sessions_rich/_get_session_rich_row
|
||||
# queries. Used when compact_rows=True to avoid reading the blob for
|
||||
# dashboard and picker callers that only need lightweight metadata.
|
||||
_SESSION_COMPACT_COLS = (
|
||||
"s.id, s.source, s.user_id, s.model, s.model_config, "
|
||||
"s.parent_session_id, s.started_at, s.ended_at, s.end_reason, "
|
||||
"s.message_count, s.tool_call_count, s.input_tokens, s.output_tokens, "
|
||||
"s.cache_read_tokens, s.cache_write_tokens, s.reasoning_tokens, "
|
||||
"s.cwd, s.billing_provider, s.billing_base_url, s.billing_mode, "
|
||||
"s.estimated_cost_usd, s.actual_cost_usd, s.cost_status, s.cost_source, "
|
||||
"s.pricing_version, s.title, s.api_call_count, s.handoff_state, "
|
||||
"s.handoff_platform, s.handoff_error, s.rewind_count, s.archived"
|
||||
)
|
||||
# Columns excluded from compact_rows projections: only the payload-heavy
|
||||
# blob no list consumer renders. Everything else — including gateway
|
||||
# routing fields and desktop sidebar fields like git_branch — stays, and
|
||||
# the projection is derived from SCHEMA_SQL so columns added later via
|
||||
# declarative reconciliation are included automatically instead of
|
||||
# silently dropping out of list rows.
|
||||
_SESSION_COMPACT_EXCLUDED = frozenset({"system_prompt"})
|
||||
_session_compact_cols_sql: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def _compact_session_cols(cls) -> str:
|
||||
"""SELECT list for compact_rows: every ``sessions`` column declared in
|
||||
SCHEMA_SQL except the ``system_prompt`` blob, aliased with the ``s``
|
||||
prefix used by list_sessions_rich/_get_session_rich_row queries."""
|
||||
if cls._session_compact_cols_sql is None:
|
||||
declared = cls._parse_schema_columns(SCHEMA_SQL)["sessions"]
|
||||
cls._session_compact_cols_sql = ", ".join(
|
||||
f"s.{name}" for name in declared
|
||||
if name not in cls._SESSION_COMPACT_EXCLUDED
|
||||
)
|
||||
return cls._session_compact_cols_sql
|
||||
|
||||
def distinct_session_cwds(self, include_archived: bool = False) -> List[Dict[str, Any]]:
|
||||
"""Distinct non-empty session cwds with usage stats, for repo discovery.
|
||||
@@ -3160,7 +3167,7 @@ class SessionDB:
|
||||
outer_where = (
|
||||
f"{where_sql} AND {combined}" if where_sql else f"WHERE {combined}"
|
||||
)
|
||||
_sel = self._SESSION_COMPACT_COLS if compact_rows else "s.*"
|
||||
_sel = self._compact_session_cols() if compact_rows else "s.*"
|
||||
query = f"""
|
||||
WITH RECURSIVE chain(root_id, cur_id) AS (
|
||||
SELECT s.id, s.id FROM sessions s {where_sql}
|
||||
@@ -3207,7 +3214,7 @@ class SessionDB:
|
||||
# only applies to the outer select.
|
||||
params = params + params + id_params + [limit, offset]
|
||||
else:
|
||||
_sel = self._SESSION_COMPACT_COLS if compact_rows else "s.*"
|
||||
_sel = self._compact_session_cols() if compact_rows else "s.*"
|
||||
query = f"""
|
||||
SELECT {_sel},
|
||||
COALESCE(
|
||||
@@ -3260,7 +3267,7 @@ class SessionDB:
|
||||
if tip_id == s["id"]:
|
||||
projected.append(s)
|
||||
continue
|
||||
tip_row = self._get_session_rich_row(tip_id)
|
||||
tip_row = self._get_session_rich_row(tip_id, compact_rows=compact_rows)
|
||||
if not tip_row:
|
||||
projected.append(s)
|
||||
continue
|
||||
@@ -3354,7 +3361,7 @@ class SessionDB:
|
||||
Pass ``compact_rows=True`` to omit the ``system_prompt`` blob (see
|
||||
``list_sessions_rich`` for details).
|
||||
"""
|
||||
_sel = self._SESSION_COMPACT_COLS if compact_rows else "s.*"
|
||||
_sel = self._compact_session_cols() if compact_rows else "s.*"
|
||||
query = f"""
|
||||
SELECT {_sel},
|
||||
COALESCE(
|
||||
|
||||
Reference in New Issue
Block a user