finished going through the entire gui_2.py minor sift through ai client

This commit is contained in:
ed
2026-06-13 16:16:03 -04:00
parent f2fa566064
commit 1136273331
2 changed files with 214 additions and 329 deletions
+14 -63
View File
@@ -79,10 +79,7 @@ events: EventEmitter = EventEmitter()
#region: Provider Configuration #region: Provider Configuration
def set_model_params(temp: float, max_tok: int, trunc_limit: int = 8000, top_p: float = 1.0) -> None: def set_model_params(temp: float, max_tok: int, trunc_limit: int = 8000, top_p: float = 1.0) -> None:
""" """Sets global generation parameters like temperature and max tokens."""
Sets global generation parameters like temperature and max tokens.
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate]
"""
global _temperature, _max_tokens, _history_trunc_limit, _top_p global _temperature, _max_tokens, _history_trunc_limit, _top_p
_temperature = temp _temperature = temp
_max_tokens = max_tok _max_tokens = max_tok
@@ -150,17 +147,11 @@ _local_storage = threading.local()
_tool_approval_modes: dict[str, str] = {} _tool_approval_modes: dict[str, str] = {}
def get_current_tier() -> Optional[str]: def get_current_tier() -> Optional[str]:
""" """Returns the current tier from thread-local storage."""
Returns the current tier from thread-local storage.
[C: src/app_controller.py:AppController._on_tool_log, tests/test_ai_client_concurrency.py:intercepted_append]
"""
return getattr(_local_storage, "current_tier", None) return getattr(_local_storage, "current_tier", None)
def set_current_tier(tier: Optional[str]) -> None: def set_current_tier(tier: Optional[str]) -> None:
""" """Sets the current tier in thread-local storage."""
Sets the current tier in thread-local storage.
[C: src/app_controller.py:AppController._handle_request_event, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:reset_tier, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2]
"""
_local_storage.current_tier = tier _local_storage.current_tier = tier
# Increased to allow thorough code exploration before forcing a summary # Increased to allow thorough code exploration before forcing a summary
@@ -198,10 +189,7 @@ _project_context_marker: str = ""
#region: System Prompt Management #region: System Prompt Management
def set_custom_system_prompt(prompt: str) -> None: def set_custom_system_prompt(prompt: str) -> None:
""" """Sets a custom system prompt to be combined with the default instructions."""
Sets a custom system prompt to be combined with the default instructions.
[C: simulation/user_agent.py:UserSimAgent.generate_response, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp]
"""
global _custom_system_prompt global _custom_system_prompt
_custom_system_prompt = prompt _custom_system_prompt = prompt
@@ -236,9 +224,6 @@ def _get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optio
return base return base
def get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optional[BiasProfile] = None) -> str: def get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optional[BiasProfile] = None) -> str:
"""
[C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event]
"""
return _get_combined_system_prompt(preset, bias) return _get_combined_system_prompt(preset, bias)
_comms_log: deque[dict[str, Any]] = deque(maxlen=1000) _comms_log: deque[dict[str, Any]] = deque(maxlen=1000)
@@ -250,27 +235,16 @@ COMMS_CLAMP_CHARS: int = 300
#region: Comms Log #region: Comms Log
def get_comms_log_callback() -> Optional[Callable[[dict[str, Any]], None]]: def get_comms_log_callback() -> Optional[Callable[[dict[str, Any]], None]]:
"""
Returns the comms log callback (thread-local with global fallback).
[C: src/multi_agent_conductor.py:run_worker_lifecycle]
"""
tl_cb = getattr(_local_storage, "comms_log_callback", None) tl_cb = getattr(_local_storage, "comms_log_callback", None)
if tl_cb: return tl_cb if tl_cb: return tl_cb
return comms_log_callback return comms_log_callback
def set_comms_log_callback(cb: Optional[Callable[[dict[str, Any]], None]]) -> None: def set_comms_log_callback(cb: Optional[Callable[[dict[str, Any]], None]]) -> None:
"""
Sets the comms log callback (both global and thread-local).
[C: src/app_controller.py:AppController._init_ai_and_hooks, src/multi_agent_conductor.py:run_worker_lifecycle]
"""
global comms_log_callback global comms_log_callback
comms_log_callback = cb comms_log_callback = cb
_local_storage.comms_log_callback = cb _local_storage.comms_log_callback = cb
def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None: def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None:
"""
[C: tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2]
"""
entry: dict[str, Any] = { entry: dict[str, Any] = {
"ts": datetime.datetime.now().strftime("%H:%M:%S"), "ts": datetime.datetime.now().strftime("%H:%M:%S"),
"direction": direction, "direction": direction,
@@ -287,27 +261,15 @@ def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None:
_cb(entry) _cb(entry)
def get_comms_log() -> list[dict[str, Any]]: def get_comms_log() -> list[dict[str, Any]]:
"""
[C: src/app_controller.py:AppController._bg_task, src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._start_track_logic, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_token_usage.py:test_token_usage_tracking]
"""
return list(_comms_log) return list(_comms_log)
def clear_comms_log() -> None: def clear_comms_log() -> None:
"""
[C: src/app_controller.py:AppController._handle_reset_session, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._show_menus, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_token_usage.py:test_token_usage_tracking]
"""
_comms_log.clear() _comms_log.clear()
def get_credentials_path() -> Path: def get_credentials_path() -> Path:
"""
[C: src/mcp_client.py:_is_allowed]
"""
return Path(os.environ.get("SLOP_CREDENTIALS", str(Path(__file__).parent.parent / "credentials.toml"))) return Path(os.environ.get("SLOP_CREDENTIALS", str(Path(__file__).parent.parent / "credentials.toml")))
def _load_credentials() -> dict[str, Any]: def _load_credentials() -> dict[str, Any]:
"""
[C: src/ai_server.py:_send_anthropic, src/ai_server.py:_send_deepseek, src/ai_server.py:_send_gemini, src/ai_server.py:_send_minimax, src/ai_server.py:handle_command, tests/test_deepseek_infra.py:test_credentials_error_mentions_deepseek, tests/test_minimax_provider.py:test_minimax_credentials_template]
"""
cred_path = get_credentials_path() cred_path = get_credentials_path()
try: try:
with open(cred_path, "rb") as f: with open(cred_path, "rb") as f:
@@ -407,15 +369,13 @@ def _classify_minimax_error(exc: Exception, source: str = "ai_client.minimax") -
return ErrorInfo(kind=ErrorKind.UNKNOWN, message=body, source=source, original=exc) return ErrorInfo(kind=ErrorKind.UNKNOWN, message=body, source=source, original=exc)
def set_provider(provider: str, model: str, validate: bool = True) -> None: def set_provider(provider: str, model: str, validate: bool = True) -> None:
""" """Updates the active LLM provider and model name.
Updates the active LLM provider and model name.
When validate is True (default), the model is checked against the provider's When validate is True (default), the model is checked against the provider's
LIVE model list, which for gemini_cli/minimax means a blocking subprocess / LIVE model list, which for gemini_cli/minimax means a blocking subprocess /
network call (and importing the provider SDK). Pass validate=False during network call (and importing the provider SDK). Pass validate=False during
startup so the GUI's first frame is not blocked — AppController._fetch_models startup so the GUI's first frame is not blocked — AppController._fetch_models
corrects the model against the live list shortly after, off the main thread. corrects the model against the live list shortly after, off the main thread.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.do_fetch, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_model_selection, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_minimax_provider.py:test_minimax_default_model, tests/test_minimax_provider.py:test_minimax_model_selection, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_rag_integration.py:test_rag_integration, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_tier4_interceptor.py:test_gemini_provider_passes_qa_callback_to_run_script, tests/test_token_usage.py:test_token_usage_tracking]
""" """
global _provider, _model global _provider, _model
_provider = provider _provider = provider
@@ -442,17 +402,11 @@ def set_provider(provider: str, model: str, validate: bool = True) -> None:
_model = model _model = model
def get_provider() -> str: def get_provider() -> str:
""" """Returns the current active provider name."""
Returns the current active provider name.
[C: src/multi_agent_conductor.py:run_worker_lifecycle]
"""
return _provider return _provider
def cleanup() -> None: def cleanup() -> None:
""" """Performs cleanup operations like deleting server-side Gemini caches."""
Performs cleanup operations like deleting server-side Gemini caches.
[C: src/app_controller.py:AppController.clear_cache, src/app_controller.py:AppController.shutdown, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking_cleanup, tests/test_log_registry.py:TestLogRegistry.tearDown, tests/test_project_serialization.py:TestProjectSerialization.tearDown]
"""
global _gemini_client, _gemini_cache, _gemini_cached_file_paths global _gemini_client, _gemini_cache, _gemini_cached_file_paths
if _gemini_client and _gemini_cache: if _gemini_client and _gemini_cache:
try: try:
@@ -462,10 +416,7 @@ def cleanup() -> None:
_gemini_cached_file_paths = [] _gemini_cached_file_paths = []
def reset_session() -> None: def reset_session() -> None:
""" """Clears conversation history and resets provider-specific session state."""
Clears conversation history and resets provider-specific session state.
[C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking]
"""
global _gemini_client, _gemini_chat, _gemini_cache global _gemini_client, _gemini_chat, _gemini_cache
global _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths global _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths
global _anthropic_client, _anthropic_history global _anthropic_client, _anthropic_history
@@ -2208,7 +2159,7 @@ def _ensure_grok_client() -> Any:
_grok_client = openai.OpenAI(api_key=api_key, base_url="https://api.x.ai/v1") _grok_client = openai.OpenAI(api_key=api_key, base_url="https://api.x.ai/v1")
return _grok_client return _grok_client
def _send_grok_result(md_content: str, user_message: str, base_dir: str, def _send_grok(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
stream: bool = False, stream: bool = False,
@@ -2260,7 +2211,7 @@ def _list_grok_models() -> list[str]:
from src.vendor_capabilities import list_models_for_vendor from src.vendor_capabilities import list_models_for_vendor
return list_models_for_vendor("grok") return list_models_for_vendor("grok")
def _send_minimax_result(md_content: str, user_message: str, base_dir: str, def _send_minimax(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
stream: bool = False, stream: bool = False,
@@ -2432,7 +2383,7 @@ def _ensure_llama_client() -> Any:
_llama_client = openai.OpenAI(api_key=_llama_api_key, base_url=_llama_base_url) _llama_client = openai.OpenAI(api_key=_llama_api_key, base_url=_llama_base_url)
return _llama_client return _llama_client
def _send_llama_result(md_content: str, user_message: str, base_dir: str, def _send_llama(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
stream: bool = False, stream: bool = False,
@@ -2497,7 +2448,7 @@ def ollama_chat(
resp = requests.post(f"{base_url}/api/chat", json=payload, timeout=120) resp = requests.post(f"{base_url}/api/chat", json=payload, timeout=120)
return resp.json() return resp.json()
def _send_llama_native_result(md_content: str, user_message: str, base_dir: str, def _send_llama_native(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
stream: bool = False, stream: bool = False,
@@ -2751,7 +2702,7 @@ def send_result(
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
elif p == "minimax": elif p == "minimax":
res = _send_minimax_result( res = _send_minimax(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
@@ -2771,7 +2722,7 @@ def send_result(
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
elif p == "llama_native": elif p == "llama_native":
res = _send_llama_native_result( res = _send_llama_native(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
+110 -176
View File
@@ -4513,13 +4513,11 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
imgui.separator() imgui.separator()
def render_discussion_entry_read_mode(app: App, entry: dict, index: int) -> None: def render_discussion_entry_read_mode(app: App, entry: dict, index: int) -> None:
""" """Renders a discussion entry in read-only mode.
Renders a discussion entry in read-only mode.
Parses the markdown content, isolates retrieved context sections (RAG chunks), Parses the markdown content, isolates retrieved context sections (RAG chunks),
handles custom definition/AST links, and invokes the markdown syntax highlighter. handles custom definition/AST links, and invokes the markdown syntax highlighter.
SSDL Shape: SSDL: `[I:extract_rag] -> [B:definitions?] => [I:markdown]`
`[I:extract_rag] -> [B:definitions?] => [I:markdown]`
ASCII Layout Map: ASCII Layout Map:
+-------------------------------------------------------------+ +-------------------------------------------------------------+
@@ -4578,16 +4576,10 @@ def render_discussion_entry_read_mode(app: App, entry: dict, index: int) -> None
imgui.end_group() imgui.end_group()
def render_history_window(app: App) -> None: def render_history_window(app: App) -> None:
""" """Renders the Undo/Redo History window. Displays past UI snapshots in reverse chronological
Renders the Undo/Redo History window. Displays past UI snapshots in reverse chronological
order and allows reverting to prior states. order and allows reverting to prior states.
State Mutations: SSDL: `[I:history_list] -> [B:undo_redo_buttons] => [B:selectable_snapshots]`
app.show_windows['Undo/Redo History'] (updates visibility)
app.history (jumping/traversing history)
SSDL Shape:
`[I:history_list] -> [B:undo_redo_buttons] => [B:selectable_snapshots]`
""" """
if not app.show_windows.get('Undo/Redo History', False): if not app.show_windows.get('Undo/Redo History', False):
return return
@@ -4613,15 +4605,10 @@ def render_history_window(app: App) -> None:
else: iterate_history(history) else: iterate_history(history)
def render_session_insights_panel(app: App) -> None: def render_session_insights_panel(app: App) -> None:
""" """Renders session productivity insights, displaying total tokens, API call counts,
Renders session productivity insights, displaying total tokens, API call counts,
burn rates, total costs, completed ticket counts, and token efficiency. burn rates, total costs, completed ticket counts, and token efficiency.
State Mutations: SSDL: `[I:insights] -> [I:telemetry_texts]`
None directly.
SSDL Shape:
`[I:insights] -> [I:telemetry_texts]`
""" """
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_session_insights_panel") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_session_insights_panel")
imgui.text_colored(C_LBL(), 'Session Insights') imgui.text_colored(C_LBL(), 'Session Insights')
@@ -4638,16 +4625,10 @@ def render_session_insights_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_session_insights_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_session_insights_panel")
def render_prior_session_view(app: App) -> None: def render_prior_session_view(app: App) -> None:
""" """Renders a historical read-only view of a loaded prior discussion session, complete
Renders a historical read-only view of a loaded prior discussion session, complete
with collapsing message bubbles and bubble colors. with collapsing message bubbles and bubble colors.
State Mutations: SSDL: `[I:prior_entries] -> [B:exit_button] -> [I:scroll_list]`
app.prior_disc_entries (collapsing/expanding bubbles)
app._comms_log_dirty (triggers log reload on exit)
SSDL Shape:
`[I:prior_entries] -> [B:exit_button] -> [I:scroll_list]`
""" """
with imscope.style_color(imgui.Col_.child_bg, theme.get_color("bubble_vendor")): with imscope.style_color(imgui.Col_.child_bg, theme.get_color("bubble_vendor")):
if imgui.button("Exit Prior Session"): app.controller.cb_exit_prior_session(); app._comms_log_dirty = True if imgui.button("Exit Prior Session"): app.controller.cb_exit_prior_session(); app._comms_log_dirty = True
@@ -4674,26 +4655,20 @@ def render_prior_session_view(app: App) -> None:
imgui.separator() imgui.separator()
def render_thinking_indicator(app: App) -> None: def render_thinking_indicator(app: App) -> None:
""" """Renders a blinking red status indicator when the AI is currently executing,
Renders a blinking red status indicator when the AI is currently executing,
sending, streaming, or running shell commands. sending, streaming, or running shell commands.
State Mutations: SSDL: `[I:ai_status] -> [I:blinking_text]`
None.
SSDL Shape:
`[I:ai_status] -> [I:blinking_text]`
""" """
is_thinking = app.ai_status in ['sending...', 'streaming...', 'running powershell...'] is_thinking = app.ai_status in ['sending...', 'streaming...', 'running powershell...']
if is_thinking: if is_thinking:
val = math.sin(time.time() * 10 * math.pi) val = math.sin(time.time() * 10 * math.pi)
alpha = 1.0 if val > 0 else 0.0 alpha = 1.0 if val > 0 else 0.0
c = theme.get_color("status_error", alpha=alpha) c = theme.get_color("status_error", alpha=alpha)
imgui.text_colored(c, "THINKING..."); imgui.same_line() imgui.text_colored(c, "THINKING...");
def _on_warmup_complete_callback(app: App, status: dict) -> None: def _on_warmup_complete_callback(app: App, status: dict) -> None:
""" """Thread-safe callback registered with controller.on_warmup_complete()
Thread-safe callback registered with controller.on_warmup_complete()
during App._post_init. Records the completion timestamp; the during App._post_init. Records the completion timestamp; the
indicator function uses it to show a brief "ready" tag. Also indicator function uses it to show a brief "ready" tag. Also
appends a message to a lock-protected list that the indicator appends a message to a lock-protected list that the indicator
@@ -4709,7 +4684,7 @@ def _on_warmup_complete_callback(app: App, status: dict) -> None:
if failed: msg = f"Warmup finished with {len(failed)} failures ({total} modules)" if failed: msg = f"Warmup finished with {len(failed)} failures ({total} modules)"
else: msg = f"All imports ready ({total} modules)" else: msg = f"All imports ready ({total} modules)"
if not hasattr(app, "_warmup_toast_lock"): if not hasattr(app, "_warmup_toast_lock"):
import threading as _threading import threading as _threading #TODO(Ed): Review local import
app._warmup_toast_lock = _threading.Lock() app._warmup_toast_lock = _threading.Lock()
with app._warmup_toast_lock: with app._warmup_toast_lock:
if not hasattr(app, "_warmup_toast_messages"): app._warmup_toast_messages = [] if not hasattr(app, "_warmup_toast_messages"): app._warmup_toast_messages = []
@@ -4717,15 +4692,10 @@ def _on_warmup_complete_callback(app: App, status: dict) -> None:
except Exception: pass except Exception: pass
def render_warmup_status_indicator(app: App) -> None: def render_warmup_status_indicator(app: App) -> None:
""" """Renders a transient warmup status indicator in the main interface frame. Shows progress
Renders a transient warmup status indicator in the main interface frame. Shows progress
of AppController's background module imports. of AppController's background module imports.
State Mutations: SSDL: `[I:warmup_status] -> [I:status_text]`
None directly (reads controller warmup state).
SSDL Shape:
`[I:warmup_status] -> [I:status_text]`
""" """
controller = getattr(app, "controller", None) controller = getattr(app, "controller", None)
if controller is None: return if controller is None: return
@@ -4784,16 +4754,10 @@ def render_synthesis_panel(app: App) -> None:
app._handle_generate_send() app._handle_generate_send()
def render_comms_history_panel(app: App) -> None: def render_comms_history_panel(app: App) -> None:
""" """Renders the communications history log panel. Displays outgoing requests, incoming responses,
Renders the communications history log panel. Displays outgoing requests, incoming responses,
and tool call inputs/outputs in chronological order. and tool call inputs/outputs in chronological order.
State Mutations: SSDL: `[I:ai_status] -> [B:clear_exit_buttons] -> [I:direction_colors] -> [I:entries_scroll_list]`
app._comms_log_dirty (marks log for reload)
app._comms_log (clears log on user clear request)
SSDL Shape:
`[I:ai_status] -> [B:clear_exit_buttons] -> [I:direction_colors] -> [I:entries_scroll_list]`
""" """
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_comms_history_panel") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_comms_history_panel")
st_col = theme.get_color("text_disabled") st_col = theme.get_color("text_disabled")
@@ -4853,8 +4817,8 @@ def render_comms_history_panel(app: App) -> None:
imgui.text_colored(theme.get_color("status_error"), f"[{ticket_id}]") imgui.text_colored(theme.get_color("status_error"), f"[{ticket_id}]")
imgui.same_line() imgui.same_line()
d_col_fn = DIR_COLORS.get(direction, C_VAL) d_col_fn = DIR_COLORS.get(direction, C_VAL)
imgui.text_colored(d_col_fn(), direction); imgui.same_line()
k_col_fn = KIND_COLORS.get(kind, C_VAL) k_col_fn = KIND_COLORS.get(kind, C_VAL)
imgui.text_colored(d_col_fn(), direction); imgui.same_line()
imgui.text_colored(k_col_fn(), kind); imgui.same_line() imgui.text_colored(k_col_fn(), kind); imgui.same_line()
imgui.text_colored(C_LBL(), f"{provider}/{model}"); imgui.same_line() imgui.text_colored(C_LBL(), f"{provider}/{model}"); imgui.same_line()
imgui.text_colored(C_SUB(), f"[{tier}]") imgui.text_colored(C_SUB(), f"[{tier}]")
@@ -4903,12 +4867,10 @@ def render_comms_history_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_comms_history_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_comms_history_panel")
def render_takes_panel(app: App) -> None: def render_takes_panel(app: App) -> None:
""" """Renders the Takes & Synthesis panel. Lists all discussion takes in a table,
Renders the Takes & Synthesis panel. Lists all discussion takes in a table,
allows switching or deleting them, and provides a multi-take synthesis workflow. allows switching or deleting them, and provides a multi-take synthesis workflow.
SSDL Shape: SSDL: `[I:takes_table] -> [B:switch/delete] -> [I:synthesis_config] => [B:generate_synthesis]`
`[I:takes_table] -> [B:switch/delete] -> [I:synthesis_config] => [B:generate_synthesis]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -4931,10 +4893,8 @@ def render_takes_panel(app: App) -> None:
imgui.text("Takes & Synthesis") imgui.text("Takes & Synthesis")
imgui.separator() imgui.separator()
discussions = app.project.get('discussion', {}).get('discussions', {}) discussions = app.project.get('discussion', {}).get('discussions', {})
if not isinstance(getattr(app, 'ui_synthesis_selected_takes', None), dict): if not isinstance(getattr(app, 'ui_synthesis_selected_takes', None), dict): app.ui_synthesis_selected_takes = {name: False for name in discussions}
app.ui_synthesis_selected_takes = {name: False for name in discussions} if not isinstance(getattr(app, 'ui_synthesis_prompt', None), str): app.ui_synthesis_prompt = ""
if not isinstance(getattr(app, 'ui_synthesis_prompt', None), str):
app.ui_synthesis_prompt = ""
if imgui.begin_table("takes_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders): if imgui.begin_table("takes_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
imgui.table_setup_column("Name", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("Name", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Entries", imgui.TableColumnFlags_.width_fixed, 80) imgui.table_setup_column("Entries", imgui.TableColumnFlags_.width_fixed, 80)
@@ -4984,12 +4944,10 @@ def render_takes_panel(app: App) -> None:
app._handle_generate_send() app._handle_generate_send()
def render_discussion_entries(app: App) -> None: def render_discussion_entries(app: App) -> None:
""" """Renders the scrollable list of all discussion entry bubbles. When a focus agent is
Renders the scrollable list of all discussion entry bubbles. When a focus agent is
active (ui_focus_agent), only entries matching that agent's persona (or User) are shown. active (ui_focus_agent), only entries matching that agent's persona (or User) are shown.
SSDL Shape: SSDL: `[I:filter_by_agent?] -> [I:scroll_child] => [I:entry_bubbles]`
`[I:filter_by_agent?] -> [I:scroll_child] => [I:entry_bubbles]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5013,13 +4971,11 @@ def render_discussion_entries(app: App) -> None:
if app._scroll_disc_to_bottom: imgui.set_scroll_here_y(1.0); app._scroll_disc_to_bottom = False if app._scroll_disc_to_bottom: imgui.set_scroll_here_y(1.0); app._scroll_disc_to_bottom = False
def render_discussion_entry_controls(app: App) -> None: def render_discussion_entry_controls(app: App) -> None:
""" """Renders the action buttons at the bottom of the discussion panel.
Renders the action buttons at the bottom of the discussion panel.
Handles adding entries, expanding/collapsing all bubbles, clearing, saving, Handles adding entries, expanding/collapsing all bubbles, clearing, saving,
compressing logs, and configuring auto-history checkpoints. compressing logs, and configuring auto-history checkpoints.
SSDL Shape: SSDL: `[I:buttons] -> [B:clicks] => [S:entries_or_config]`
`[I:buttons] -> [B:clicks] => [S:entries_or_config]`
ASCII Layout Map: ASCII Layout Map:
+-------------------------------------------------------------+ +-------------------------------------------------------------+
@@ -5051,12 +5007,10 @@ def render_discussion_entry_controls(app: App) -> None:
app.ai_status = f"history truncated to {app.ui_disc_truncate_pairs} pairs" app.ai_status = f"history truncated to {app.ui_disc_truncate_pairs} pairs"
def render_discussion_metadata(app: App) -> None: def render_discussion_metadata(app: App) -> None:
""" """Renders per-discussion metadata: cumulative token counts, git commit association,
Renders per-discussion metadata: cumulative token counts, git commit association,
last updated timestamp, and controls for creating, renaming, and deleting discussions. last updated timestamp, and controls for creating, renaming, and deleting discussions.
SSDL Shape: SSDL: `[I:token_totals] -> [I:commit_row] -> [I:timestamp] -> [B:create/rename/delete]`
`[I:token_totals] -> [I:commit_row] -> [I:timestamp] -> [B:create/rename/delete]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5099,13 +5053,11 @@ def render_discussion_metadata(app: App) -> None:
if imgui.button("Delete"): app._delete_discussion(app.active_discussion) if imgui.button("Delete"): app._delete_discussion(app.active_discussion)
def render_discussion_panel(app: App) -> None: def render_discussion_panel(app: App) -> None:
""" """Top-level discussion panel compositor. Renders the thinking indicator,
Top-level discussion panel compositor. Renders the thinking indicator,
prior-session read-only view (if active), discussion selector, entry controls, prior-session read-only view (if active), discussion selector, entry controls,
role list, and scrollable entry bubbles. role list, and scrollable entry bubbles.
SSDL Shape: SSDL: `[I:thinking_indicator] -> [I:prior_view?] | [I:selector] -> [I:controls] -> [I:roles] -> [I:entries]`
`[I:thinking_indicator] -> [I:prior_view?] | [I:selector] -> [I:controls] -> [I:roles] -> [I:entries]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5142,12 +5094,10 @@ def render_discussion_panel(app: App) -> None:
return return
def render_discussion_roles(app: App) -> None: def render_discussion_roles(app: App) -> None:
""" """Renders the collapsible Roles section. Lists current roles with an [X] delete button
Renders the collapsible Roles section. Lists current roles with an [X] delete button
per entry, and exposes an input field for adding new roles. per entry, and exposes an input field for adding new roles.
SSDL Shape: SSDL: `[B:collapsing_header] => [I:roles_list] -> [B:add_role]`
`[B:collapsing_header] => [I:roles_list] -> [B:add_role]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5171,13 +5121,11 @@ def render_discussion_roles(app: App) -> None:
return return
def render_discussion_selector(app: App) -> None: def render_discussion_selector(app: App) -> None:
""" """Renders the collapsible Discussions selector. Shows a combo-box for choosing
Renders the collapsible Discussions selector. Shows a combo-box for choosing
the active base discussion, a tab-bar for takes, a Synthesis tab, promote/track the active base discussion, a tab-bar for takes, a Synthesis tab, promote/track
controls, and the discussion metadata row. controls, and the discussion metadata row.
SSDL Shape: SSDL: `[B:collapsing_header] => [I:combo_base] -> [I:takes_tabs] -> [B:promote?] -> [B:track?] -> [I:metadata]`
`[B:collapsing_header] => [I:combo_base] -> [I:takes_tabs] -> [B:promote?] -> [B:track?] -> [I:metadata]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5189,11 +5137,9 @@ def render_discussion_selector(app: App) -> None:
| [new-name] [Create] [Rename] [Delete] | | [new-name] [Create] [Rename] [Delete] |
+---------------------------------------------------------+ +---------------------------------------------------------+
""" """
if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): return
return
names = app._get_discussion_names(); grouped = {} names = app._get_discussion_names(); grouped = {}
for name in names: for name in names: base = name.split("_take_")[0]; grouped.setdefault(base, []).append(name)
base = name.split("_take_")[0]; grouped.setdefault(base, []).append(name)
active_base = app.active_discussion.split("_take_")[0] active_base = app.active_discussion.split("_take_")[0]
if active_base not in grouped: active_base = names[0] if names else "" if active_base not in grouped: active_base = names[0] if names else ""
base_names = sorted(grouped.keys()) base_names = sorted(grouped.keys())
@@ -5237,8 +5183,7 @@ def render_discussion_selector(app: App) -> None:
return return
def render_discussion_tab(app: App) -> None: def render_discussion_tab(app: App) -> None:
""" """Renders the Discussion tab content. Comprises a resizable top pane (history/entries)
Renders the Discussion tab content. Comprises a resizable top pane (history/entries)
and a bottom pane with a draggable splitter bar, pop-out checkboxes, and and a bottom pane with a draggable splitter bar, pop-out checkboxes, and
inline Message/Response tabs. inline Message/Response tabs.
@@ -5297,12 +5242,10 @@ def render_discussion_tab(app: App) -> None:
#region: Operations Monitor #region: Operations Monitor
def render_operations_hub(app: App) -> None: def render_operations_hub(app: App) -> None:
""" """Top-level Operations Monitor hub. Houses pop-out checkboxes for Tool Calls, Usage Analytics,
Top-level Operations Monitor hub. Houses pop-out checkboxes for Tool Calls, Usage Analytics,
and External Tools, then a tab-bar for all ops sub-panels. and External Tools, then a tab-bar for all ops sub-panels.
SSDL Shape: SSDL: `[B:popout_toggles] -> [I:tab_bar] => [I:active_tab_content]`
`[B:popout_toggles] -> [I:tab_bar] => [I:active_tab_content]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5356,12 +5299,10 @@ def render_operations_hub(app: App) -> None:
if exp: render_vendor_state(app) if exp: render_vendor_state(app)
def render_vendor_state(app: App) -> None: def render_vendor_state(app: App) -> None:
""" """Renders the Operations Hub > Vendor State panel. Displays per-vendor health metrics
Renders the Operations Hub > Vendor State panel. Displays per-vendor health metrics
(model name, cache state, token budget, connection status) in a colour-coded table. (model name, cache state, token budget, connection status) in a colour-coded table.
SSDL Shape: SSDL: `[I:metrics_table] => [I:state_column]`
`[I:metrics_table] => [I:state_column]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5392,12 +5333,10 @@ def render_vendor_state(app: App) -> None:
imgui.end_table() imgui.end_table()
def render_message_panel(app: App) -> None: def render_message_panel(app: App) -> None:
""" """Renders user message text input area, exposing buttons to generate assistant responses,
Renders user message text input area, exposing buttons to generate assistant responses,
inject contextual files, or reset active conversation sessions. inject contextual files, or reset active conversation sessions.
SSDL Shape: SSDL: `[I:live_indicator] -> [I:input_textbox] -> [B:gen_send_buttons] -> [B:inject_reset]`
`[I:live_indicator] -> [I:input_textbox] -> [B:gen_send_buttons] -> [B:inject_reset]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5448,12 +5387,10 @@ def render_message_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_message_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_message_panel")
def render_response_panel(app: App) -> None: def render_response_panel(app: App) -> None:
""" """Renders assistant stream output panel. Renders thinking traces, markdown segments,
Renders assistant stream output panel. Renders thinking traces, markdown segments,
and exports active responses to history entries. and exports active responses to history entries.
SSDL Shape: SSDL: `[I:response_text] -> [I:thinking_trace] -> [I:markdown_view] => [B:export_to_history]`
`[I:response_text] -> [I:thinking_trace] -> [I:markdown_view] => [B:export_to_history]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5506,12 +5443,10 @@ def render_response_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_response_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_response_panel")
def render_tool_calls_panel(app: App) -> None: def render_tool_calls_panel(app: App) -> None:
""" """Renders tool call execution log. Displays script execution details, status codes,
Renders tool call execution log. Displays script execution details, status codes,
and outputs in a scrollable table. Clicking a row opens the full call in the Text Viewer. and outputs in a scrollable table. Clicking a row opens the full call in the Text Viewer.
SSDL Shape: SSDL: `[I:tool_log] -> [B:clear_button] => [I:calls_table]`
`[I:tool_log] -> [B:clear_button] => [I:calls_table]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5580,12 +5515,10 @@ def render_tool_calls_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_calls_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_calls_panel")
def render_external_tools_panel(app: App) -> None: def render_external_tools_panel(app: App) -> None:
""" """Renders the External MCPs panel. Shows server health indicators, a [Refresh] button,
Renders the External MCPs panel. Shows server health indicators, a [Refresh] button,
and a table of all registered external MCP tool names, servers, and descriptions. and a table of all registered external MCP tool names, servers, and descriptions.
SSDL Shape: SSDL Shape: `[B:refresh] -> [I:server_status_badges] -> [I:tools_table]`
`[B:refresh] -> [I:server_status_badges] -> [I:tools_table]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5642,13 +5575,11 @@ def render_external_tools_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_external_tools_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_external_tools_panel")
def render_text_viewer_window(app: App) -> None: def render_text_viewer_window(app: App) -> None:
""" """Renders the standalone text/code/markdown viewer window. Supports four rendering modes
Renders the standalone text/code/markdown viewer window. Supports four rendering modes
based on content type: markdown (rendered), slice editor (line-click range selection), based on content type: markdown (rendered), slice editor (line-click range selection),
syntax-highlighted code (CodeEditor widget), and plain text (scrollable). syntax-highlighted code (CodeEditor widget), and plain text (scrollable).
SSDL Shape: SSDL: `[I:mode_dispatch] => [I:markdown] | [I:slice_editor] | [I:code_editor] | [I:plain_text]`
`[I:mode_dispatch] => [I:markdown] | [I:slice_editor] | [I:code_editor] | [I:plain_text]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5729,7 +5660,9 @@ def render_text_viewer_window(app: App) -> None:
imgui.separator() imgui.separator()
if imgui.button("Copy"): imgui.set_clipboard_text(app.text_viewer_content) if imgui.button("Copy"): imgui.set_clipboard_text(app.text_viewer_content)
imgui.same_line(); _, app.text_viewer_wrap = imgui.checkbox("Word Wrap", app.text_viewer_wrap) imgui.same_line(); _, app.text_viewer_wrap = imgui.checkbox("Word Wrap", app.text_viewer_wrap)
imgui.separator() imgui.separator()
renderer = markdown_helper.get_renderer(); tv_type = getattr(app, "text_viewer_type", "text") renderer = markdown_helper.get_renderer(); tv_type = getattr(app, "text_viewer_type", "text")
if tv_type == 'markdown': if tv_type == 'markdown':
with imscope.child("tv_md_scroll", -1, -1, True): markdown_helper.render(app.text_viewer_content, context_id='text_viewer') with imscope.child("tv_md_scroll", -1, -1, True): markdown_helper.render(app.text_viewer_content, context_id='text_viewer')
@@ -5768,12 +5701,10 @@ def render_text_viewer_window(app: App) -> None:
imgui.end() imgui.end()
def render_patch_modal(app: App) -> None: def render_patch_modal(app: App) -> None:
""" """Renders the Apply Patch? modal. Shows files to be modified, a syntax-highlighted
Renders the Apply Patch? modal. Shows files to be modified, a syntax-highlighted
diff preview, and action buttons to open in an external editor, apply, or reject. diff preview, and action buttons to open in an external editor, apply, or reject.
SSDL Shape: SSDL: `[I:files_list] -> [I:diff_preview] => [B:open_external | B:apply | B:reject]`
`[I:files_list] -> [I:diff_preview] => [B:open_external | B:apply | B:reject]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5836,12 +5767,10 @@ def render_patch_modal(app: App) -> None:
imgui.close_current_popup() imgui.close_current_popup()
def render_external_editor_panel(app: App) -> None: def render_external_editor_panel(app: App) -> None:
""" """Renders the External Editor configuration panel. Lists configured editors
Renders the External Editor configuration panel. Lists configured editors
from config.toml, shows the current default, and allows setting a new default. from config.toml, shows the current default, and allows setting a new default.
SSDL Shape: SSDL: `[I:editors_list] -> [B:set_default] => [I:config_hint?]`
`[I:editors_list] -> [B:set_default] => [I:config_hint?]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -5970,13 +5899,11 @@ def render_approve_script_modal(app: App) -> None:
#region: Misc Tools #region: Misc Tools
def render_theme_panel(app: App) -> None: def render_theme_panel(app: App) -> None:
""" """Renders the Theme configuration window. Covers palette selection, panel pop-out toggles,
Renders the Theme configuration window. Covers palette selection, panel pop-out toggles,
font path/size, DPI scale, transparency sliders, background shader, CRT filter, and font path/size, DPI scale, transparency sliders, background shader, CRT filter, and
per-palette tone mapping (brightness/contrast/gamma). per-palette tone mapping (brightness/contrast/gamma).
SSDL Shape: SSDL: `[I:palette_combo] -> [B:panel_popout_toggles] -> [I:font_config] -> [I:scale_sliders] -> [I:tone_mapping]`
`[I:palette_combo] -> [B:panel_popout_toggles] -> [I:font_config] -> [I:scale_sliders] -> [I:tone_mapping]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6099,13 +6026,13 @@ def render_theme_panel(app: App) -> None:
imgui.end() imgui.end()
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_theme_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_theme_panel")
#NOTE(Ed): This was part of an experiment to see what the ai could generate and if it could figure how to add
# A post-process mechanism to the gui (it did not).
def render_shader_live_editor(app: App) -> None: def render_shader_live_editor(app: App) -> None:
""" """Renders the Shader Live Editor window. Exposes real-time sliders for CRT curvature,
Renders the Shader Live Editor window. Exposes real-time sliders for CRT curvature,
scanline intensity, and bloom threshold. scanline intensity, and bloom threshold.
SSDL Shape: SSDL: `[I:shader_uniforms] => [I:slider_controls]`
`[I:shader_uniforms] => [I:slider_controls]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6114,8 +6041,6 @@ def render_shader_live_editor(app: App) -> None:
| Scanline Intensity: [==|=========] 0.20 | | Scanline Intensity: [==|=========] 0.20 |
| Bloom Threshold: [===|========] 0.30 | | Bloom Threshold: [===|========] 0.30 |
+---------------------------------------------------------+ +---------------------------------------------------------+
[C: tests/test_shader_live_editor.py:test_shader_live_editor_renders]
""" """
if app.show_windows.get('Shader Editor', False): if app.show_windows.get('Shader Editor', False):
with imscope.window('Shader Editor', app.show_windows['Shader Editor']) as (exp, opened): with imscope.window('Shader Editor', app.show_windows['Shader Editor']) as (exp, opened):
@@ -6125,13 +6050,12 @@ def render_shader_live_editor(app: App) -> None:
changed_scan, app.shader_uniforms['scanline'] = imgui.slider_float('Scanline Intensity', app.shader_uniforms['scanline'], 0.0, 1.0) changed_scan, app.shader_uniforms['scanline'] = imgui.slider_float('Scanline Intensity', app.shader_uniforms['scanline'], 0.0, 1.0)
changed_bloom, app.shader_uniforms['bloom'] = imgui.slider_float('Bloom Threshold', app.shader_uniforms['bloom'], 0.0, 1.0) changed_bloom, app.shader_uniforms['bloom'] = imgui.slider_float('Bloom Threshold', app.shader_uniforms['bloom'], 0.0, 1.0)
#TODO(Ed): This shouldn't be here, it needs to be within the test that uitlizes it.
def render_markdown_test(app: App) -> None: def render_markdown_test(app: App) -> None:
""" """Renders a static markdown test panel used to validate the markdown renderer.
Renders a static markdown test panel used to validate the markdown renderer.
Displays headers, bold/italic text, lists, links, and a code block. Displays headers, bold/italic text, lists, links, and a code block.
SSDL Shape: SSDL: `[I:static_md_sample] => [I:rendered_markdown]`
`[I:static_md_sample] => [I:rendered_markdown]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6184,7 +6108,6 @@ def render_error_tint(app: App) -> None:
imgui.text_colored(theme.get_color("status_error"), "HOT RELOAD ERROR") imgui.text_colored(theme.get_color("status_error"), "HOT RELOAD ERROR")
imgui.text_wrapped(HotReloader.last_error or "Unknown error") imgui.text_wrapped(HotReloader.last_error or "Unknown error")
def render_project_stale_tint(app: App) -> None: def render_project_stale_tint(app: App) -> None:
"""Renders a yellow/amber tint overlay when the project is mid-switch. """Renders a yellow/amber tint overlay when the project is mid-switch.
@@ -6205,15 +6128,12 @@ def render_project_stale_tint(app: App) -> None:
imgui.text_wrapped(f"Loading: {Path(pending).stem if pending else '?'}") imgui.text_wrapped(f"Loading: {Path(pending).stem if pending else '?'}")
imgui.text_wrapped("UI is read-only until the switch completes. You can still browse tabs.") imgui.text_wrapped("UI is read-only until the switch completes. You can still browse tabs.")
def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -> None: def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -> None:
""" """Renders a labelled heavy-text field: a [+] button to pop content into the Text Viewer,
Renders a labelled heavy-text field: a [+] button to pop content into the Text Viewer,
a truncated inline preview, and a scrollable child panel showing the full content a truncated inline preview, and a scrollable child panel showing the full content
(markdown-rendered for message/text/content/system labels; plain text otherwise). (markdown-rendered for message/text/content/system labels; plain text otherwise).
SSDL Shape: SSDL: `[B:pop_out_viewer] -> [I:label_preview] -> [I:content_child]`
`[B:pop_out_viewer] -> [I:label_preview] -> [I:content_child]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6248,7 +6168,6 @@ def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -
else: else:
imgui.text_colored(theme.get_color("text"), content) imgui.text_colored(theme.get_color("text"), content)
#endregion: Misc Tools #endregion: Misc Tools
#region: MMA #region: MMA
@@ -6562,10 +6481,8 @@ def render_mma_global_controls(app: App) -> None:
imgui.same_line() imgui.same_line()
if imgui.button("Reload GUI"): if imgui.button("Reload GUI"):
success = app._trigger_hot_reload() success = app._trigger_hot_reload()
if success: if success: imgui.text_colored(theme.get_color("status_success"), "Reloaded!")
imgui.text_colored(theme.get_color("status_success"), "Reloaded!") else: imgui.text_colored(theme.get_color("status_error"), f"Error: {app._hot_reload_error or 'Unknown'}")
else:
imgui.text_colored(theme.get_color("status_error"), f"Error: {app._hot_reload_error or 'Unknown'}")
imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)") imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)")
def render_mma_usage_section(app: App) -> None: def render_mma_usage_section(app: App) -> None:
@@ -6593,8 +6510,23 @@ def render_mma_usage_section(app: App) -> None:
imgui.table_setup_column("Tier"); imgui.table_setup_column("Model"); imgui.table_setup_column("Input"); imgui.table_setup_column("Output"); imgui.table_setup_column("Est. Cost"); imgui.table_headers_row() imgui.table_setup_column("Tier"); imgui.table_setup_column("Model"); imgui.table_setup_column("Input"); imgui.table_setup_column("Output"); imgui.table_setup_column("Est. Cost"); imgui.table_headers_row()
total_cost = 0.0 total_cost = 0.0
for tier, stats in app.mma_tier_usage.items(): for tier, stats in app.mma_tier_usage.items():
imgui.table_next_row(); imgui.table_next_column(); imgui.text(tier); imgui.table_next_column(); model = stats.get('model', 'unknown'); imgui.text(model); imgui.table_next_column(); in_t = stats.get('input', 0); imgui.text(f"{in_t:,}"); imgui.table_next_column(); out_t = stats.get('output', 0); imgui.text(f"{out_t:,}"); imgui.table_next_column(); cost = cost_tracker.estimate_cost(model, in_t, out_t); total_cost += cost; imgui.text(f"${cost:,.4f}") imgui.table_next_row();
imgui.table_next_row(); imgui.table_set_bg_color(imgui.TableBgTarget_.row_bg0, imgui.get_color_u32(imgui.Col_.plot_lines_hovered)); imgui.table_next_column(); imgui.text("TOTAL"); imgui.table_next_column(); imgui.text(""); imgui.table_next_column(); imgui.text(""); imgui.table_next_column(); imgui.text(""); imgui.table_next_column(); imgui.text(f"${total_cost:,.4f}"); imgui.end_table() imgui.table_next_column();
imgui.text(tier); imgui.table_next_column();
model = stats.get('model', 'unknown'); imgui.text(model); imgui.table_next_column();
in_t = stats.get('input', 0); imgui.text(f"{in_t:,}"); imgui.table_next_column();
out_t = stats.get('output', 0); imgui.text(f"{out_t:,}"); imgui.table_next_column();
cost = cost_tracker.estimate_cost(model, in_t, out_t);
total_cost += cost; imgui.text(f"${cost:,.4f}")
imgui.table_next_row();
imgui.table_set_bg_color(imgui.TableBgTarget_.row_bg0, imgui.get_color_u32(imgui.Col_.plot_lines_hovered)); imgui.table_next_column();
imgui.text("TOTAL"); imgui.table_next_column();
imgui.text(""); imgui.table_next_column();
imgui.text(""); imgui.table_next_column();
imgui.text(""); imgui.table_next_column();
imgui.text(f"${total_cost:,.4f}");
imgui.end_table()
if imgui.collapsing_header("Tier Model Config"): if imgui.collapsing_header("Tier Model Config"):
for tier in app.mma_tier_usage.keys(): for tier in app.mma_tier_usage.keys():
imgui.text(f"{tier}:"); imgui.same_line(); curr_model, curr_prov = app.mma_tier_usage[tier].get("model", "unknown"), app.mma_tier_usage[tier].get("provider", "gemini") imgui.text(f"{tier}:"); imgui.same_line(); curr_model, curr_prov = app.mma_tier_usage[tier].get("model", "unknown"), app.mma_tier_usage[tier].get("provider", "gemini")
@@ -6631,12 +6563,10 @@ def render_mma_usage_section(app: App) -> None:
imgui.pop_item_width() imgui.pop_item_width()
def render_mma_ticket_editor(app: App) -> None: def render_mma_ticket_editor(app: App) -> None:
""" """Renders the ticket detail editor panel, letting the user modify priority, target,
Renders the ticket detail editor panel, letting the user modify priority, target,
and persona override, mark a ticket complete, or delete it. and persona override, mark a ticket complete, or delete it.
SSDL Shape: SSDL: `[I:ticket_details] -> [B:combo_selectors] => [B:action_buttons]`
`[I:ticket_details] -> [B:combo_selectors] => [B:action_buttons]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6661,19 +6591,21 @@ def render_mma_ticket_editor(app: App) -> None:
imgui.text(f"Target: {ticket.get('target_file', '')}"); imgui.text(f"Depends on: {', '.join(ticket.get('depends_on', []))}") imgui.text(f"Target: {ticket.get('target_file', '')}"); imgui.text(f"Depends on: {', '.join(ticket.get('depends_on', []))}")
personas = getattr(app.controller, 'personas', {}); curr_pers = ticket.get('persona_id', '') personas = getattr(app.controller, 'personas', {}); curr_pers = ticket.get('persona_id', '')
imgui.text("Persona Override:"); imgui.same_line() imgui.text("Persona Override:"); imgui.same_line()
pers_opts = ["None"] + sorted(personas.keys()); curr_idx = pers_opts.index(curr_pers) + 1 if curr_pers in pers_opts else 0 pers_opts = ["None"] + sorted(personas.keys());
curr_idx = pers_opts.index(curr_pers) + 1 if curr_pers in pers_opts else 0
_, curr_idx = imgui.combo(f"##ticket_persona_{ticket.get('id')}", curr_idx, pers_opts) _, curr_idx = imgui.combo(f"##ticket_persona_{ticket.get('id')}", curr_idx, pers_opts)
ticket['persona_id'] = None if curr_idx == 0 or pers_opts[curr_idx] == "None" else pers_opts[curr_idx] ticket['persona_id'] = None if curr_idx == 0 or pers_opts[curr_idx] == "None" else pers_opts[curr_idx]
if imgui.button(f"Mark Complete##{app.ui_selected_ticket_id}"): ticket['status'] = 'done'; app._push_mma_state_update() if imgui.button(f"Mark Complete##{app.ui_selected_ticket_id}"): ticket['status'] = 'done'; app._push_mma_state_update()
imgui.same_line() imgui.same_line()
if imgui.button(f"Delete##{app.ui_selected_ticket_id}"): app.active_tickets = [t for t in app.active_tickets if str(t.get('id', '')) != app.ui_selected_ticket_id]; app.ui_selected_ticket_id = None; app._push_mma_state_update() if imgui.button(f"Delete##{app.ui_selected_ticket_id}"):
app.active_tickets = [t for t in app.active_tickets if str(t.get('id', '')) != app.ui_selected_ticket_id]
app.ui_selected_ticket_id = None
app._push_mma_state_update()
def render_mma_agent_streams(app: App) -> None: def render_mma_agent_streams(app: App) -> None:
""" """Renders the agent execution stream panels in a tabbed view for Tier 1, 2, 3, and 4.
Renders the agent execution stream panels in a tabbed view for Tier 1, 2, 3, and 4.
SSDL Shape: SSDL: `[I] -> [B:tab_bar] => [I:stream_panels]`
`[I] -> [B:tab_bar] => [I:stream_panels]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6689,7 +6621,8 @@ def render_mma_agent_streams(app: App) -> None:
for tier, label, sep_flag_attr in [("Tier 1", "Tier 1", "ui_separate_tier1"), ("Tier 2", "Tier 2 (Tech Lead)", "ui_separate_tier2"), ("Tier 3", None, "ui_separate_tier3"), ("Tier 4", "Tier 4 (QA)", "ui_separate_tier4")]: for tier, label, sep_flag_attr in [("Tier 1", "Tier 1", "ui_separate_tier1"), ("Tier 2", "Tier 2 (Tech Lead)", "ui_separate_tier2"), ("Tier 3", None, "ui_separate_tier3"), ("Tier 4", "Tier 4 (QA)", "ui_separate_tier4")]:
with imscope.tab_item(tier) as (exp, _): with imscope.tab_item(tier) as (exp, _):
if exp: if exp:
sep_val = getattr(app, sep_flag_attr); ch, new_val = imgui.checkbox(f"Pop Out {tier}", sep_val) sep_val = getattr(app, sep_flag_attr);
ch, new_val = imgui.checkbox(f"Pop Out {tier}", sep_val)
if ch: if ch:
setattr(app, sep_flag_attr, new_val) setattr(app, sep_flag_attr, new_val)
app.show_windows[f"{tier}: Strategy" if tier == "Tier 1" else (f"{tier}: Tech Lead" if tier == "Tier 2" else (f"{tier}: Workers" if tier == "Tier 3" else f"{tier}: QA"))] = new_val app.show_windows[f"{tier}: Strategy" if tier == "Tier 1" else (f"{tier}: Tech Lead" if tier == "Tier 2" else (f"{tier}: Workers" if tier == "Tier 3" else f"{tier}: QA"))] = new_val
@@ -6753,16 +6686,13 @@ def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) ->
for key in tier3_keys: for key in tier3_keys:
ticket_id = key.split(": ", 1)[-1] if ": " in key else key ticket_id = key.split(": ", 1)[-1] if ": " in key else key
status = worker_status.get(key, "unknown") status = worker_status.get(key, "unknown")
if status == "running": if status == "running": imgui.text_colored(theme.get_color("status_warning"), f"{ticket_id} [{status}]")
imgui.text_colored(theme.get_color("status_warning"), f"{ticket_id} [{status}]") elif status == "completed": imgui.text_colored(theme.get_color("status_success"), f"{ticket_id} [{status}]")
elif status == "completed": elif status == "failed": imgui.text_colored(theme.get_color("status_error"), f"{ticket_id} [{status}]")
imgui.text_colored(theme.get_color("status_success"), f"{ticket_id} [{status}]") else: imgui.text( f"{ticket_id} [{status}]")
elif status == "failed":
imgui.text_colored(theme.get_color("status_error"), f"{ticket_id} [{status}]")
else:
imgui.text(f"{ticket_id} [{status}]")
imgui.begin_child(f"##tier3_{ticket_id}_scroll", imgui.ImVec2(-1, 150), True) imgui.begin_child(f"##tier3_{ticket_id}_scroll", imgui.ImVec2(-1, 150), True)
render_selectable_label(app, f'stream_t3_{ticket_id}', app.mma_streams[key], width=-1, multiline=True, height=0) render_selectable_label(app, f'stream_t3_{ticket_id}', app.mma_streams[key], width=-1, multiline=True, height=0)
#NOTE(Ed): Exception(Thirdparty)
try: try:
if len(app.mma_streams[key]) != app._tier_stream_last_len.get(key, -1): if len(app.mma_streams[key]) != app._tier_stream_last_len.get(key, -1):
imgui.set_scroll_here_y(1.0) imgui.set_scroll_here_y(1.0)
@@ -6797,7 +6727,7 @@ def render_track_proposal_modal(app: App) -> None:
if app._show_track_proposal_modal: if app._show_track_proposal_modal:
imgui.open_popup("Track Proposal") imgui.open_popup("Track Proposal")
if imgui.begin_popup_modal("Track Proposal", True, imgui.WindowFlags_.always_auto_resize)[0]: if imgui.begin_popup_modal("Track Proposal", True, imgui.WindowFlags_.always_auto_resize)[0]:
from src import shaders from src import shaders #TODO(Ed): Review local import
p_min = imgui.get_window_pos() p_min = imgui.get_window_pos()
p_max = imgui.ImVec2(p_min.x + imgui.get_window_size().x, p_min.y + imgui.get_window_size().y) p_max = imgui.ImVec2(p_min.x + imgui.get_window_size().x, p_min.y + imgui.get_window_size().y)
# Render soft shadow behind the modal # Render soft shadow behind the modal
@@ -7069,15 +6999,18 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
app._push_mma_state_update() app._push_mma_state_update()
break break
ed.end_delete() ed.end_delete()
# Validate DAG after any changes # Validate DAG after any changes
#TODO(Ed): Exception(Review)
try: try:
from src.dag_engine import TrackDAG from src.dag_engine import TrackDAG #TODO(Ed) Reivew local import
ticket_dicts = [{'id': str(t.get('id', '')), 'depends_on': t.get('depends_on', [])} for t in app.active_tickets] ticket_dicts = [{'id': str(t.get('id', '')), 'depends_on': t.get('depends_on', [])} for t in app.active_tickets]
temp_dag = TrackDAG(ticket_dicts) temp_dag = TrackDAG(ticket_dicts)
if temp_dag.has_cycle(): if temp_dag.has_cycle():
imgui.open_popup("Cycle Detected!") imgui.open_popup("Cycle Detected!")
except Exception: except Exception:
pass pass
ed.end() ed.end()
# 5. Add Ticket Form # 5. Add Ticket Form
imgui.separator() imgui.separator()
@@ -7089,6 +7022,7 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
for t in app.active_tickets: for t in app.active_tickets:
tid = t.get('id', '') tid = t.get('id', '')
if tid.startswith('T-'): if tid.startswith('T-'):
#TODO(Ed): Exception(Review)
try: max_id = max(max_id, int(tid[2:])) try: max_id = max(max_id, int(tid[2:]))
except: pass except: pass
app.ui_new_ticket_id = f"T-{max_id + 1:03d}" app.ui_new_ticket_id = f"T-{max_id + 1:03d}"