VibecoderMcSwaggins commited on
Commit
a5b5479
·
1 Parent(s): 1bfc1df

refactor(orchestrator): DRY synthesis + constants + clean imports

Browse files

Addresses senior review feedback on P1 fix code quality:

Priority 1 - DRY Violation:
- Unified `_handle_timeout()` and `_force_synthesis()` into `_synthesize_fallback(iteration, reason)`
- Removed 40+ lines of duplicate code
- Single method handles timeout, no_reporter, and max_rounds scenarios

Priority 2 - Redundant Imports:
- Added `get_magentic_state` to module-level imports
- Removed duplicate imports from inside methods

Priority 3 - Magic Strings:
- Added constants: REPORTER_AGENT_ID, SEARCHER_AGENT_ID, JUDGE_AGENT_ID, HYPOTHESIZER_AGENT_ID
- Updated `_get_event_type_for_agent()` to use constants
- Updated reporter detection in run() to use constant

Also:
- Fixed test patch paths (must patch in module namespace, not source)
- Added SPEC_ARCHITECTURAL_DEBT.md documenting Phase 2 refactoring roadmap

All 318 tests pass.

SPEC_ARCHITECTURAL_DEBT.md ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Architectural Debt Specification
2
+
3
+ > **Status**: IMPLEMENTED (Phase 1 Complete)
4
+ > **Date**: 2025-12-04
5
+ > **Scope**: `src/orchestrators/advanced.py` (Primary), System-Wide (Secondary)
6
+ > **Purpose**: Roadmap for "DeepMind Status" Code Quality
7
+ > **Author**: Claude (Senior Review Incorporated)
8
+
9
+ ---
10
+
11
+ ## Executive Summary
12
+
13
+ The P1/P2 bug fixes in PR #124 introduced technical debt that must be addressed before the PR is considered "done". This spec documents three immediate priorities (DRY violation, redundant imports, magic strings) and five medium-term system-wide improvements.
14
+
15
+ ---
16
+
17
+ ## Part 1: Immediate Cleanup (MUST Complete Before PR Merge)
18
+
19
+ ### Priority 1: DRY Violation - Synthesis Methods
20
+
21
+ **Problem**: `_handle_timeout()` (lines 201-248) and `_force_synthesis()` (lines 250-297) are **95% identical**.
22
+
23
+ | `_handle_timeout()` | `_force_synthesis()` |
24
+ |---------------------|----------------------|
25
+ | Lines 201-248 (47 lines) | Lines 250-297 (47 lines) |
26
+ | Yields "Workflow timed out. Synthesizing..." | Yields "Synthesizing research findings..." |
27
+ | Error data: `timeout_synthesis` | Error data: `forced_synthesis` |
28
+ | **Everything else is identical** | **Everything else is identical** |
29
+
30
+ **SOLID Violation**: **DRY (Don't Repeat Yourself)**. Changes to synthesis logic must be made in two places. This is a maintenance nightmare and a source of future bugs.
31
+
32
+ **Fix**: Extract unified method `_synthesize_fallback(iteration: int, reason: str)`:
33
+
34
+ ```python
35
+ async def _synthesize_fallback(
36
+ self, iteration: int, reason: str
37
+ ) -> AsyncGenerator[AgentEvent, None]:
38
+ """
39
+ Unified fallback synthesis for all termination scenarios.
40
+
41
+ Args:
42
+ iteration: Current workflow iteration
43
+ reason: Why synthesis is being forced ("timeout", "no_reporter", "max_rounds")
44
+ """
45
+ status_messages = {
46
+ "timeout": "Workflow timed out. Synthesizing available evidence...",
47
+ "no_reporter": "Synthesizing research findings...",
48
+ "max_rounds": "Max rounds reached. Synthesizing findings...",
49
+ }
50
+
51
+ try:
52
+ state = get_magentic_state()
53
+ evidence_summary = await state.memory.get_context_summary()
54
+ report_agent = create_report_agent(self._chat_client, domain=self.domain)
55
+
56
+ yield AgentEvent(
57
+ type="synthesizing",
58
+ message=status_messages.get(reason, "Synthesizing..."),
59
+ iteration=iteration,
60
+ )
61
+
62
+ synthesis_result = await report_agent.run(
63
+ f"Synthesize research report from this evidence. "
64
+ f"If evidence is sparse, say so.\n\n{evidence_summary}"
65
+ )
66
+
67
+ yield AgentEvent(
68
+ type="complete",
69
+ message=synthesis_result.text,
70
+ data={"reason": f"{reason}_synthesis", "iterations": iteration},
71
+ iteration=iteration,
72
+ )
73
+ except Exception as synth_error:
74
+ logger.error(f"{reason} synthesis failed", error=str(synth_error))
75
+ yield AgentEvent(
76
+ type="complete",
77
+ message=f"Research completed. Synthesis failed: {synth_error}",
78
+ data={"reason": f"{reason}_synthesis_failed", "iterations": iteration},
79
+ iteration=iteration,
80
+ )
81
+ ```
82
+
83
+ **Call Sites to Update**:
84
+ 1. Line 447: `async for event in self._handle_timeout(iteration):` → `self._synthesize_fallback(iteration, "timeout")`
85
+ 2. Line 412: `async for synth_event in self._force_synthesis(iteration):` → `self._synthesize_fallback(iteration, "no_reporter")`
86
+ 3. Line 432: `async for synth_event in self._force_synthesis(iteration):` → `self._synthesize_fallback(iteration, "max_rounds")`
87
+
88
+ **Delete After Refactor**:
89
+ - `_handle_timeout()` method (lines 201-248)
90
+ - `_force_synthesis()` method (lines 250-297)
91
+
92
+ ---
93
+
94
+ ### Priority 2: Redundant Imports
95
+
96
+ **Problem**: Imports inside methods that already exist at module level.
97
+
98
+ | Location | Import | Already At |
99
+ |----------|--------|------------|
100
+ | Line 207 | `from src.agents.magentic_agents import create_report_agent` | Line 35 |
101
+ | Line 208 | `from src.agents.state import get_magentic_state` | Missing! |
102
+ | Line 257 | `from src.agents.magentic_agents import create_report_agent` | Line 35 |
103
+ | Line 258 | `from src.agents.state import get_magentic_state` | Missing! |
104
+
105
+ **SOLID Violation**: **SRP (Single Responsibility)**. Import management is scattered across the file instead of centralized at the top.
106
+
107
+ **Fix**:
108
+ 1. Add to module-level imports (around line 38):
109
+ ```python
110
+ from src.agents.state import get_magentic_state, init_magentic_state
111
+ ```
112
+ Note: `init_magentic_state` is already imported at line 38. Add `get_magentic_state` to that import.
113
+
114
+ 2. Remove redundant imports from:
115
+ - Lines 207-208 (inside `_handle_timeout`)
116
+ - Lines 257-258 (inside `_force_synthesis`)
117
+
118
+ ---
119
+
120
+ ### Priority 3: Magic Strings
121
+
122
+ **Problem**: Agent detection relies on string literals that break silently if agents are renamed.
123
+
124
+ **Current Code** (line 385):
125
+ ```python
126
+ agent_name = (event.agent_id or "").lower()
127
+ if "report" in agent_name: # FRAGILE: Breaks if agent renamed
128
+ reporter_ran = True
129
+ ```
130
+
131
+ **Also in** `_get_event_type_for_agent()` (lines 593-602):
132
+ ```python
133
+ if "search" in agent_lower: # Magic string
134
+ if "judge" in agent_lower: # Magic string
135
+ if "hypothes" in agent_lower: # Magic string
136
+ if "report" in agent_lower: # Magic string
137
+ ```
138
+
139
+ **SOLID Violation**: **OCP (Open/Closed Principle)**. Renaming an agent requires changes in multiple locations.
140
+
141
+ **Fix Option A** - Constants:
142
+ ```python
143
+ # At module level (after imports)
144
+ REPORTER_AGENT_ID = "reporter"
145
+ SEARCHER_AGENT_ID = "searcher"
146
+ JUDGE_AGENT_ID = "judge"
147
+ HYPOTHESIZER_AGENT_ID = "hypothesizer"
148
+ ```
149
+
150
+ **Fix Option B** - Agent Name Attribute (Preferred):
151
+ ```python
152
+ # In magentic_agents.py, ensure each agent has a .name attribute
153
+ # Then in advanced.py:
154
+ if event.agent_id == report_agent.name:
155
+ reporter_ran = True
156
+ ```
157
+
158
+ **Recommendation**: Option A is simpler and doesn't require modifying agent factory. Use constants.
159
+
160
+ ---
161
+
162
+ ## Part 2: System-Wide Issues (Future PRs)
163
+
164
+ These are valid concerns identified during code review but are NOT blockers for the current PR.
165
+
166
+ ### Priority 4: Dead Config
167
+
168
+ **Location**: `src/utils/config.py`
169
+ **Issue**: Zombie configuration values that are never used or raise NotImplemented.
170
+ - `magentic_timeout`: Deprecated, never read
171
+ - `anthropic_api_key`: Config exists but factory raises NotImplemented
172
+
173
+ **Fix**: Audit config.py, remove dead settings, add deprecation warnings for transitional settings.
174
+
175
+ ---
176
+
177
+ ### Priority 5: Prompt Unification
178
+
179
+ **Location**: `src/prompts/` vs `src/config/domain.py`
180
+ **Issue**: Two sources of truth for prompts. `src/prompts/` files exist but are ignored. System uses hardcoded strings in `domain.py`.
181
+
182
+ **Fix**: Pick ONE source of truth. Recommendation: Delete `src/prompts/` if unused, or migrate `domain.py` prompts there.
183
+
184
+ ---
185
+
186
+ ### Priority 6: Factory Monolith
187
+
188
+ **Location**: `src/clients/factory.py`
189
+ **Issue**: Hardcoded logic for detecting API key prefixes (`sk-` → OpenAI, `sk-ant-` → Anthropic error).
190
+
191
+ **SOLID Violation**: OCP. Adding a new provider requires modifying the factory.
192
+
193
+ **Fix**: Provider registry pattern with auto-registration, or strategy pattern with key prefix handlers.
194
+
195
+ ---
196
+
197
+ ### Priority 7: State Class
198
+
199
+ **Location**: `src/orchestrators/advanced.py` `run()` method
200
+ **Issue**: Method manages 6+ loose variables (`iteration`, `reporter_ran`, `buffer`, `current_agent_id`, `last_streamed_length`, `final_event_received`).
201
+
202
+ **Fix**: Extract to `WorkflowState` dataclass:
203
+ ```python
204
+ @dataclass
205
+ class WorkflowState:
206
+ iteration: int = 0
207
+ reporter_ran: bool = False
208
+ current_message_buffer: str = ""
209
+ current_agent_id: str | None = None
210
+ last_streamed_length: int = 0
211
+ final_event_received: bool = False
212
+ ```
213
+
214
+ ---
215
+
216
+ ### Priority 8: Real Integration Tests
217
+
218
+ **Location**: `tests/e2e/`
219
+ **Issue**: We deleted flaky integration tests. Now we have ZERO automated tests against real APIs.
220
+
221
+ **Fix**: Create stable `make test-live` suite with:
222
+ - Real HuggingFace Free Tier test
223
+ - Real OpenAI BYOK test
224
+ - Proper timeout handling
225
+ - Skip markers for CI (run manually or on schedule)
226
+
227
+ ---
228
+
229
+ ## Execution Strategy
230
+
231
+ ### Phase 1: Current PR (REQUIRED)
232
+ Implement **Priority 1, 2, and 3** before merging PR #124.
233
+
234
+ **Definition of Done**:
235
+ - [x] `_synthesize_fallback(iteration, reason)` implemented
236
+ - [x] `_handle_timeout()` and `_force_synthesis()` deleted
237
+ - [x] All synthesis call sites updated
238
+ - [x] Redundant imports removed
239
+ - [x] `get_magentic_state` added to module-level imports
240
+ - [x] Magic strings replaced with constants
241
+ - [x] All tests pass (`make check`)
242
+
243
+ ### Phase 2: Future PRs (Separate Tickets)
244
+ Create GitHub issues for Priority 4-8. Do NOT bloat the current bug fix PR.
245
+
246
+ ---
247
+
248
+ ## Appendix: Line Number Reference
249
+
250
+ | Item | Current Location |
251
+ |------|------------------|
252
+ | `_handle_timeout()` | Lines 201-248 |
253
+ | `_force_synthesis()` | Lines 250-297 |
254
+ | Redundant imports (timeout) | Lines 207-208 |
255
+ | Redundant imports (force) | Lines 257-258 |
256
+ | Magic string detection | Line 385 |
257
+ | `_get_event_type_for_agent()` | Lines 582-602 |
258
+ | Module imports | Lines 18-48 |
259
+ | `run()` method | Lines 299-456 |
src/orchestrators/advanced.py CHANGED
@@ -35,7 +35,7 @@ from src.agents.magentic_agents import (
35
  create_report_agent,
36
  create_search_agent,
37
  )
38
- from src.agents.state import init_magentic_state
39
  from src.clients.base import BaseChatClient
40
  from src.clients.factory import get_chat_client
41
  from src.config.domain import ResearchDomain, get_domain_config
@@ -49,6 +49,12 @@ if TYPE_CHECKING:
49
 
50
  logger = structlog.get_logger()
51
 
 
 
 
 
 
 
52
 
53
  class AdvancedOrchestrator(OrchestratorProtocol):
54
  """
@@ -198,81 +204,39 @@ The final output should be a structured research report."""
198
  iteration=0,
199
  )
