feat(mma): complete Phase 6 and finalize Comprehensive GUI UX track

- Implement Live Worker Streaming: wire ai_client.comms_log_callback to Tier 3 streams
- Add Parallel DAG Execution using asyncio.gather for non-dependent tickets
- Implement Automatic Retry with Model Escalation (Flash-Lite -> Flash -> Pro)
- Add Tier Model Configuration UI to MMA Dashboard with project TOML persistence
- Fix FPS reporting in PerformanceMonitor to prevent transient 0.0 values
- Update Ticket model with retry_count and dictionary-like access
- Stabilize Gemini CLI integration tests and handle script approval events in simulations
- Finalize and verify all 6 phases of the implementation plan
This commit is contained in:
2026-03-01 22:38:43 -05:00
parent d1ce0eaaeb
commit 9fb01ce5d1
22 changed files with 756 additions and 498 deletions

134
gui_2.py
View File

@@ -879,13 +879,21 @@ class App:
payload = task.get("payload", {})
text = payload.get("text", "")
stream_id = payload.get("stream_id")
is_streaming = payload.get("status") == "streaming..."
if stream_id:
self.mma_streams[stream_id] = text
if is_streaming:
if stream_id not in self.mma_streams: self.mma_streams[stream_id] = ""
self.mma_streams[stream_id] += text
else:
self.mma_streams[stream_id] = text
if stream_id == "Tier 1":
if "status" in payload:
self.ai_status = payload["status"]
else:
self.ai_response = text
if is_streaming:
self.ai_response += text
else:
self.ai_response = text
self.ai_status = payload.get("status", "done")
self._trigger_blink = True
if self.ui_auto_add_history and not stream_id:
@@ -1239,7 +1247,15 @@ class App:
ai_client.set_model_params(self.temperature, self.max_tokens, self.history_trunc_limit)
ai_client.set_agent_tools(self.ui_agent_tools)
try:
resp = ai_client.send(event.stable_md, event.prompt, event.base_dir, event.file_items, event.disc_text)
resp = ai_client.send(
event.stable_md,
event.prompt,
event.base_dir,
event.file_items,
event.disc_text,
pre_tool_callback=self._confirm_and_run,
qa_callback=ai_client.run_tier4_analysis
)
# Emit response event
asyncio.run_coroutine_threadsafe(
self.event_queue.put("response", {"text": resp, "status": "done"}),
@@ -2759,7 +2775,9 @@ class App:
def _cb_create_track(self, name: str, desc: str, track_type: str) -> None:
if not name: return
track_id = name.lower().replace(" ", "_")
from datetime import datetime
date_suffix = datetime.now().strftime("%Y%m%d")
track_id = f"{name.lower().replace(' ', '_')}_{date_suffix}"
track_dir = Path("conductor/tracks") / track_id
track_dir.mkdir(parents=True, exist_ok=True)
spec_file = track_dir / "spec.md"
@@ -2776,7 +2794,7 @@ class App:
"title": name,
"description": desc,
"type": track_type,
"status": "proposed",
"status": "new",
"progress": 0.0
}, f, indent=1)
# Refresh tracks from disk
@@ -3072,6 +3090,19 @@ class App:
imgui.text(f"${total_cost:,.4f}")
imgui.end_table()
imgui.separator()
# 3b. Tier Model Config
if imgui.collapsing_header("Tier Model Config"):
for tier in self.mma_tier_usage.keys():
imgui.text(f"{tier}:")
imgui.same_line()
current_model = self.mma_tier_usage[tier].get("model", "unknown")
if imgui.begin_combo(f"##combo_{tier}", current_model):
for model in self.available_models:
if imgui.selectable(model, current_model == model)[0]:
self.mma_tier_usage[tier]["model"] = model
self.project.setdefault("mma", {}).setdefault("tier_models", {})[tier] = model
imgui.end_combo()
imgui.separator()
# 4. Task DAG Visualizer
imgui.text("Task DAG")
if self.active_track:
@@ -3170,6 +3201,9 @@ class App:
def _render_ticket_dag_node(self, ticket: Ticket, tickets_by_id: dict[str, Ticket], children_map: dict[str, list[str]], rendered: set[str]) -> None:
tid = ticket.get('id', '??')
is_duplicate = tid in rendered
if not is_duplicate:
rendered.add(tid)
target = ticket.get('target_file', 'general')
status = ticket.get('status', 'pending').upper()
status_color = vec4(178, 178, 178)
@@ -3182,14 +3216,13 @@ class App:
elif status == 'PAUSED':
status_color = vec4(255, 165, 0)
p_min = imgui.get_cursor_screen_pos()
p_max = imgui.ImVec2(p_min.x + 4, p_min.y + imgui.get_text_line_height_with_spacing())
p_max = imgui.ImVec2(p_min.x + 4, p_min.y + imgui.get_text_line_height())
imgui.get_window_draw_list().add_rect_filled(p_min, p_max, imgui.get_color_u32(status_color))
imgui.set_cursor_screen_pos(imgui.ImVec2(p_min.x + 8, p_min.y))
flags = imgui.TreeNodeFlags_.open_on_arrow | imgui.TreeNodeFlags_.open_on_double_click | imgui.TreeNodeFlags_.default_open
children = children_map.get(tid, [])
if not children:
if not children or is_duplicate:
flags |= imgui.TreeNodeFlags_.leaf
is_duplicate = tid in rendered
node_open = imgui.tree_node_ex(f"##{tid}", flags)
if imgui.is_item_hovered():
imgui.begin_tooltip()
@@ -3228,84 +3261,15 @@ class App:
if tid in deps:
t['depends_on'] = [d for d in deps if d != tid]
self._push_mma_state_update()
if node_open:
if not is_duplicate:
rendered.add(tid)
for child_id in children:
child = tickets_by_id.get(child_id)
if child:
self._render_ticket_dag_node(child, tickets_by_id, children_map, rendered)
else:
imgui.text_disabled(" (shown above)")
if is_duplicate:
imgui.same_line()
imgui.text_disabled("(shown above)")
if node_open and not is_duplicate:
for child_id in children:
child = tickets_by_id.get(child_id)
if child:
self._render_ticket_dag_node(child, tickets_by_id, children_map, rendered)
imgui.tree_pop()
if imgui.button("Clear##tc"):
self._tool_log.clear()
imgui.separator()
imgui.begin_child("tc_scroll", imgui.ImVec2(0, 0), False, imgui.WindowFlags_.horizontal_scrollbar)
log_copy = list(self._tool_log)
for idx_minus_one, entry in enumerate(log_copy):
idx = idx_minus_one + 1
# Handle both old (tuple) and new (tuple with ts) entries
if len(entry) == 3:
script, result, local_ts = entry
else:
script, result = entry
local_ts = 0
# Blink effect
blink_alpha = 0.0
if local_ts > 0:
elapsed = time.time() - local_ts
if elapsed < 3.0:
blink_alpha = (1.0 - (elapsed / 3.0)) * 0.3 * (math.sin(elapsed * 10) * 0.5 + 0.5)
imgui.push_id(f"tc_entry_{idx}")
if blink_alpha > 0:
imgui.push_style_color(imgui.Col_.child_bg, vec4(0, 255, 0, blink_alpha))
imgui.begin_group()
first_line = script.strip().splitlines()[0][:80] if script.strip() else "(empty)"
imgui.text_colored(C_KEY, f"Call #{idx}: {first_line}")
# Script Display
imgui.text_colored(C_LBL, "Script:")
imgui.same_line()
if imgui.button(f"[+]##script_{idx}"):
self.show_text_viewer = True
self.text_viewer_title = f"Call Script #{idx}"
self.text_viewer_content = script
if self.ui_word_wrap:
imgui.begin_child(f"tc_script_wrap_{idx}", imgui.ImVec2(-1, 72), True)
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
imgui.text(script)
imgui.pop_text_wrap_pos()
imgui.end_child()
else:
imgui.begin_child(f"tc_script_fixed_width_{idx}", imgui.ImVec2(0, 72), True, imgui.WindowFlags_.horizontal_scrollbar)
imgui.input_text_multiline(f"##tc_script_res_{idx}", script, imgui.ImVec2(-1, -1), imgui.InputTextFlags_.read_only)
imgui.end_child()
# Result Display
imgui.text_colored(C_LBL, "Output:")
imgui.same_line()
if imgui.button(f"[+]##output_{idx}"):
self.show_text_viewer = True
self.text_viewer_title = f"Call Output #{idx}"
self.text_viewer_content = result
if self.ui_word_wrap:
imgui.begin_child(f"tc_res_wrap_{idx}", imgui.ImVec2(-1, 72), True)
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
imgui.text(result)
imgui.pop_text_wrap_pos()
imgui.end_child()
else:
imgui.begin_child(f"tc_res_fixed_width_{idx}", imgui.ImVec2(0, 72), True, imgui.WindowFlags_.horizontal_scrollbar)
imgui.input_text_multiline(f"##tc_res_val_{idx}", result, imgui.ImVec2(-1, -1), imgui.InputTextFlags_.read_only)
imgui.end_child()
imgui.separator()
if blink_alpha > 0:
imgui.end_group()
imgui.pop_style_color()
imgui.pop_id()
if self._scroll_tool_calls_to_bottom:
imgui.set_scroll_here_y(1.0)
self._scroll_tool_calls_to_bottom = False
imgui.end_child()
def _render_comms_history_panel(self) -> None:
imgui.text_colored(vec4(200, 220, 160), f"Status: {self.ai_status}")