Private
Public Access
finished going through the entire gui_2.py minor sift through ai client
This commit is contained in:
+14
-63
@@ -79,10 +79,7 @@ events: EventEmitter = EventEmitter()
|
||||
#region: Provider Configuration
|
||||
|
||||
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.
|
||||
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate]
|
||||
"""
|
||||
"""Sets global generation parameters like temperature and max tokens."""
|
||||
global _temperature, _max_tokens, _history_trunc_limit, _top_p
|
||||
_temperature = temp
|
||||
_max_tokens = max_tok
|
||||
@@ -150,17 +147,11 @@ _local_storage = threading.local()
|
||||
_tool_approval_modes: dict[str, str] = {}
|
||||
|
||||
def get_current_tier() -> Optional[str]:
|
||||
"""
|
||||
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]
|
||||
"""
|
||||
"""Returns the current tier from thread-local storage."""
|
||||
return getattr(_local_storage, "current_tier", None)
|
||||
|
||||
def set_current_tier(tier: Optional[str]) -> None:
|
||||
"""
|
||||
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]
|
||||
"""
|
||||
"""Sets the current tier in thread-local storage."""
|
||||
_local_storage.current_tier = tier
|
||||
|
||||
# Increased to allow thorough code exploration before forcing a summary
|
||||
@@ -198,10 +189,7 @@ _project_context_marker: str = ""
|
||||
#region: System Prompt Management
|
||||
|
||||
def set_custom_system_prompt(prompt: str) -> None:
|
||||
"""
|
||||
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]
|
||||
"""
|
||||
"""Sets a custom system prompt to be combined with the default instructions."""
|
||||
global _custom_system_prompt
|
||||
_custom_system_prompt = prompt
|
||||
|
||||
@@ -236,9 +224,6 @@ def _get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optio
|
||||
return base
|
||||
|
||||
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)
|
||||
|
||||
_comms_log: deque[dict[str, Any]] = deque(maxlen=1000)
|
||||
@@ -250,27 +235,16 @@ COMMS_CLAMP_CHARS: int = 300
|
||||
#region: Comms Log
|
||||
|
||||
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)
|
||||
if tl_cb: return tl_cb
|
||||
return comms_log_callback
|
||||
|
||||
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
|
||||
comms_log_callback = cb
|
||||
_local_storage.comms_log_callback = cb
|
||||
|
||||
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] = {
|
||||
"ts": datetime.datetime.now().strftime("%H:%M:%S"),
|
||||
"direction": direction,
|
||||
@@ -287,27 +261,15 @@ def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None:
|
||||
_cb(entry)
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
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")))
|
||||
|
||||
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()
|
||||
try:
|
||||
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)
|
||||
|
||||
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
|
||||
LIVE model list, which for gemini_cli/minimax means a blocking subprocess /
|
||||
network call (and importing the provider SDK). Pass validate=False during
|
||||
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.
|
||||
[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
|
||||
_provider = provider
|
||||
@@ -442,17 +402,11 @@ def set_provider(provider: str, model: str, validate: bool = True) -> None:
|
||||
_model = model
|
||||
|
||||
def get_provider() -> str:
|
||||
"""
|
||||
Returns the current active provider name.
|
||||
[C: src/multi_agent_conductor.py:run_worker_lifecycle]
|
||||
"""
|
||||
"""Returns the current active provider name."""
|
||||
return _provider
|
||||
|
||||
def cleanup() -> None:
|
||||
"""
|
||||
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]
|
||||
"""
|
||||
"""Performs cleanup operations like deleting server-side Gemini caches."""
|
||||
global _gemini_client, _gemini_cache, _gemini_cached_file_paths
|
||||
if _gemini_client and _gemini_cache:
|
||||
try:
|
||||
@@ -462,10 +416,7 @@ def cleanup() -> None:
|
||||
_gemini_cached_file_paths = []
|
||||
|
||||
def reset_session() -> None:
|
||||
"""
|
||||
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]
|
||||
"""
|
||||
"""Clears conversation history and resets provider-specific session state."""
|
||||
global _gemini_client, _gemini_chat, _gemini_cache
|
||||
global _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths
|
||||
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")
|
||||
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,
|
||||
discussion_history: str = "",
|
||||
stream: bool = False,
|
||||
@@ -2260,7 +2211,7 @@ def _list_grok_models() -> list[str]:
|
||||
from src.vendor_capabilities import list_models_for_vendor
|
||||
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,
|
||||
discussion_history: str = "",
|
||||
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)
|
||||
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,
|
||||
discussion_history: str = "",
|
||||
stream: bool = False,
|
||||
@@ -2497,7 +2448,7 @@ def ollama_chat(
|
||||
resp = requests.post(f"{base_url}/api/chat", json=payload, timeout=120)
|
||||
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,
|
||||
discussion_history: str = "",
|
||||
stream: bool = False,
|
||||
@@ -2751,7 +2702,7 @@ def send_result(
|
||||
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||
)
|
||||
elif p == "minimax":
|
||||
res = _send_minimax_result(
|
||||
res = _send_minimax(
|
||||
md_content, user_message, base_dir, file_items, discussion_history,
|
||||
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
|
||||
)
|
||||
elif p == "llama_native":
|
||||
res = _send_llama_native_result(
|
||||
res = _send_llama_native(
|
||||
md_content, user_message, base_dir, file_items, discussion_history,
|
||||
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||
)
|
||||
|
||||
+110
-176
@@ -4513,13 +4513,11 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
|
||||
imgui.separator()
|
||||
|
||||
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),
|
||||
handles custom definition/AST links, and invokes the markdown syntax highlighter.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:extract_rag] -> [B:definitions?] => [I:markdown]`
|
||||
SSDL: `[I:extract_rag] -> [B:definitions?] => [I:markdown]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+-------------------------------------------------------------+
|
||||
@@ -4578,16 +4576,10 @@ def render_discussion_entry_read_mode(app: App, entry: dict, index: int) -> None
|
||||
imgui.end_group()
|
||||
|
||||
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.
|
||||
|
||||
State Mutations:
|
||||
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]`
|
||||
SSDL: `[I:history_list] -> [B:undo_redo_buttons] => [B:selectable_snapshots]`
|
||||
"""
|
||||
if not app.show_windows.get('Undo/Redo History', False):
|
||||
return
|
||||
@@ -4613,15 +4605,10 @@ def render_history_window(app: App) -> None:
|
||||
else: iterate_history(history)
|
||||
|
||||
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.
|
||||
|
||||
State Mutations:
|
||||
None directly.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:insights] -> [I:telemetry_texts]`
|
||||
SSDL: `[I:insights] -> [I:telemetry_texts]`
|
||||
"""
|
||||
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_session_insights_panel")
|
||||
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")
|
||||
|
||||
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.
|
||||
|
||||
State Mutations:
|
||||
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]`
|
||||
SSDL: `[I:prior_entries] -> [B:exit_button] -> [I:scroll_list]`
|
||||
"""
|
||||
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
|
||||
@@ -4674,26 +4655,20 @@ def render_prior_session_view(app: App) -> None:
|
||||
imgui.separator()
|
||||
|
||||
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.
|
||||
|
||||
State Mutations:
|
||||
None.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:ai_status] -> [I:blinking_text]`
|
||||
SSDL: `[I:ai_status] -> [I:blinking_text]`
|
||||
"""
|
||||
is_thinking = app.ai_status in ['sending...', 'streaming...', 'running powershell...']
|
||||
if is_thinking:
|
||||
val = math.sin(time.time() * 10 * math.pi)
|
||||
alpha = 1.0 if val > 0 else 0.0
|
||||
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:
|
||||
"""
|
||||
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
|
||||
indicator function uses it to show a brief "ready" tag. Also
|
||||
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)"
|
||||
else: msg = f"All imports ready ({total} modules)"
|
||||
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()
|
||||
with app._warmup_toast_lock:
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
State Mutations:
|
||||
None directly (reads controller warmup state).
|
||||
|
||||
SSDL Shape:
|
||||
`[I:warmup_status] -> [I:status_text]`
|
||||
SSDL: `[I:warmup_status] -> [I:status_text]`
|
||||
"""
|
||||
controller = getattr(app, "controller", None)
|
||||
if controller is None: return
|
||||
@@ -4784,16 +4754,10 @@ def render_synthesis_panel(app: App) -> None:
|
||||
app._handle_generate_send()
|
||||
|
||||
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.
|
||||
|
||||
State Mutations:
|
||||
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]`
|
||||
SSDL: `[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")
|
||||
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.same_line()
|
||||
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)
|
||||
imgui.text_colored(d_col_fn(), direction); 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_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")
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:takes_table] -> [B:switch/delete] -> [I:synthesis_config] => [B:generate_synthesis]`
|
||||
SSDL: `[I:takes_table] -> [B:switch/delete] -> [I:synthesis_config] => [B:generate_synthesis]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -4931,10 +4893,8 @@ def render_takes_panel(app: App) -> None:
|
||||
imgui.text("Takes & Synthesis")
|
||||
imgui.separator()
|
||||
discussions = app.project.get('discussion', {}).get('discussions', {})
|
||||
if not isinstance(getattr(app, 'ui_synthesis_selected_takes', None), dict):
|
||||
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_selected_takes', None), dict): 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 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("Entries", imgui.TableColumnFlags_.width_fixed, 80)
|
||||
@@ -4984,12 +4944,10 @@ def render_takes_panel(app: App) -> None:
|
||||
app._handle_generate_send()
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:filter_by_agent?] -> [I:scroll_child] => [I:entry_bubbles]`
|
||||
SSDL: `[I:filter_by_agent?] -> [I:scroll_child] => [I:entry_bubbles]`
|
||||
|
||||
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
|
||||
|
||||
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,
|
||||
compressing logs, and configuring auto-history checkpoints.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:buttons] -> [B:clicks] => [S:entries_or_config]`
|
||||
SSDL: `[I:buttons] -> [B:clicks] => [S:entries_or_config]`
|
||||
|
||||
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"
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:token_totals] -> [I:commit_row] -> [I:timestamp] -> [B:create/rename/delete]`
|
||||
SSDL: `[I:token_totals] -> [I:commit_row] -> [I:timestamp] -> [B:create/rename/delete]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5099,13 +5053,11 @@ def render_discussion_metadata(app: App) -> None:
|
||||
if imgui.button("Delete"): app._delete_discussion(app.active_discussion)
|
||||
|
||||
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,
|
||||
role list, and scrollable entry bubbles.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:thinking_indicator] -> [I:prior_view?] | [I:selector] -> [I:controls] -> [I:roles] -> [I:entries]`
|
||||
SSDL: `[I:thinking_indicator] -> [I:prior_view?] | [I:selector] -> [I:controls] -> [I:roles] -> [I:entries]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5142,12 +5094,10 @@ def render_discussion_panel(app: App) -> None:
|
||||
return
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[B:collapsing_header] => [I:roles_list] -> [B:add_role]`
|
||||
SSDL: `[B:collapsing_header] => [I:roles_list] -> [B:add_role]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5171,13 +5121,11 @@ def render_discussion_roles(app: App) -> None:
|
||||
return
|
||||
|
||||
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
|
||||
controls, and the discussion metadata row.
|
||||
|
||||
SSDL Shape:
|
||||
`[B:collapsing_header] => [I:combo_base] -> [I:takes_tabs] -> [B:promote?] -> [B:track?] -> [I:metadata]`
|
||||
SSDL: `[B:collapsing_header] => [I:combo_base] -> [I:takes_tabs] -> [B:promote?] -> [B:track?] -> [I:metadata]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5189,11 +5137,9 @@ def render_discussion_selector(app: App) -> None:
|
||||
| [new-name] [Create] [Rename] [Delete] |
|
||||
+---------------------------------------------------------+
|
||||
"""
|
||||
if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open):
|
||||
return
|
||||
if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): return
|
||||
names = app._get_discussion_names(); grouped = {}
|
||||
for name in names:
|
||||
base = name.split("_take_")[0]; grouped.setdefault(base, []).append(name)
|
||||
for name in names: base = name.split("_take_")[0]; grouped.setdefault(base, []).append(name)
|
||||
active_base = app.active_discussion.split("_take_")[0]
|
||||
if active_base not in grouped: active_base = names[0] if names else ""
|
||||
base_names = sorted(grouped.keys())
|
||||
@@ -5237,8 +5183,7 @@ def render_discussion_selector(app: App) -> None:
|
||||
return
|
||||
|
||||
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
|
||||
inline Message/Response tabs.
|
||||
|
||||
@@ -5297,12 +5242,10 @@ def render_discussion_tab(app: App) -> None:
|
||||
#region: Operations Monitor
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[B:popout_toggles] -> [I:tab_bar] => [I:active_tab_content]`
|
||||
SSDL: `[B:popout_toggles] -> [I:tab_bar] => [I:active_tab_content]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5356,12 +5299,10 @@ def render_operations_hub(app: App) -> None:
|
||||
if exp: render_vendor_state(app)
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:metrics_table] => [I:state_column]`
|
||||
SSDL: `[I:metrics_table] => [I:state_column]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5392,12 +5333,10 @@ def render_vendor_state(app: App) -> None:
|
||||
imgui.end_table()
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:live_indicator] -> [I:input_textbox] -> [B:gen_send_buttons] -> [B:inject_reset]`
|
||||
SSDL: `[I:live_indicator] -> [I:input_textbox] -> [B:gen_send_buttons] -> [B:inject_reset]`
|
||||
|
||||
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")
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:response_text] -> [I:thinking_trace] -> [I:markdown_view] => [B:export_to_history]`
|
||||
SSDL: `[I:response_text] -> [I:thinking_trace] -> [I:markdown_view] => [B:export_to_history]`
|
||||
|
||||
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")
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:tool_log] -> [B:clear_button] => [I:calls_table]`
|
||||
SSDL: `[I:tool_log] -> [B:clear_button] => [I:calls_table]`
|
||||
|
||||
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")
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[B:refresh] -> [I:server_status_badges] -> [I:tools_table]`
|
||||
SSDL Shape: `[B:refresh] -> [I:server_status_badges] -> [I:tools_table]`
|
||||
|
||||
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")
|
||||
|
||||
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),
|
||||
syntax-highlighted code (CodeEditor widget), and plain text (scrollable).
|
||||
|
||||
SSDL Shape:
|
||||
`[I:mode_dispatch] => [I:markdown] | [I:slice_editor] | [I:code_editor] | [I:plain_text]`
|
||||
SSDL: `[I:mode_dispatch] => [I:markdown] | [I:slice_editor] | [I:code_editor] | [I:plain_text]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5729,7 +5660,9 @@ def render_text_viewer_window(app: App) -> None:
|
||||
imgui.separator()
|
||||
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.separator()
|
||||
|
||||
renderer = markdown_helper.get_renderer(); tv_type = getattr(app, "text_viewer_type", "text")
|
||||
if tv_type == 'markdown':
|
||||
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()
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:files_list] -> [I:diff_preview] => [B:open_external | B:apply | B:reject]`
|
||||
SSDL: `[I:files_list] -> [I:diff_preview] => [B:open_external | B:apply | B:reject]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5836,12 +5767,10 @@ def render_patch_modal(app: App) -> None:
|
||||
imgui.close_current_popup()
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:editors_list] -> [B:set_default] => [I:config_hint?]`
|
||||
SSDL: `[I:editors_list] -> [B:set_default] => [I:config_hint?]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -5970,13 +5899,11 @@ def render_approve_script_modal(app: App) -> None:
|
||||
#region: Misc Tools
|
||||
|
||||
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
|
||||
per-palette tone mapping (brightness/contrast/gamma).
|
||||
|
||||
SSDL Shape:
|
||||
`[I:palette_combo] -> [B:panel_popout_toggles] -> [I:font_config] -> [I:scale_sliders] -> [I:tone_mapping]`
|
||||
SSDL: `[I:palette_combo] -> [B:panel_popout_toggles] -> [I:font_config] -> [I:scale_sliders] -> [I:tone_mapping]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -6099,13 +6026,13 @@ def render_theme_panel(app: App) -> None:
|
||||
imgui.end()
|
||||
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:
|
||||
"""
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:shader_uniforms] => [I:slider_controls]`
|
||||
SSDL: `[I:shader_uniforms] => [I:slider_controls]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -6114,8 +6041,6 @@ def render_shader_live_editor(app: App) -> None:
|
||||
| Scanline Intensity: [==|=========] 0.20 |
|
||||
| Bloom Threshold: [===|========] 0.30 |
|
||||
+---------------------------------------------------------+
|
||||
|
||||
[C: tests/test_shader_live_editor.py:test_shader_live_editor_renders]
|
||||
"""
|
||||
if app.show_windows.get('Shader Editor', False):
|
||||
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_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:
|
||||
"""
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:static_md_sample] => [I:rendered_markdown]`
|
||||
SSDL: `[I:static_md_sample] => [I:rendered_markdown]`
|
||||
|
||||
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_wrapped(HotReloader.last_error or "Unknown error")
|
||||
|
||||
|
||||
def render_project_stale_tint(app: App) -> None:
|
||||
"""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("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:
|
||||
"""
|
||||
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
|
||||
(markdown-rendered for message/text/content/system labels; plain text otherwise).
|
||||
|
||||
SSDL Shape:
|
||||
`[B:pop_out_viewer] -> [I:label_preview] -> [I:content_child]`
|
||||
SSDL: `[B:pop_out_viewer] -> [I:label_preview] -> [I:content_child]`
|
||||
|
||||
ASCII Layout Map:
|
||||
+---------------------------------------------------------+
|
||||
@@ -6248,7 +6168,6 @@ def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -
|
||||
else:
|
||||
imgui.text_colored(theme.get_color("text"), content)
|
||||
|
||||
|
||||
#endregion: Misc Tools
|
||||
|
||||
#region: MMA
|
||||
@@ -6562,10 +6481,8 @@ def render_mma_global_controls(app: App) -> None:
|
||||
imgui.same_line()
|
||||
if imgui.button("Reload GUI"):
|
||||
success = app._trigger_hot_reload()
|
||||
if success:
|
||||
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'}")
|
||||
if success: 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'}")
|
||||
imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)")
|
||||
|
||||
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()
|
||||
total_cost = 0.0
|
||||
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_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_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_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"):
|
||||
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")
|
||||
@@ -6631,12 +6563,10 @@ def render_mma_usage_section(app: App) -> None:
|
||||
imgui.pop_item_width()
|
||||
|
||||
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.
|
||||
|
||||
SSDL Shape:
|
||||
`[I:ticket_details] -> [B:combo_selectors] => [B:action_buttons]`
|
||||
SSDL: `[I:ticket_details] -> [B:combo_selectors] => [B:action_buttons]`
|
||||
|
||||
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', []))}")
|
||||
personas = getattr(app.controller, 'personas', {}); curr_pers = ticket.get('persona_id', '')
|
||||
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)
|
||||
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()
|
||||
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:
|
||||
"""
|
||||
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:
|
||||
`[I] -> [B:tab_bar] => [I:stream_panels]`
|
||||
SSDL: `[I] -> [B:tab_bar] => [I:stream_panels]`
|
||||
|
||||
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")]:
|
||||
with imscope.tab_item(tier) as (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:
|
||||
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
|
||||
@@ -6753,16 +6686,13 @@ def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) ->
|
||||
for key in tier3_keys:
|
||||
ticket_id = key.split(": ", 1)[-1] if ": " in key else key
|
||||
status = worker_status.get(key, "unknown")
|
||||
if status == "running":
|
||||
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 == "failed":
|
||||
imgui.text_colored(theme.get_color("status_error"), f"{ticket_id} [{status}]")
|
||||
else:
|
||||
imgui.text(f"{ticket_id} [{status}]")
|
||||
if status == "running": 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 == "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)
|
||||
render_selectable_label(app, f'stream_t3_{ticket_id}', app.mma_streams[key], width=-1, multiline=True, height=0)
|
||||
#NOTE(Ed): Exception(Thirdparty)
|
||||
try:
|
||||
if len(app.mma_streams[key]) != app._tier_stream_last_len.get(key, -1):
|
||||
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:
|
||||
imgui.open_popup("Track Proposal")
|
||||
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_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
|
||||
@@ -7069,15 +6999,18 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
|
||||
app._push_mma_state_update()
|
||||
break
|
||||
ed.end_delete()
|
||||
|
||||
# Validate DAG after any changes
|
||||
#TODO(Ed): Exception(Review)
|
||||
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]
|
||||
temp_dag = TrackDAG(ticket_dicts)
|
||||
if temp_dag.has_cycle():
|
||||
imgui.open_popup("Cycle Detected!")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ed.end()
|
||||
# 5. Add Ticket Form
|
||||
imgui.separator()
|
||||
@@ -7089,6 +7022,7 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
|
||||
for t in app.active_tickets:
|
||||
tid = t.get('id', '')
|
||||
if tid.startswith('T-'):
|
||||
#TODO(Ed): Exception(Review)
|
||||
try: max_id = max(max_id, int(tid[2:]))
|
||||
except: pass
|
||||
app.ui_new_ticket_id = f"T-{max_id + 1:03d}"
|
||||
|
||||
Reference in New Issue
Block a user