200
 
201
- async def _handle_timeout(self, iteration: int) -> AsyncGenerator[AgentEvent, None]:
202
- """Handle workflow timeout by attempting synthesis."""
203
- logger.warning("Workflow timed out", iterations=iteration)
204
-
205
- # ACTUALLY synthesize from gathered evidence
206
- try:
207
- from src.agents.magentic_agents import create_report_agent
208
- from src.agents.state import get_magentic_state
209
-
210
- state = get_magentic_state()
211
- memory = state.memory
212
-
213
- # Get evidence summary from memory
214
- evidence_summary = await memory.get_context_summary()
215
-
216
- # Create and invoke ReportAgent for synthesis
217
- report_agent = create_report_agent(self._chat_client, domain=self.domain)
218
-
219
- yield AgentEvent(
220
- type="synthesizing",
221
- message="Workflow timed out. Synthesizing available evidence...",
222
- iteration=iteration,
223
- )
224
-
225
- # Invoke ReportAgent directly
226
- # Note: ChatAgent.run() returns AgentRunResponse; access text via .text
227
- synthesis_result = await report_agent.run(
228
- "Synthesize research report from this evidence. "
229
- f"If evidence is sparse, say so.\n\n{evidence_summary}"
230
- )
231
-
232
- yield AgentEvent(
233
- type="complete",
234
- message=synthesis_result.text,
235
- data={"reason": "timeout_synthesis", "iterations": iteration},
236
- iteration=iteration,
237
- )
238
- except Exception as synth_error:
239
- logger.error("Timeout synthesis failed", error=str(synth_error))
240
- yield AgentEvent(
241
- type="complete",
242
- message=(
243
- f"Research timed out after {iteration} rounds. "
244
- f"Evidence gathered but synthesis failed: {synth_error}"
245
- ),
246
- data={"reason": "timeout_synthesis_failed", "iterations": iteration},
247
- iteration=iteration,
248
- )
249
 
