Skip to content

Commit 3789375

Browse files
fix(#125): address bradjin8 tab nocache, ctx enrichment, and tests
1 parent f2c7663 commit 3789375

3 files changed

Lines changed: 45 additions & 16 deletions

File tree

api/workspaces.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ def get_workspace_tabs(workspace_id: str) -> tuple[Response, int] | Response:
167167
workspace_id: Storage folder name, ``global`` for unassigned chats, or
168168
``cli:<project_id>``.
169169
summary: When ``1`` or ``true``, return lightweight tab headers only.
170-
nocache: When ``1`` or ``true``, bypass cache on summary requests.
170+
nocache: When ``1`` or ``true``, bypass cache on summary and full-tab
171+
requests (alias disk cache on per-tab lazy load).
171172
172173
Returns:
173174
Tabs payload from :func:`services.workspace_tabs` helpers (typically
@@ -190,7 +191,9 @@ def get_workspace_tabs(workspace_id: str) -> tuple[Response, int] | Response:
190191
workspace_id, workspace_path, rules, nocache=_request_nocache(),
191192
)
192193
else:
193-
payload, status = assemble_workspace_tabs(workspace_id, workspace_path, rules)
194+
payload, status = assemble_workspace_tabs(
195+
workspace_id, workspace_path, rules, nocache=_request_nocache(),
196+
)
194197
return json_response(payload, status)
195198
except Exception:
196199
_logger.exception("Failed to get workspace tabs")
@@ -209,6 +212,7 @@ def get_workspace_tab(workspace_id: str, composer_id: str) -> tuple[Response, in
209212
workspace_id: Storage folder name, ``global`` for unassigned chats, or
210213
``cli:<project_id>`` (CLI workspaces return 400).
211214
composer_id: Composer UUID to load.
215+
nocache: When ``1`` or ``true``, bypass alias disk cache.
212216
213217
Returns:
214218
Single-tab JSON from :func:`services.workspace_tabs.assemble_single_tab`
@@ -221,7 +225,13 @@ def get_workspace_tab(workspace_id: str, composer_id: str) -> tuple[Response, in
221225
try:
222226
workspace_path = resolve_workspace_path()
223227
rules = exclusion_rules()
224-
payload, status = assemble_single_tab(workspace_id, composer_id, workspace_path, rules)
228+
payload, status = assemble_single_tab(
229+
workspace_id,
230+
composer_id,
231+
workspace_path,
232+
rules,
233+
nocache=_request_nocache(),
234+
)
225235
return json_response(payload, status)
226236
except Exception:
227237
_logger.exception("Failed to get workspace tab")

services/workspace_tabs.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
set_cached_tab_summaries,
5252
)
5353
from services.workspace_context import (
54-
resolve_invalid_workspace_aliases_cached,
5554
resolve_workspace_context_cached,
55+
with_invalid_workspace_aliases,
5656
)
5757
from services.workspace_db import (
5858
COMPOSER_ROWS_WITH_HEADERS_SQL,
@@ -453,16 +453,19 @@ def _build_workspace_tab_summaries_uncached(
453453

454454
project_layouts_map = load_project_layouts_map(global_db)
455455

456+
# Full composerData roster still required for the summary tab loop;
457+
# alias inference alone is fingerprint-cached across requests.
456458
composer_rows = safe_fetchall(global_db, COMPOSER_ROWS_WITH_HEADERS_SQL)
457459

458-
invalid_workspace_aliases = resolve_invalid_workspace_aliases_cached(
460+
ctx = with_invalid_workspace_aliases(
459461
ctx,
460462
global_db,
461463
workspace_path,
462464
rules,
463465
nocache=nocache,
464466
project_layouts_map=project_layouts_map,
465467
)
468+
invalid_workspace_aliases = ctx.invalid_workspace_aliases or {}
466469

467470
for row in composer_rows:
468471
composer = parse_composer_data_row(
@@ -529,6 +532,8 @@ def assemble_single_tab(
529532
composer_id: str,
530533
workspace_path: str,
531534
rules: list[Any],
535+
*,
536+
nocache: bool = False,
532537
) -> tuple[dict[str, Any], int]:
533538
"""Assemble a single conversation tab for GET /api/workspaces/<id>/tabs/<composer_id>.
534539
@@ -541,6 +546,7 @@ def assemble_single_tab(
541546
composer_id: UUID of the composer / conversation to assemble.
542547
workspace_path: Cursor ``workspaceStorage`` root.
543548
rules: Exclusion rule token lists.
549+
nocache: When ``True``, bypass alias disk cache reads and writes.
544550
545551
Returns:
546552
``(payload, status)``. On success (``200``), *payload* is
@@ -550,7 +556,9 @@ def assemble_single_tab(
550556
"""
551557
parse_warnings = ParseWarningCollector()
552558

553-
ctx = resolve_workspace_context_cached(workspace_path, rules)
559+
ctx = resolve_workspace_context_cached(
560+
workspace_path, rules, nocache=nocache,
561+
)
554562
workspace_entries = ctx.workspace_entries
555563
invalid_workspace_ids = ctx.invalid_workspace_ids
556564
project_name_map = ctx.project_name_to_workspace_id
@@ -583,12 +591,10 @@ def assemble_single_tab(
583591
project_layouts_map[composer_id] = load_project_layouts_for_composer(
584592
global_db, composer_id,
585593
)
586-
invalid_workspace_aliases = resolve_invalid_workspace_aliases_cached(
587-
ctx,
588-
global_db,
589-
workspace_path,
590-
rules,
594+
ctx = with_invalid_workspace_aliases(
595+
ctx, global_db, workspace_path, rules, nocache=nocache,
591596
)
597+
invalid_workspace_aliases = ctx.invalid_workspace_aliases or {}
592598

593599
bubble_map = load_bubbles_for_composer(
594600
global_db, composer_id, parse_warnings=parse_warnings,
@@ -633,13 +639,16 @@ def assemble_workspace_tabs(
633639
workspace_id: str,
634640
workspace_path: str,
635641
rules: list[Any],
642+
*,
643+
nocache: bool = False,
636644
) -> tuple[dict[str, Any], int]:
637645
"""Build tabs payload for GET /api/workspaces/<id>/tabs (IDE workspaces).
638646
639647
Args:
640648
workspace_id: Workspace folder name, or ``"global"`` for unassigned chats.
641649
workspace_path: Cursor ``workspaceStorage`` root.
642650
rules: Exclusion rule token lists from :func:`utils.exclusion_rules.load_rules`.
651+
nocache: When ``True``, bypass alias disk cache reads and writes.
643652
644653
Returns:
645654
``(payload, status)``. On success (``200``), *payload* contains ``tabs``
@@ -651,7 +660,9 @@ def assemble_workspace_tabs(
651660
parse_warnings = ParseWarningCollector()
652661
response: dict[str, Any] = {"tabs": []}
653662

654-
ctx = resolve_workspace_context_cached(workspace_path, rules)
663+
ctx = resolve_workspace_context_cached(
664+
workspace_path, rules, nocache=nocache,
665+
)
655666
workspace_entries = ctx.workspace_entries
656667
invalid_workspace_ids = ctx.invalid_workspace_ids
657668
project_name_map = ctx.project_name_to_workspace_id
@@ -714,13 +725,15 @@ def assemble_workspace_tabs(
714725
# Get composer data entries with conversations
715726
composer_rows = safe_fetchall(global_db, COMPOSER_ROWS_WITH_HEADERS_SQL)
716727

717-
invalid_workspace_aliases = resolve_invalid_workspace_aliases_cached(
728+
ctx = with_invalid_workspace_aliases(
718729
ctx,
719730
global_db,
720731
workspace_path,
721732
rules,
733+
nocache=nocache,
722734
project_layouts_map=project_layouts_map,
723735
)
736+
invalid_workspace_aliases = ctx.invalid_workspace_aliases or {}
724737

725738
for row in composer_rows:
726739
composer = parse_composer_data_row(

tests/test_workspace_listing_performance.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,18 @@ def test_output_shape_preserved(self):
137137

138138
def test_nocache_bypasses_alias_disk_cache(self):
139139
ws_path = _make_fixture_with_invalid_workspace(self.tmp.name)
140-
with patch(
141-
"services.summary_cache.get_cached_invalid_workspace_aliases",
142-
) as mock_get:
140+
with (
141+
patch(
142+
"services.summary_cache.get_cached_invalid_workspace_aliases",
143+
) as mock_get,
144+
patch(
145+
"services.summary_cache.set_cached_invalid_workspace_aliases",
146+
) as mock_set,
147+
):
143148
mock_get.return_value = {"invalid-ws": "global"}
144149
list_workspace_projects(ws_path, rules=[], nocache=True)
145150
mock_get.assert_not_called()
151+
mock_set.assert_not_called()
146152

147153

148154
if __name__ == "__main__":

0 commit comments

Comments
 (0)