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:
134
gui_2.py
134
gui_2.py
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user