chore(conductor): Mark track 'Curate Provider Registries' as complete. Includes critical fixes for RecursionError, NoneType Hook API responses, and plurality mismatches.
This commit is contained in:
+9
-48
@@ -134,7 +134,6 @@ class AppController:
|
||||
The headless controller for the Manual Slop application.
|
||||
Owns the application state and manages background services.
|
||||
"""
|
||||
PROVIDERS: list[str] = ["gemini", "anthropic", "gemini_cli", "deepseek", "minimax"]
|
||||
|
||||
def __init__(self):
|
||||
# Initialize locks first to avoid initialization order issues
|
||||
@@ -646,8 +645,6 @@ class AppController:
|
||||
}
|
||||
|
||||
def _update_gcli_adapter(self, path: str) -> None:
|
||||
sys.stderr.write(f"[DEBUG] _update_gcli_adapter called with: {path}\n")
|
||||
sys.stderr.flush()
|
||||
if not ai_client._gemini_cli_adapter:
|
||||
ai_client._gemini_cli_adapter = ai_client.GeminiCliAdapter(binary_path=str(path))
|
||||
else:
|
||||
@@ -725,16 +722,12 @@ class AppController:
|
||||
|
||||
if not self._pending_gui_tasks:
|
||||
return
|
||||
sys.stderr.write(f"[DEBUG] _process_pending_gui_tasks: processing {len(self._pending_gui_tasks)} tasks\n")
|
||||
sys.stderr.flush()
|
||||
with self._pending_gui_tasks_lock:
|
||||
tasks = self._pending_gui_tasks[:]
|
||||
self._pending_gui_tasks.clear()
|
||||
for task in tasks:
|
||||
try:
|
||||
action = task.get("action")
|
||||
sys.stderr.write(f"[DEBUG] Processing GUI task: action={action}\n")
|
||||
sys.stderr.flush()
|
||||
if action:
|
||||
session_logger.log_api_hook("PROCESS_TASK", action, str(task))
|
||||
# ...
|
||||
@@ -746,8 +739,6 @@ class AppController:
|
||||
self._tool_log_dirty = True
|
||||
elif action == "set_ai_status":
|
||||
self.ai_status = task.get("payload", "")
|
||||
sys.stderr.write(f"[DEBUG] Updated ai_status via task to: {self.ai_status}\n")
|
||||
sys.stderr.flush()
|
||||
elif action == "set_mma_status":
|
||||
self.mma_status = task.get("payload", "")
|
||||
elif action == "handle_ai_response":
|
||||
@@ -775,8 +766,6 @@ class AppController:
|
||||
else:
|
||||
self.ai_response = text
|
||||
self.ai_status = payload.get("status", "done")
|
||||
sys.stderr.write(f"[DEBUG] Updated ai_status to: {self.ai_status}\n")
|
||||
sys.stderr.flush()
|
||||
self._trigger_blink = True
|
||||
if not stream_id:
|
||||
self._token_stats_dirty = True
|
||||
@@ -799,9 +788,6 @@ class AppController:
|
||||
if not isinstance(p, dict):
|
||||
p = task # Fallback to task itself if payload is missing or wrong type
|
||||
|
||||
sys.stderr.write(f"[DEBUG] mma_state_update: status={p.get('status')} active_tier={p.get('active_tier')}\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
track_data = p.get("track")
|
||||
is_active_track = False
|
||||
if track_data and self.active_track and track_data.get("id") == self.active_track.id:
|
||||
@@ -853,20 +839,14 @@ class AppController:
|
||||
elif action == "set_value":
|
||||
item = task.get("item")
|
||||
value = task.get("value")
|
||||
sys.stderr.write(f"[DEBUG] Processing set_value: {item}={value}\n")
|
||||
sys.stderr.flush()
|
||||
if item in self._settable_fields:
|
||||
attr_name = self._settable_fields[item]
|
||||
setattr(self, attr_name, value)
|
||||
sys.stderr.write(f"[DEBUG] Set {attr_name} to {value}\n")
|
||||
sys.stderr.flush()
|
||||
if item == "gcli_path":
|
||||
self._update_gcli_adapter(str(value))
|
||||
elif action == "click":
|
||||
item = task.get("item")
|
||||
user_data = task.get("user_data")
|
||||
sys.stderr.write(f"[DEBUG] Processing click: {item} (user_data={user_data})\n")
|
||||
sys.stderr.flush()
|
||||
if item == "btn_project_new_automated":
|
||||
self._cb_new_project_automated(user_data)
|
||||
elif item == "btn_mma_load_track":
|
||||
@@ -1492,7 +1472,7 @@ class AppController:
|
||||
|
||||
def do_fetch() -> None:
|
||||
try:
|
||||
for p in self.PROVIDERS:
|
||||
for p in models.PROVIDERS:
|
||||
try:
|
||||
self.all_available_models[p] = ai_client.list_models(p)
|
||||
except Exception as e:
|
||||
@@ -1512,14 +1492,10 @@ class AppController:
|
||||
|
||||
def start_services(self, app: Any = None):
|
||||
"""Starts background threads."""
|
||||
sys.stderr.write("[DEBUG] AppController.start_services called\n")
|
||||
sys.stderr.flush()
|
||||
self._prune_old_logs()
|
||||
self._init_ai_and_hooks(app)
|
||||
self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
|
||||
self._loop_thread.start()
|
||||
sys.stderr.write(f"[DEBUG] _loop_thread started: {self._loop_thread.ident}\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""Stops background threads and cleans up resources."""
|
||||
@@ -1568,13 +1544,8 @@ class AppController:
|
||||
|
||||
def _process_event_queue(self) -> None:
|
||||
"""Listens for and processes events from the SyncEventQueue."""
|
||||
sys.stderr.write("[DEBUG] _process_event_queue entered\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
while True:
|
||||
event_name, payload = self.event_queue.get()
|
||||
sys.stderr.write(f"[DEBUG] _process_event_queue got event: {event_name} with payload: {str(payload)[:100]}\n")
|
||||
sys.stderr.flush()
|
||||
if event_name == "shutdown":
|
||||
break
|
||||
if event_name == "user_request":
|
||||
@@ -1641,8 +1612,6 @@ class AppController:
|
||||
ai_client.set_model_params(self.temperature, self.max_tokens, self.history_trunc_limit, self.top_p)
|
||||
ai_client.set_agent_tools(self.ui_agent_tools) # Force update adapter path right before send to bypass potential duplication issues
|
||||
self._update_gcli_adapter(self.ui_gemini_cli_path)
|
||||
sys.stderr.write(f"[DEBUG] Calling ai_client.send with provider={ai_client.get_provider()}, model={self.current_model}, gcli_path={self.ui_gemini_cli_path}\n")
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
resp = ai_client.send(
|
||||
event.stable_md,
|
||||
@@ -1659,12 +1628,8 @@ class AppController:
|
||||
)
|
||||
self.event_queue.put("response", {"text": resp, "status": "done", "role": "AI"})
|
||||
except ai_client.ProviderError as e:
|
||||
sys.stderr.write(f"[DEBUG] _handle_request_event ai_client.ProviderError: {e.ui_message()}\n")
|
||||
sys.stderr.flush()
|
||||
self.event_queue.put("response", {"text": e.ui_message(), "status": "error", "role": "Vendor API"})
|
||||
except Exception as e:
|
||||
sys.stderr.write(f"[DEBUG] _handle_request_event ERROR: {e}\n{traceback.format_exc()}\n")
|
||||
sys.stderr.flush()
|
||||
self.event_queue.put("response", {"text": f"ERROR: {e}", "status": "error", "role": "System"})
|
||||
|
||||
def _offload_entry_payload(self, entry: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@@ -1806,17 +1771,11 @@ class AppController:
|
||||
})
|
||||
|
||||
def _confirm_and_run(self, script: str, base_dir: str, qa_callback: Optional[Callable[[str], str]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> Optional[str]:
|
||||
sys.stderr.write(f"[DEBUG] _confirm_and_run called. test_hooks={self.test_hooks_enabled}, manual_approve={getattr(self, 'ui_manual_approve', False)}\n")
|
||||
sys.stderr.flush()
|
||||
if self.test_hooks_enabled and not getattr(self, "ui_manual_approve", False):
|
||||
sys.stderr.write("[DEBUG] Auto-approving script.\n")
|
||||
sys.stderr.flush()
|
||||
self._set_status("running powershell...")
|
||||
output = shell_runner.run_powershell(script, base_dir, qa_callback=qa_callback, patch_callback=patch_callback)
|
||||
self._set_status("powershell done, awaiting AI...")
|
||||
return output
|
||||
sys.stderr.write("[DEBUG] Creating ConfirmDialog.\n")
|
||||
sys.stderr.flush()
|
||||
dialog = ConfirmDialog(script, base_dir)
|
||||
is_headless = "--headless" in sys.argv
|
||||
if is_headless:
|
||||
@@ -1834,13 +1793,7 @@ class AppController:
|
||||
"base_dir": str(base_dir),
|
||||
"ts": time.time()
|
||||
})
|
||||
sys.stderr.write(f"[DEBUG] Appended script_confirmation_required to _api_event_queue. ID={dialog._uid}\n")
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write(f"[DEBUG] Waiting for dialog ID={dialog._uid}...\n")
|
||||
sys.stderr.flush()
|
||||
approved, final_script = dialog.wait()
|
||||
sys.stderr.write(f"[DEBUG] Dialog ID={dialog._uid} finished wait. approved={approved}\n")
|
||||
sys.stderr.flush()
|
||||
if is_headless:
|
||||
with self._pending_dialog_lock:
|
||||
if dialog._uid in self._pending_actions:
|
||||
@@ -1912,6 +1865,14 @@ class AppController:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def _pending_mma_spawn(self) -> Optional[Dict[str, Any]]:
|
||||
return self._pending_mma_spawns[0] if self._pending_mma_spawns else None
|
||||
|
||||
@property
|
||||
def _pending_mma_approval(self) -> Optional[Dict[str, Any]]:
|
||||
return self._pending_mma_approvals[0] if self._pending_mma_approvals else None
|
||||
|
||||
@property
|
||||
def current_provider(self) -> str:
|
||||
return self._current_provider
|
||||
|
||||
Reference in New Issue
Block a user