250
- async def _force_synthesis(self, iteration: int) -> AsyncGenerator[AgentEvent, None]:
251
- """Force synthesis when workflow ends without ReportAgent running (P1 Fix).
 
 
 
252
 
253
- This is a safety net for when the Manager agent (especially 7B models)
254
- fails to properly delegate to ReportAgent before workflow termination.
 
255
  """
256
- try:
257
- from src.agents.magentic_agents import create_report_agent
258
- from src.agents.state import get_magentic_state
 
 
259
 
 
260
  state = get_magentic_state()
261
- memory = state.memory
262
-
263
- # Get evidence summary from memory
264
- evidence_summary = await memory.get_context_summary()
265
-
266
- # Create and invoke ReportAgent for synthesis
267
  report_agent = create_report_agent(self._chat_client, domain=self.domain)
268
 
269
  yield AgentEvent(
270
  type="synthesizing",
271
- message="Synthesizing research findings...",
272
  iteration=iteration,
273
  )
274
 
275
- # Invoke ReportAgent directly
276
  synthesis_result = await report_agent.run(
277
  "Synthesize research report from this evidence. "
278
  f"If evidence is sparse, say so.\n\n{evidence_summary}"
@@ -281,18 +245,15 @@ The final output should be a structured research report."""
281
  yield AgentEvent(
282
  type="complete",
283
  message=synthesis_result.text,
284
- data={"reason": "forced_synthesis", "iterations": iteration},
285
  iteration=iteration,
286
  )
287
  except Exception as synth_error:
288
- logger.error("Forced synthesis failed", error=str(synth_error))
289
  yield AgentEvent(
290
  type="complete",
291
- message=(
292
- f"Research completed after {iteration} rounds. "
293
- f"Evidence gathered but synthesis failed: {synth_error}"
294
- ),
295
- data={"reason": "forced_synthesis_failed", "iterations": iteration},
296
  iteration=iteration,
297
  )
298
 
@@ -382,7 +343,7 @@ The final output should be a structured research report."""
382
 
383
  # P1 FIX: Track if ReportAgent produced output
384
  agent_name = (event.agent_id or "").lower()
385
- if "report" in agent_name:
386
  reporter_ran = True
387
 
388
  comp_event, prog_event = self._handle_completion_event(
@@ -409,7 +370,9 @@ The final output should be a structured research report."""
409
  "ReportAgent never ran - forcing synthesis",
410
  iterations=iteration,
411
  )
412
- async for synth_event in self._force_synthesis(iteration):
 
 
413
  yield synth_event
414
  else:
415
  yield self._handle_final_event(event, iteration, last_streamed_length)
@@ -429,7 +392,7 @@ The final output should be a structured research report."""
429
  )
430
  # P1 FIX: Force synthesis if ReportAgent never ran
431
  if not reporter_ran:
432
- async for synth_event in self._force_synthesis(iteration):
433
  yield synth_event
434
  else:
435
  yield AgentEvent(
@@ -444,7 +407,7 @@ The final output should be a structured research report."""
444
  )
445
 
446
  except TimeoutError:
447
- async for event in self._handle_timeout(iteration):
448
  yield event
449
 
450
  except Exception as e:
@@ -591,13 +554,13 @@ The final output should be a structured research report."""
591
  Event type string matching AgentEvent.type Literal
592
  """
593
  agent_lower = agent_name.lower()
594
- if "search" in agent_lower:
595
  return "search_complete"
596
- if "judge" in agent_lower:
597
  return "judge_complete"
598
- if "hypothes" in agent_lower:
599
  return "hypothesizing"
600
- if "report" in agent_lower:
601
  return "synthesizing"
602
  return "judging" # Default for unknown agents
603
 
 
35
  create_report_agent,
36
  create_search_agent,
37
  )
38
+ from src.agents.state import get_magentic_state, init_magentic_state
39
  from src.clients.base import BaseChatClient
40
  from src.clients.factory import get_chat_client
41
  from src.config.domain import ResearchDomain, get_domain_config
 
49
 
50
  logger = structlog.get_logger()
51
 
52
+ # Agent ID constants - prevents silent breakage if agents are renamed
53
+ REPORTER_AGENT_ID = "reporter"
54
+ SEARCHER_AGENT_ID = "searcher"
55
+ JUDGE_AGENT_ID = "judge"
56
+ HYPOTHESIZER_AGENT_ID = "hypothesizer"
57
+
58
 
59
  class AdvancedOrchestrator(OrchestratorProtocol):
60
  """
 
204
  iteration=0,
205
  )
206
 
207
+ async def _synthesize_fallback(
208
+ self, iteration: int, reason: str
209
+ ) -> AsyncGenerator[AgentEvent, None]:
210
+ """
211
+ Unified fallback synthesis for all termination scenarios.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
+ This method handles synthesis when the workflow terminates without
214
+ a proper report from ReportAgent. It's a safety net for:
215
+ - Timeout scenarios
216
+ - Manager model failing to delegate to ReportAgent (7B model limitation)
217
+ - Max rounds reached without synthesis
218
 
219
+ Args:
220
+ iteration: Current workflow iteration count
221
+ reason: Why synthesis is being forced ("timeout", "no_reporter", "max_rounds")
222
  """
223
+ status_messages = {
224
+ "timeout": "Workflow timed out. Synthesizing available evidence...",
225
+ "no_reporter": "Synthesizing research findings...",
226
+ "max_rounds": "Max rounds reached. Synthesizing findings...",
227
+ }
228
 
229
+ try:
230
  state = get_magentic_state()
231
+ evidence_summary = await state.memory.get_context_summary()
 
 
 
 
 
232
  report_agent = create_report_agent(self._chat_client, domain=self.domain)
233
 
234
  yield AgentEvent(
235
  type="synthesizing",
236
+ message=status_messages.get(reason, "Synthesizing..."),
237
  iteration=iteration,
238
  )
239
 
 
240
  synthesis_result = await report_agent.run(
241
  "Synthesize research report from this evidence. "
242
  f"If evidence is sparse, say so.\n\n{evidence_summary}"
 
245
  yield AgentEvent(
246
  type="complete",
247
  message=synthesis_result.text,
248
+ data={"reason": f"{reason}_synthesis", "iterations": iteration},
249
  iteration=iteration,
250
  )
251
  except Exception as synth_error:
252
+ logger.error(f"{reason} synthesis failed", error=str(synth_error))
253
  yield AgentEvent(
254
  type="complete",
255
+ message=f"Research completed. Synthesis failed: {synth_error}",
256
+ data={"reason": f"{reason}_synthesis_failed", "iterations": iteration},
 
 
 
257
  iteration=iteration,
258
  )
259
 
 
343
 
344
  # P1 FIX: Track if ReportAgent produced output
345
  agent_name = (event.agent_id or "").lower()
346
+ if REPORTER_AGENT_ID in agent_name:
347
  reporter_ran = True
348
 
349
  comp_event, prog_event = self._handle_completion_event(
 
370
  "ReportAgent never ran - forcing synthesis",
371
  iterations=iteration,
372
  )
373
+ async for synth_event in self._synthesize_fallback(
374
+ iteration, "no_reporter"
375
+ ):
376
  yield synth_event
377
  else:
378
  yield self._handle_final_event(event, iteration, last_streamed_length)
 
392
  )
393
  # P1 FIX: Force synthesis if ReportAgent never ran
394
  if not reporter_ran:
395
+ async for synth_event in self._synthesize_fallback(iteration, "max_rounds"):
396
  yield synth_event
397
  else:
398
  yield AgentEvent(
 
407
  )
408
 
409
  except TimeoutError:
410
+ async for event in self._synthesize_fallback(iteration, "timeout"):
411
  yield event
412
 
413
  except Exception as e:
 
554
  Event type string matching AgentEvent.type Literal
555
  """
556
  agent_lower = agent_name.lower()
557
+ if SEARCHER_AGENT_ID in agent_lower:
558
  return "search_complete"
559
+ if JUDGE_AGENT_ID in agent_lower:
560
  return "judge_complete"
561
+ if HYPOTHESIZER_AGENT_ID in agent_lower:
562
  return "hypothesizing"
563
+ if REPORTER_AGENT_ID in agent_lower:
564
  return "synthesizing"
565
  return "judging" # Default for unknown agents
566
 
tests/unit/orchestrators/test_advanced_timeout.py CHANGED
@@ -27,11 +27,13 @@ async def test_timeout_synthesizes_evidence():
27
  mock_workflow.run_stream = slow_stream
28
 
29
  # Mock dependencies used inside the timeout block
 
 
30
  with (
31
  patch.object(orchestrator, "_build_workflow", return_value=mock_workflow),
32
  patch("src.orchestrators.advanced.init_magentic_state"),
33
- patch("src.agents.state.get_magentic_state") as mock_get_state,
34
- patch("src.agents.magentic_agents.create_report_agent") as mock_create_agent,
35
  ):
36
  # Setup mock state and memory
37
  mock_memory = AsyncMock()
 
27
  mock_workflow.run_stream = slow_stream
28
 
29
  # Mock dependencies used inside the timeout block
30
+ # Note: get_magentic_state and create_report_agent are imported at module level in advanced.py
31
+ # so we must patch them in that module's namespace, not their original location
32
  with (
33
  patch.object(orchestrator, "_build_workflow", return_value=mock_workflow),
34
  patch("src.orchestrators.advanced.init_magentic_state"),
35
+ patch("src.orchestrators.advanced.get_magentic_state") as mock_get_state,
36
+ patch("src.orchestrators.advanced.create_report_agent") as mock_create_agent,
37
  ):
38
  # Setup mock state and memory
39
  mock_memory = AsyncMock()