feat(mma): Implement interception logic in GUI and Conductor
This commit is contained in:
109
gui_2.py
109
gui_2.py
@@ -131,6 +131,29 @@ class MMAApprovalDialog:
|
|||||||
return self._approved, self._payload
|
return self._approved, self._payload
|
||||||
|
|
||||||
|
|
||||||
|
class MMASpawnApprovalDialog:
|
||||||
|
def __init__(self, ticket_id: str, role: str, prompt: str, context_md: str):
|
||||||
|
self._ticket_id = ticket_id
|
||||||
|
self._role = role
|
||||||
|
self._prompt = prompt
|
||||||
|
self._context_md = context_md
|
||||||
|
self._condition = threading.Condition()
|
||||||
|
self._done = False
|
||||||
|
self._approved = False
|
||||||
|
self._abort = False
|
||||||
|
|
||||||
|
def wait(self) -> dict:
|
||||||
|
with self._condition:
|
||||||
|
while not self._done:
|
||||||
|
self._condition.wait(timeout=0.1)
|
||||||
|
return {
|
||||||
|
'approved': self._approved,
|
||||||
|
'abort': self._abort,
|
||||||
|
'prompt': self._prompt,
|
||||||
|
'context_md': self._context_md
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
"""The main ImGui interface orchestrator for Manual Slop."""
|
"""The main ImGui interface orchestrator for Manual Slop."""
|
||||||
|
|
||||||
@@ -246,6 +269,13 @@ class App:
|
|||||||
self._mma_approval_edit_mode = False
|
self._mma_approval_edit_mode = False
|
||||||
self._mma_approval_payload = ""
|
self._mma_approval_payload = ""
|
||||||
|
|
||||||
|
# MMA Spawn approval state
|
||||||
|
self._pending_mma_spawn = None
|
||||||
|
self._mma_spawn_open = False
|
||||||
|
self._mma_spawn_edit_mode = False
|
||||||
|
self._mma_spawn_prompt = ''
|
||||||
|
self._mma_spawn_context = ''
|
||||||
|
|
||||||
# Orchestration State
|
# Orchestration State
|
||||||
self.ui_epic_input = ""
|
self.ui_epic_input = ""
|
||||||
self.proposed_tracks: list[dict] = []
|
self.proposed_tracks: list[dict] = []
|
||||||
@@ -989,6 +1019,21 @@ class App:
|
|||||||
if "dialog_container" in task:
|
if "dialog_container" in task:
|
||||||
task["dialog_container"][0] = dlg
|
task["dialog_container"][0] = dlg
|
||||||
|
|
||||||
|
elif action == "mma_spawn_approval":
|
||||||
|
dlg = MMASpawnApprovalDialog(
|
||||||
|
task.get("ticket_id"),
|
||||||
|
task.get("role"),
|
||||||
|
task.get("prompt"),
|
||||||
|
task.get("context_md")
|
||||||
|
)
|
||||||
|
self._pending_mma_spawn = task
|
||||||
|
self._mma_spawn_prompt = task.get("prompt", "")
|
||||||
|
self._mma_spawn_context = task.get("context_md", "")
|
||||||
|
self._mma_spawn_open = True
|
||||||
|
self._mma_spawn_edit_mode = False
|
||||||
|
if "dialog_container" in task:
|
||||||
|
task["dialog_container"][0] = dlg
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error executing GUI task: {e}")
|
print(f"Error executing GUI task: {e}")
|
||||||
|
|
||||||
@@ -1020,7 +1065,7 @@ class App:
|
|||||||
else:
|
else:
|
||||||
print("[DEBUG] No pending dialog to reject")
|
print("[DEBUG] No pending dialog to reject")
|
||||||
|
|
||||||
def _handle_mma_respond(self, approved: bool, payload: str = None):
|
def _handle_mma_respond(self, approved: bool, payload: str = None, abort: bool = False, prompt: str = None, context_md: str = None):
|
||||||
if self._pending_mma_approval:
|
if self._pending_mma_approval:
|
||||||
dlg = self._pending_mma_approval.get("dialog_container", [None])[0]
|
dlg = self._pending_mma_approval.get("dialog_container", [None])[0]
|
||||||
if dlg:
|
if dlg:
|
||||||
@@ -1032,6 +1077,20 @@ class App:
|
|||||||
dlg._condition.notify_all()
|
dlg._condition.notify_all()
|
||||||
self._pending_mma_approval = None
|
self._pending_mma_approval = None
|
||||||
|
|
||||||
|
if self._pending_mma_spawn:
|
||||||
|
dlg = self._pending_mma_spawn.get("dialog_container", [None])[0]
|
||||||
|
if dlg:
|
||||||
|
with dlg._condition:
|
||||||
|
dlg._approved = approved
|
||||||
|
dlg._abort = abort
|
||||||
|
if prompt is not None:
|
||||||
|
dlg._prompt = prompt
|
||||||
|
if context_md is not None:
|
||||||
|
dlg._context_md = context_md
|
||||||
|
dlg._done = True
|
||||||
|
dlg._condition.notify_all()
|
||||||
|
self._pending_mma_spawn = None
|
||||||
|
|
||||||
def _handle_approve_ask(self):
|
def _handle_approve_ask(self):
|
||||||
"""Responds with approval for a pending /api/ask request."""
|
"""Responds with approval for a pending /api/ask request."""
|
||||||
if not self._ask_request_id: return
|
if not self._ask_request_id: return
|
||||||
@@ -1821,6 +1880,54 @@ class App:
|
|||||||
imgui.close_current_popup()
|
imgui.close_current_popup()
|
||||||
imgui.end_popup()
|
imgui.end_popup()
|
||||||
|
|
||||||
|
# MMA Spawn Approval Modal
|
||||||
|
if self._pending_mma_spawn:
|
||||||
|
if not self._mma_spawn_open:
|
||||||
|
imgui.open_popup("MMA Spawn Approval")
|
||||||
|
self._mma_spawn_open = True
|
||||||
|
self._mma_spawn_edit_mode = False
|
||||||
|
else:
|
||||||
|
self._mma_spawn_open = False
|
||||||
|
|
||||||
|
if imgui.begin_popup_modal("MMA Spawn Approval", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
||||||
|
if not self._pending_mma_spawn:
|
||||||
|
imgui.close_current_popup()
|
||||||
|
else:
|
||||||
|
role = self._pending_mma_spawn.get("role", "??")
|
||||||
|
ticket_id = self._pending_mma_spawn.get("ticket_id", "??")
|
||||||
|
imgui.text(f"Spawning {role} for Ticket {ticket_id}")
|
||||||
|
imgui.separator()
|
||||||
|
|
||||||
|
if self._mma_spawn_edit_mode:
|
||||||
|
imgui.text("Edit Prompt:")
|
||||||
|
_, self._mma_spawn_prompt = imgui.input_text_multiline("##spawn_prompt", self._mma_spawn_prompt, imgui.ImVec2(800, 200))
|
||||||
|
imgui.text("Edit Context MD:")
|
||||||
|
_, self._mma_spawn_context = imgui.input_text_multiline("##spawn_context", self._mma_spawn_context, imgui.ImVec2(800, 300))
|
||||||
|
else:
|
||||||
|
imgui.text("Proposed Prompt:")
|
||||||
|
imgui.begin_child("spawn_prompt_preview", imgui.ImVec2(800, 150), True)
|
||||||
|
imgui.text_unformatted(self._mma_spawn_prompt)
|
||||||
|
imgui.end_child()
|
||||||
|
imgui.text("Proposed Context MD:")
|
||||||
|
imgui.begin_child("spawn_context_preview", imgui.ImVec2(800, 250), True)
|
||||||
|
imgui.text_unformatted(self._mma_spawn_context)
|
||||||
|
imgui.end_child()
|
||||||
|
|
||||||
|
imgui.separator()
|
||||||
|
if imgui.button("Approve", imgui.ImVec2(120, 0)):
|
||||||
|
self._handle_mma_respond(approved=True, prompt=self._mma_spawn_prompt, context_md=self._mma_spawn_context)
|
||||||
|
imgui.close_current_popup()
|
||||||
|
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Edit Mode" if not self._mma_spawn_edit_mode else "Preview Mode", imgui.ImVec2(120, 0)):
|
||||||
|
self._mma_spawn_edit_mode = not self._mma_spawn_edit_mode
|
||||||
|
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Abort", imgui.ImVec2(120, 0)):
|
||||||
|
self._handle_mma_respond(approved=False, abort=True)
|
||||||
|
imgui.close_current_popup()
|
||||||
|
imgui.end_popup()
|
||||||
|
|
||||||
if self.show_script_output:
|
if self.show_script_output:
|
||||||
if self._trigger_script_blink:
|
if self._trigger_script_blink:
|
||||||
self._trigger_script_blink = False
|
self._trigger_script_blink = False
|
||||||
|
|||||||
@@ -219,20 +219,25 @@ def confirm_spawn(role: str, prompt: str, context_md: str, event_queue: events.A
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
if dialog_container[0]:
|
if dialog_container[0]:
|
||||||
approved, final_payload = dialog_container[0].wait()
|
res = dialog_container[0].wait()
|
||||||
|
|
||||||
# Extract modifications from final_payload if it's a dict
|
if isinstance(res, dict):
|
||||||
|
approved = res.get("approved", False)
|
||||||
|
abort = res.get("abort", False)
|
||||||
|
modified_prompt = res.get("prompt", prompt)
|
||||||
|
modified_context = res.get("context_md", context_md)
|
||||||
|
return approved and not abort, modified_prompt, modified_context
|
||||||
|
else:
|
||||||
|
# Fallback for old tuple style if any
|
||||||
|
approved, final_payload = res
|
||||||
modified_prompt = prompt
|
modified_prompt = prompt
|
||||||
modified_context = context_md
|
modified_context = context_md
|
||||||
|
|
||||||
if isinstance(final_payload, dict):
|
if isinstance(final_payload, dict):
|
||||||
modified_prompt = final_payload.get("prompt", prompt)
|
modified_prompt = final_payload.get("prompt", prompt)
|
||||||
modified_context = final_payload.get("context_md", context_md)
|
modified_context = final_payload.get("context_md", context_md)
|
||||||
|
|
||||||
return approved, modified_prompt, modified_context
|
return approved, modified_prompt, modified_context
|
||||||
|
|
||||||
return False, prompt, context_md
|
return False, prompt, context_md
|
||||||
|
|
||||||
def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: List[str] = None, event_queue: events.AsyncEventQueue = None, engine: Optional['ConductorEngine'] = None, md_content: str = ""):
|
def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: List[str] = None, event_queue: events.AsyncEventQueue = None, engine: Optional['ConductorEngine'] = None, md_content: str = ""):
|
||||||
"""
|
"""
|
||||||
Simulates the lifecycle of a single agent working on a ticket.
|
Simulates the lifecycle of a single agent working on a ticket.
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ class MockDialog:
|
|||||||
self.approved = approved
|
self.approved = approved
|
||||||
self.final_payload = final_payload
|
self.final_payload = final_payload
|
||||||
def wait(self):
|
def wait(self):
|
||||||
return self.approved, self.final_payload
|
# Match the new return format: a dictionary
|
||||||
|
res = {'approved': self.approved, 'abort': False}
|
||||||
|
if self.final_payload:
|
||||||
|
res.update(self.final_payload)
|
||||||
|
return res
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_ai_client():
|
def mock_ai_client():
|
||||||
|
|||||||
Reference in New Issue
Block a user