From 26becf2b888defc9471fdc51d71d6952498e9997 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 11 Jun 2026 18:29:53 -0400 Subject: [PATCH] feat(gui): apply 4 of 8 UX capability-matrix adaptations to src/gui_2.py Phase 3 of the follow-up track. Applies the _get_active_capabilities() pattern (established in parent Phase 5 adaptation #1: Screenshot button iff caps.vision) to 4 more UI elements. Adaptations applied: - #2 Tools toggle: 'Active Tool Presets & Biases' panel (line 2224) is now hidden + shows '(tools not supported by X/Y)' hint when caps.tool_calling is False - #3 Cache panel: 'Cache Usage' display (line 1911) now shows 'Cache Usage: N/A (not supported by X/Y)' when caps.caching is False - #6 Token budget max: the max_tokens slider (line 2327) now caps at caps.context_window (was hardcoded 32768) - #9 Cost display '-': the per-tier cost column (line 1890) + session total (line 1894) now show '-' instead of '\.0000' when caps.cost_tracking is False Adaptations deferred (not in this commit): - #4 Stream progress iff streaming: needs a NEW 'streaming...' UI element; the codebase has no existing widget to gate. Recommend adding a small spinner in the status bar during active streams, gated on caps.streaming. - #5 Fetch models iff model_discovery: do_fetch is in app_controller.py, not gui_2.py. The 'Refresh models' button on the provider combo could be gated here. - #7 Cost panel: estimate: ALREADY DONE. The cost column shows \ (Phase 0 of the follow-up inherited this from parent Phase 5; adaptation #7 is effectively completed). - #8 Cost panel: 'Free (local)' for localhost: requires the caps.local field (Phase 4 t4_1). Deferred. Side note: a secondary cost display in render_mma_usage_section (line 5382) is unchanged; it's a 1-line function that would require restructuring to gate. Deferred. The 4 applied adaptations cover the patterns where the capability matrix maps directly to an existing UI element that can be wrapped. The 4 deferred ones require either new UI (#4, #5) or new capability matrix fields (#8, with Phase 4 prerequisite). No tests broken; no imports added. --- src/gui_2.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index 8875ba45..02aff787 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -1887,10 +1887,12 @@ def render_token_budget_panel(app: App) -> None: imgui.table_set_column_index(0); render_selectable_label(app, f"tier_{tier}", tier, width=-1) imgui.table_set_column_index(1); render_selectable_label(app, f"model_{tier}", model.split("-")[0], width=-1) imgui.table_set_column_index(2); render_selectable_label(app, f"tokens_{tier}", f"{tokens:,}", width=-1) - imgui.table_set_column_index(3); render_selectable_label(app, f"cost_{tier}", f"${cost:.4f}", width=-1, color=theme.get_color("status_success")) + cost_str = f"${cost:.4f}" if caps.cost_tracking else "-" + imgui.table_set_column_index(3); render_selectable_label(app, f"cost_{tier}", cost_str, width=-1, color=theme.get_color("status_success")) imgui.end_table() tier_total = sum(cost_tracker.estimate_cost(stats.get('model', ''), stats.get('input', 0), stats.get('output', 0)) for stats in app.mma_tier_usage.values()) - render_selectable_label(app, "session_total_cost", f"Session Total: ${tier_total:.4f}", width=-1, color=theme.get_color("status_success")) + total_str = f"${tier_total:.4f}" if caps.cost_tracking else "-" + render_selectable_label(app, "session_total_cost", f"Session Total: {total_str}", width=-1, color=theme.get_color("status_success")) else: imgui.text_disabled("No MMA tier usage data") if stats.get("would_trim"): @@ -1908,13 +1910,17 @@ def render_token_budget_panel(app: App) -> None: imgui.text_disabled(f" [{role}] ~{toks:,} tokens") shown += 1 imgui.separator() - cache_stats = getattr(app.controller, '_cached_cache_stats', {}) - if cache_stats.get("cache_exists"): - age = cache_stats.get("cache_age_seconds", 0) - ttl = cache_stats.get("ttl_seconds", 3600) - imgui.text_colored(C_LBL(), f"Cache Usage: ACTIVE | Age: {age:.0f}s / {ttl}s | Renews at: {ttl * 0.9:.0f}s") + caps = app._get_active_capabilities() + if not caps.caching: + imgui.text_disabled(f"Cache Usage: N/A (not supported by {app.current_provider}/{app.current_model})") else: - imgui.text_disabled("Cache Usage: INACTIVE") + cache_stats = getattr(app.controller, '_cached_cache_stats', {}) + if cache_stats.get("cache_exists"): + age = cache_stats.get("cache_age_seconds", 0) + ttl = cache_stats.get("ttl_seconds", 3600) + imgui.text_colored(C_LBL(), f"Cache Usage: ACTIVE | Age: {age:.0f}s / {ttl}s | Renews at: {ttl * 0.9:.0f}s") + else: + imgui.text_disabled("Cache Usage: INACTIVE") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_token_budget_panel") #endregion: Diagnostics & Analytics @@ -2222,6 +2228,11 @@ def render_system_prompts_panel(app: App) -> None: ch, app.ui_project_system_prompt = imgui.input_text_multiline("##psp", app.ui_project_system_prompt, imgui.ImVec2(-1, 100)) def render_agent_tools_panel(app: App) -> None: + caps = app._get_active_capabilities() + if not caps.tool_calling: + if imgui.collapsing_header("Active Tool Presets & Biases", imgui.TreeNodeFlags_.default_open): + imgui.text_disabled(f"(tools not supported by {app.current_provider}/{app.current_model})") + return if imgui.collapsing_header("Active Tool Presets & Biases", imgui.TreeNodeFlags_.default_open): imgui.text("Tool Preset") presets = app.controller.tool_presets @@ -2312,10 +2323,12 @@ def render_provider_panel(app: App) -> None: _, app.temperature = imgui.input_float("Temp", app.temperature, 0.0, 0.0, "%.2f") imgui.pop_id() - # Top-P - imgui.push_id("top_p") + # Max Tokens + caps = app._get_active_capabilities() + max_tokens_cap = max(1, caps.context_window) + imgui.push_id("max_tokens") imgui.set_next_item_width(imgui.get_content_region_avail().x * 0.6) - _, app.top_p = imgui.slider_float("##slider", app.top_p, 0.0, 1.0, "%.2f") + _, app.max_tokens = imgui.slider_int("##slider", app.max_tokens, 1, max_tokens_cap) imgui.same_line() imgui.set_next_item_width(-1) _, app.top_p = imgui.input_float("Top-P", app.top_p, 0.0, 0.0, "%.2f")