WIP: PAIN3
This commit is contained in:
@@ -8,5 +8,5 @@ active = "main"
|
|||||||
|
|
||||||
[discussions.main]
|
[discussions.main]
|
||||||
git_commit = ""
|
git_commit = ""
|
||||||
last_updated = "2026-03-05T14:39:44"
|
last_updated = "2026-03-05T14:42:31"
|
||||||
history = []
|
history = []
|
||||||
|
|||||||
@@ -290,7 +290,11 @@ def reset_session() -> None:
|
|||||||
_gemini_cache = None
|
_gemini_cache = None
|
||||||
_gemini_cache_md_hash = None
|
_gemini_cache_md_hash = None
|
||||||
_gemini_cache_created_at = None
|
_gemini_cache_created_at = None
|
||||||
_gemini_cli_adapter = None
|
|
||||||
|
# Preserve binary_path if adapter exists
|
||||||
|
old_path = _gemini_cli_adapter.binary_path if _gemini_cli_adapter else "gemini"
|
||||||
|
_gemini_cli_adapter = GeminiCliAdapter(binary_path=old_path)
|
||||||
|
|
||||||
_anthropic_client = None
|
_anthropic_client = None
|
||||||
|
|
||||||
with _anthropic_history_lock:
|
with _anthropic_history_lock:
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ class ApiHookClient:
|
|||||||
return {"performance": diag}
|
return {"performance": diag}
|
||||||
|
|
||||||
def get_mma_status(self) -> dict[str, Any]:
|
def get_mma_status(self) -> dict[str, Any]:
|
||||||
"""Convenience to get the current MMA engine status. Returns FULL state."""
|
"""Retrieves the dedicated MMA engine status."""
|
||||||
return self.get_gui_state()
|
return self._make_request('GET', '/api/gui/mma_status') or {}
|
||||||
|
|
||||||
def get_node_status(self, node_id: str) -> dict[str, Any]:
|
def get_node_status(self, node_id: str) -> dict[str, Any]:
|
||||||
"""Retrieves status for a specific node in the MMA DAG."""
|
"""Retrieves status for a specific node in the MMA DAG."""
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ class AppController:
|
|||||||
'show_confirm_modal': 'show_confirm_modal',
|
'show_confirm_modal': 'show_confirm_modal',
|
||||||
'mma_epic_input': 'ui_epic_input',
|
'mma_epic_input': 'ui_epic_input',
|
||||||
'mma_status': 'mma_status',
|
'mma_status': 'mma_status',
|
||||||
|
'ai_status': 'ai_status',
|
||||||
'mma_active_tier': 'active_tier',
|
'mma_active_tier': 'active_tier',
|
||||||
'ui_new_track_name': 'ui_new_track_name',
|
'ui_new_track_name': 'ui_new_track_name',
|
||||||
'ui_new_track_desc': 'ui_new_track_desc',
|
'ui_new_track_desc': 'ui_new_track_desc',
|
||||||
@@ -409,10 +410,10 @@ class AppController:
|
|||||||
"collapsed": False,
|
"collapsed": False,
|
||||||
"ts": project_manager.now_ts()
|
"ts": project_manager.now_ts()
|
||||||
})
|
})
|
||||||
elif action == "mma_stream_append":
|
elif action in ("mma_stream", "mma_stream_append"):
|
||||||
payload = task.get("payload", {})
|
# Some events might have these at top level, some in a 'payload' dict
|
||||||
stream_id = payload.get("stream_id")
|
stream_id = task.get("stream_id") or task.get("payload", {}).get("stream_id")
|
||||||
text = payload.get("text", "")
|
text = task.get("text") or task.get("payload", {}).get("text", "")
|
||||||
if stream_id:
|
if stream_id:
|
||||||
if stream_id not in self.mma_streams:
|
if stream_id not in self.mma_streams:
|
||||||
self.mma_streams[stream_id] = ""
|
self.mma_streams[stream_id] = ""
|
||||||
@@ -421,7 +422,11 @@ class AppController:
|
|||||||
self.proposed_tracks = task.get("payload", [])
|
self.proposed_tracks = task.get("payload", [])
|
||||||
self._show_track_proposal_modal = True
|
self._show_track_proposal_modal = True
|
||||||
elif action == "mma_state_update":
|
elif action == "mma_state_update":
|
||||||
payload = task.get("payload", {})
|
# Handle both internal (nested) and hook-server (flattened) payloads
|
||||||
|
payload = task.get("payload")
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
payload = task # Fallback to task if payload missing or wrong type
|
||||||
|
|
||||||
self.mma_status = payload.get("status", "idle")
|
self.mma_status = payload.get("status", "idle")
|
||||||
self.active_tier = payload.get("active_tier")
|
self.active_tier = payload.get("active_tier")
|
||||||
self.mma_tier_usage = payload.get("tier_usage", self.mma_tier_usage)
|
self.mma_tier_usage = payload.get("tier_usage", self.mma_tier_usage)
|
||||||
@@ -783,12 +788,11 @@ class AppController:
|
|||||||
break
|
break
|
||||||
if event_name == "user_request":
|
if event_name == "user_request":
|
||||||
threading.Thread(target=self._handle_request_event, args=(payload,), daemon=True).start()
|
threading.Thread(target=self._handle_request_event, args=(payload,), daemon=True).start()
|
||||||
elif event_name == "response":
|
elif event_name == "gui_task":
|
||||||
with self._pending_gui_tasks_lock:
|
with self._pending_gui_tasks_lock:
|
||||||
self._pending_gui_tasks.append({
|
# Directly append the task from the hook server.
|
||||||
"action": "handle_ai_response",
|
# It already contains 'action' and any necessary fields.
|
||||||
"payload": payload
|
self._pending_gui_tasks.append(payload)
|
||||||
})
|
|
||||||
elif event_name == "mma_state_update":
|
elif event_name == "mma_state_update":
|
||||||
with self._pending_gui_tasks_lock:
|
with self._pending_gui_tasks_lock:
|
||||||
self._pending_gui_tasks.append({
|
self._pending_gui_tasks.append({
|
||||||
@@ -803,6 +807,7 @@ class AppController:
|
|||||||
})
|
})
|
||||||
elif event_name in ("mma_spawn_approval", "mma_step_approval"):
|
elif event_name in ("mma_spawn_approval", "mma_step_approval"):
|
||||||
with self._pending_gui_tasks_lock:
|
with self._pending_gui_tasks_lock:
|
||||||
|
# These payloads already contain the 'action' field
|
||||||
self._pending_gui_tasks.append(payload)
|
self._pending_gui_tasks.append(payload)
|
||||||
|
|
||||||
def _handle_request_event(self, event: events.UserRequestEvent) -> None:
|
def _handle_request_event(self, event: events.UserRequestEvent) -> None:
|
||||||
@@ -1679,13 +1684,17 @@ class AppController:
|
|||||||
def _cb_accept_tracks(self) -> None:
|
def _cb_accept_tracks(self) -> None:
|
||||||
self._show_track_proposal_modal = False
|
self._show_track_proposal_modal = False
|
||||||
def _bg_task() -> None:
|
def _bg_task() -> None:
|
||||||
|
sys.stderr.write("[DEBUG] _cb_accept_tracks _bg_task started\n")
|
||||||
# Generate skeletons once
|
# Generate skeletons once
|
||||||
self._set_status("Phase 2: Generating skeletons for all tracks...")
|
self._set_status("Phase 2: Generating skeletons for all tracks...")
|
||||||
parser = file_cache.ASTParser(language="python")
|
sys.stderr.write("[DEBUG] Creating ASTParser...\n")
|
||||||
|
parser = ASTParser(language="python")
|
||||||
|
|
||||||
generated_skeletons = ""
|
generated_skeletons = ""
|
||||||
try:
|
try:
|
||||||
# Use a local copy of files to avoid concurrent modification issues
|
# Use a local copy of files to avoid concurrent modification issues
|
||||||
files_to_scan = list(self.files)
|
files_to_scan = list(self.files)
|
||||||
|
sys.stderr.write(f"[DEBUG] Scanning {len(files_to_scan)} files for skeletons...\n")
|
||||||
for i, file_path in enumerate(files_to_scan):
|
for i, file_path in enumerate(files_to_scan):
|
||||||
try:
|
try:
|
||||||
self._set_status(f"Phase 2: Scanning files ({i+1}/{len(files_to_scan)})...")
|
self._set_status(f"Phase 2: Scanning files ({i+1}/{len(files_to_scan)})...")
|
||||||
@@ -1695,12 +1704,13 @@ class AppController:
|
|||||||
code = f.read()
|
code = f.read()
|
||||||
generated_skeletons += f"\nFile: {file_path}\n{parser.get_skeleton(code)}\n"
|
generated_skeletons += f"\nFile: {file_path}\n{parser.get_skeleton(code)}\n"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error parsing skeleton for {file_path}: {e}")
|
sys.stderr.write(f"[DEBUG] Error parsing skeleton for {file_path}: {e}\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
sys.stderr.write(f"[DEBUG] Error in scan loop: {e}\n")
|
||||||
self._set_status(f"Error generating skeletons: {e}")
|
self._set_status(f"Error generating skeletons: {e}")
|
||||||
print(f"Error generating skeletons: {e}")
|
|
||||||
return # Exit if skeleton generation fails
|
return # Exit if skeleton generation fails
|
||||||
|
|
||||||
|
sys.stderr.write("[DEBUG] Skeleton generation complete. Starting tracks...\n")
|
||||||
# Now loop through tracks and call _start_track_logic with generated skeletons
|
# Now loop through tracks and call _start_track_logic with generated skeletons
|
||||||
total_tracks = len(self.proposed_tracks)
|
total_tracks = len(self.proposed_tracks)
|
||||||
for i, track_data in enumerate(self.proposed_tracks):
|
for i, track_data in enumerate(self.proposed_tracks):
|
||||||
@@ -1708,6 +1718,7 @@ class AppController:
|
|||||||
self._set_status(f"Processing track {i+1} of {total_tracks}: '{title}'...")
|
self._set_status(f"Processing track {i+1} of {total_tracks}: '{title}'...")
|
||||||
self._start_track_logic(track_data, skeletons_str=generated_skeletons) # Pass skeletons
|
self._start_track_logic(track_data, skeletons_str=generated_skeletons) # Pass skeletons
|
||||||
|
|
||||||
|
sys.stderr.write("[DEBUG] All tracks started. Refreshing...\n")
|
||||||
with self._pending_gui_tasks_lock:
|
with self._pending_gui_tasks_lock:
|
||||||
self._pending_gui_tasks.append({'action': 'refresh_from_project'}) # Ensure UI refresh after tracks are started
|
self._pending_gui_tasks.append({'action': 'refresh_from_project'}) # Ensure UI refresh after tracks are started
|
||||||
self._set_status(f"All {total_tracks} tracks accepted and execution started.")
|
self._set_status(f"All {total_tracks} tracks accepted and execution started.")
|
||||||
@@ -1797,6 +1808,12 @@ class AppController:
|
|||||||
meta = models.Metadata(id=track_id, name=title, status="todo", created_at=datetime.now(), updated_at=datetime.now())
|
meta = models.Metadata(id=track_id, name=title, status="todo", created_at=datetime.now(), updated_at=datetime.now())
|
||||||
state = models.TrackState(metadata=meta, discussion=[], tasks=tickets)
|
state = models.TrackState(metadata=meta, discussion=[], tasks=tickets)
|
||||||
project_manager.save_track_state(track_id, state, self.ui_files_base_dir)
|
project_manager.save_track_state(track_id, state, self.ui_files_base_dir)
|
||||||
|
|
||||||
|
# Add to memory and notify UI
|
||||||
|
self.tracks.append({"id": track_id, "title": title, "status": "todo"})
|
||||||
|
with self._pending_gui_tasks_lock:
|
||||||
|
self._pending_gui_tasks.append({'action': 'refresh_from_project'})
|
||||||
|
|
||||||
# 4. Initialize ConductorEngine and run loop
|
# 4. Initialize ConductorEngine and run loop
|
||||||
engine = multi_agent_conductor.ConductorEngine(track, self.event_queue, auto_queue=not self.mma_step_mode)
|
engine = multi_agent_conductor.ConductorEngine(track, self.event_queue, auto_queue=not self.mma_step_mode)
|
||||||
# Use current full markdown context for the track execution
|
# Use current full markdown context for the track execution
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import json
|
|||||||
from typing import Any, Optional, TYPE_CHECKING, Union
|
from typing import Any, Optional, TYPE_CHECKING, Union
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from models import TrackState
|
from src.models import TrackState
|
||||||
TS_FMT: str = "%Y-%m-%dT%H:%M:%S"
|
TS_FMT: str = "%Y-%m-%dT%H:%M:%S"
|
||||||
|
|
||||||
def now_ts() -> str:
|
def now_ts() -> str:
|
||||||
@@ -248,7 +248,7 @@ def load_track_state(track_id: str, base_dir: Union[str, Path] = ".") -> Optiona
|
|||||||
"""
|
"""
|
||||||
Loads a TrackState object from conductor/tracks/<track_id>/state.toml.
|
Loads a TrackState object from conductor/tracks/<track_id>/state.toml.
|
||||||
"""
|
"""
|
||||||
from models import TrackState
|
from src.models import TrackState
|
||||||
state_file = Path(base_dir) / "conductor" / "tracks" / track_id / "state.toml"
|
state_file = Path(base_dir) / "conductor" / "tracks" / track_id / "state.toml"
|
||||||
if not state_file.exists():
|
if not state_file.exists():
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
from models import Ticket, Track, WorkerContext
|
from src.models import Ticket, Track, WorkerContext
|
||||||
import ai_client
|
from src import ai_client
|
||||||
|
|
||||||
# These tests define the expected interface for multi_agent_conductor.py
|
# These tests define the expected interface for multi_agent_conductor.py
|
||||||
# which will be implemented in the next phase of TDD.
|
# which will be implemented in the next phase of TDD.
|
||||||
@@ -11,7 +11,7 @@ def test_conductor_engine_initialization() -> None:
|
|||||||
Test that ConductorEngine can be initialized with a Track.
|
Test that ConductorEngine can be initialized with a Track.
|
||||||
"""
|
"""
|
||||||
track = Track(id="test_track", description="Test Track")
|
track = Track(id="test_track", description="Test Track")
|
||||||
from multi_agent_conductor import ConductorEngine
|
from src.multi_agent_conductor import ConductorEngine
|
||||||
engine = ConductorEngine(track=track, auto_queue=True)
|
engine = ConductorEngine(track=track, auto_queue=True)
|
||||||
assert engine.track == track
|
assert engine.track == track
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ async def test_conductor_engine_run_executes_tickets_in_order(monkeypatch: pytes
|
|||||||
ticket1 = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
ticket1 = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
||||||
ticket2 = Ticket(id="T2", description="Task 2", status="todo", assigned_to="worker2", depends_on=["T1"])
|
ticket2 = Ticket(id="T2", description="Task 2", status="todo", assigned_to="worker2", depends_on=["T1"])
|
||||||
track = Track(id="track1", description="Track 1", tickets=[ticket1, ticket2])
|
track = Track(id="track1", description="Track 1", tickets=[ticket1, ticket2])
|
||||||
from multi_agent_conductor import ConductorEngine
|
from src.multi_agent_conductor import ConductorEngine
|
||||||
engine = ConductorEngine(track=track, auto_queue=True)
|
engine = ConductorEngine(track=track, auto_queue=True)
|
||||||
|
|
||||||
vlogger.log_state("Ticket Count", 0, 2)
|
vlogger.log_state("Ticket Count", 0, 2)
|
||||||
@@ -34,7 +34,7 @@ async def test_conductor_engine_run_executes_tickets_in_order(monkeypatch: pytes
|
|||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
# We mock run_worker_lifecycle as it is expected to be in the same module
|
# We mock run_worker_lifecycle as it is expected to be in the same module
|
||||||
with patch("multi_agent_conductor.run_worker_lifecycle") as mock_lifecycle:
|
with patch("src.multi_agent_conductor.run_worker_lifecycle") as mock_lifecycle:
|
||||||
# Mocking lifecycle to mark ticket as complete so dependencies can be resolved
|
# Mocking lifecycle to mark ticket as complete so dependencies can be resolved
|
||||||
|
|
||||||
def side_effect(ticket, context, *args, **kwargs):
|
def side_effect(ticket, context, *args, **kwargs):
|
||||||
@@ -64,7 +64,7 @@ async def test_run_worker_lifecycle_calls_ai_client_send(monkeypatch: pytest.Mon
|
|||||||
"""
|
"""
|
||||||
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
||||||
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
||||||
from multi_agent_conductor import run_worker_lifecycle
|
from src.multi_agent_conductor import run_worker_lifecycle
|
||||||
# Mock ai_client.send using monkeypatch
|
# Mock ai_client.send using monkeypatch
|
||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
@@ -86,12 +86,12 @@ async def test_run_worker_lifecycle_context_injection(monkeypatch: pytest.Monkey
|
|||||||
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
||||||
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
||||||
context_files = ["primary.py", "secondary.py"]
|
context_files = ["primary.py", "secondary.py"]
|
||||||
from multi_agent_conductor import run_worker_lifecycle
|
from src.multi_agent_conductor import run_worker_lifecycle
|
||||||
# Mock ai_client.send using monkeypatch
|
# Mock ai_client.send using monkeypatch
|
||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
# We mock ASTParser which is expected to be imported in multi_agent_conductor
|
# We mock ASTParser which is expected to be imported in multi_agent_conductor
|
||||||
with patch("multi_agent_conductor.ASTParser") as mock_ast_parser_class, \
|
with patch("src.multi_agent_conductor.ASTParser") as mock_ast_parser_class, \
|
||||||
patch("builtins.open", new_callable=MagicMock) as mock_open:
|
patch("builtins.open", new_callable=MagicMock) as mock_open:
|
||||||
# Setup open mock to return different content for different files
|
# Setup open mock to return different content for different files
|
||||||
file_contents = {
|
file_contents = {
|
||||||
@@ -131,7 +131,7 @@ async def test_run_worker_lifecycle_handles_blocked_response(monkeypatch: pytest
|
|||||||
"""
|
"""
|
||||||
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1")
|
||||||
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
||||||
from multi_agent_conductor import run_worker_lifecycle
|
from src.multi_agent_conductor import run_worker_lifecycle
|
||||||
# Mock ai_client.send using monkeypatch
|
# Mock ai_client.send using monkeypatch
|
||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
@@ -150,14 +150,14 @@ async def test_run_worker_lifecycle_step_mode_confirmation(monkeypatch: pytest.M
|
|||||||
"""
|
"""
|
||||||
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1", step_mode=True)
|
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1", step_mode=True)
|
||||||
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
||||||
from multi_agent_conductor import run_worker_lifecycle
|
from src.multi_agent_conductor import run_worker_lifecycle
|
||||||
# Mock ai_client.send using monkeypatch
|
# Mock ai_client.send using monkeypatch
|
||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
|
|
||||||
# Important: confirm_spawn is called first if event_queue is present!
|
# Important: confirm_spawn is called first if event_queue is present!
|
||||||
with patch("multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
with patch("src.multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
||||||
patch("multi_agent_conductor.confirm_execution") as mock_confirm:
|
patch("src.multi_agent_conductor.confirm_execution") as mock_confirm:
|
||||||
mock_spawn.return_value = (True, "mock prompt", "mock context")
|
mock_spawn.return_value = (True, "mock prompt", "mock context")
|
||||||
mock_confirm.return_value = True
|
mock_confirm.return_value = True
|
||||||
|
|
||||||
@@ -186,12 +186,12 @@ async def test_run_worker_lifecycle_step_mode_rejection(monkeypatch: pytest.Monk
|
|||||||
"""
|
"""
|
||||||
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1", step_mode=True)
|
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1", step_mode=True)
|
||||||
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[])
|
||||||
from multi_agent_conductor import run_worker_lifecycle
|
from src.multi_agent_conductor import run_worker_lifecycle
|
||||||
# Mock ai_client.send using monkeypatch
|
# Mock ai_client.send using monkeypatch
|
||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
with patch("multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
with patch("src.multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
||||||
patch("multi_agent_conductor.confirm_execution") as mock_confirm:
|
patch("src.multi_agent_conductor.confirm_execution") as mock_confirm:
|
||||||
mock_spawn.return_value = (True, "mock prompt", "mock context")
|
mock_spawn.return_value = (True, "mock prompt", "mock context")
|
||||||
mock_confirm.return_value = False
|
mock_confirm.return_value = False
|
||||||
mock_send.return_value = "Task failed because tool execution was rejected."
|
mock_send.return_value = "Task failed because tool execution was rejected."
|
||||||
@@ -209,7 +209,7 @@ async def test_conductor_engine_dynamic_parsing_and_execution(monkeypatch: pytes
|
|||||||
Test that parse_json_tickets correctly populates the track and run executes them in dependency order.
|
Test that parse_json_tickets correctly populates the track and run executes them in dependency order.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
from multi_agent_conductor import ConductorEngine
|
from src.multi_agent_conductor import ConductorEngine
|
||||||
track = Track(id="dynamic_track", description="Dynamic Track")
|
track = Track(id="dynamic_track", description="Dynamic Track")
|
||||||
engine = ConductorEngine(track=track, auto_queue=True)
|
engine = ConductorEngine(track=track, auto_queue=True)
|
||||||
tickets_json = json.dumps([
|
tickets_json = json.dumps([
|
||||||
@@ -246,7 +246,7 @@ async def test_conductor_engine_dynamic_parsing_and_execution(monkeypatch: pytes
|
|||||||
mock_send = MagicMock()
|
mock_send = MagicMock()
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
# Mock run_worker_lifecycle to mark tickets as complete
|
# Mock run_worker_lifecycle to mark tickets as complete
|
||||||
with patch("multi_agent_conductor.run_worker_lifecycle") as mock_lifecycle:
|
with patch("src.multi_agent_conductor.run_worker_lifecycle") as mock_lifecycle:
|
||||||
def side_effect(ticket, context, *args, **kwargs):
|
def side_effect(ticket, context, *args, **kwargs):
|
||||||
ticket.mark_complete()
|
ticket.mark_complete()
|
||||||
return "Success"
|
return "Success"
|
||||||
@@ -279,9 +279,9 @@ def test_run_worker_lifecycle_pushes_response_via_queue(monkeypatch: pytest.Monk
|
|||||||
mock_send = MagicMock(return_value="Task complete.")
|
mock_send = MagicMock(return_value="Task complete.")
|
||||||
monkeypatch.setattr(ai_client, 'send', mock_send)
|
monkeypatch.setattr(ai_client, 'send', mock_send)
|
||||||
monkeypatch.setattr(ai_client, 'reset_session', MagicMock())
|
monkeypatch.setattr(ai_client, 'reset_session', MagicMock())
|
||||||
from multi_agent_conductor import run_worker_lifecycle
|
from src.multi_agent_conductor import run_worker_lifecycle
|
||||||
with patch("multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
with patch("src.multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
||||||
patch("multi_agent_conductor._queue_put") as mock_queue_put:
|
patch("src.multi_agent_conductor._queue_put") as mock_queue_put:
|
||||||
mock_spawn.return_value = (True, "prompt", "context")
|
mock_spawn.return_value = (True, "prompt", "context")
|
||||||
run_worker_lifecycle(ticket, context, event_queue=mock_event_queue, loop=mock_loop)
|
run_worker_lifecycle(ticket, context, event_queue=mock_event_queue, loop=mock_loop)
|
||||||
mock_queue_put.assert_called_once()
|
mock_queue_put.assert_called_once()
|
||||||
@@ -309,14 +309,13 @@ def test_run_worker_lifecycle_token_usage_from_comms_log(monkeypatch: pytest.Mon
|
|||||||
[], # baseline call (before send)
|
[], # baseline call (before send)
|
||||||
fake_comms, # after-send call
|
fake_comms, # after-send call
|
||||||
]))
|
]))
|
||||||
from multi_agent_conductor import run_worker_lifecycle, ConductorEngine
|
from src.multi_agent_conductor import run_worker_lifecycle, ConductorEngine
|
||||||
from models import Track
|
from src.models import Track
|
||||||
track = Track(id="test_track", description="Test")
|
track = Track(id="test_track", description="Test")
|
||||||
engine = ConductorEngine(track=track, auto_queue=True)
|
engine = ConductorEngine(track=track, auto_queue=True)
|
||||||
with patch("multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
with patch("src.multi_agent_conductor.confirm_spawn") as mock_spawn, \
|
||||||
patch("multi_agent_conductor._queue_put"):
|
patch("src.multi_agent_conductor._queue_put"):
|
||||||
mock_spawn.return_value = (True, "prompt", "ctx")
|
mock_spawn.return_value = (True, "prompt", "ctx")
|
||||||
run_worker_lifecycle(ticket, context, event_queue=MagicMock(), loop=MagicMock(), engine=engine)
|
run_worker_lifecycle(ticket, context, event_queue=MagicMock(), loop=MagicMock(), engine=engine)
|
||||||
assert engine.tier_usage["Tier 3"]["input"] == 120
|
assert engine.tier_usage["Tier 3"]["input"] == 120
|
||||||
assert engine.tier_usage["Tier 3"]["output"] == 45
|
assert engine.tier_usage["Tier 3"]["output"] == 45
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
from gui_2 import App
|
from gui_2 import App
|
||||||
from models import Track
|
from src.models import Track
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def setup_mock_app(mock_app: App):
|
def setup_mock_app(mock_app: App):
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from models import Ticket, Track, WorkerContext
|
from src.models import Ticket, Track, WorkerContext
|
||||||
|
|
||||||
def test_ticket_instantiation() -> None:
|
def test_ticket_instantiation() -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from typing import Any
|
from typing import Any
|
||||||
import json
|
import json
|
||||||
from project_manager import get_all_tracks, save_track_state
|
from src.project_manager import get_all_tracks, save_track_state
|
||||||
from models import TrackState, Metadata, Ticket
|
from src.models import TrackState, Metadata, Ticket
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
def test_get_all_tracks_empty(tmp_path: Any) -> None:
|
def test_get_all_tracks_empty(tmp_path: Any) -> None:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
import multi_agent_conductor
|
import multi_agent_conductor
|
||||||
from models import Ticket, WorkerContext
|
from src.models import Ticket, WorkerContext
|
||||||
import events
|
import events
|
||||||
import asyncio
|
import asyncio
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Import the real models
|
# Import the real models
|
||||||
from models import TrackState, Metadata, Ticket
|
from src.models import TrackState, Metadata, Ticket
|
||||||
# Import the persistence functions from project_manager
|
# Import the persistence functions from project_manager
|
||||||
from project_manager import save_track_state, load_track_state
|
from project_manager import save_track_state, load_track_state
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
# Import necessary classes from models.py
|
# Import necessary classes from models.py
|
||||||
from models import Metadata, TrackState, Ticket
|
from src.models import Metadata, TrackState, Ticket
|
||||||
|
|
||||||
# --- Pytest Tests ---
|
# --- Pytest Tests ---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user