| # P3 Bug Report: Advanced Mode Missing Termination Guarantee |
|
|
| ## Status |
| - **Date:** 2025-11-29 |
| - **Priority:** P3 (Edge case, but confusing UX) |
| - **Component:** `src/orchestrator_magentic.py` |
| - **Resolution:** Fixed (Guarantee termination event) |
|
|
| --- |
|
|
| ## Symptoms |
|
|
| In **Advanced (Magentic) mode** with OpenAI API key: |
|
|
| 1. Workflow runs for many iterations (up to 10 rounds) |
| 2. Agents search, judge, hypothesize repeatedly |
| 3. Eventually... **nothing happens** |
| - No "complete" event |
| - No error message |
| - UI just stops updating |
|
|
| **User perception:** "Did it finish? Did it crash? What happened?" |
|
|
| ### Observed Behavior |
|
|
| When workflow hits `max_round_count=10`: |
| - `workflow.run_stream(task)` iterator ends |
| - NO `MagenticFinalResultEvent` is emitted by agent-framework |
| - Our code yields nothing after the loop |
| - User is left hanging |
|
|
| --- |
|
|
| ## Root Cause Analysis |
|
|
| ### Code Path (`src/orchestrator_magentic.py:170-186`) |
| |
| ```python |
| iteration = 0 |
| try: |
| async for event in workflow.run_stream(task): |
| agent_event = self._process_event(event, iteration) |
| if agent_event: |
| if isinstance(event, MagenticAgentMessageEvent): |
| iteration += 1 |
| yield agent_event |
| # BUG: NO FALLBACK HERE! |
| # If loop ends without FinalResultEvent, user sees nothing |
| |
| except Exception as e: |
| logger.error("Magentic workflow failed", error=str(e)) |
| yield AgentEvent( |
| type="error", |
| message=f"Workflow error: {e!s}", |
| iteration=iteration, |
| ) |
| # BUG: NO FINALLY BLOCK TO GUARANTEE TERMINATION EVENT |
| ``` |
| |
| ### Workflow Configuration (`src/orchestrator_magentic.py:110-116`) |
| |
| ```python |
| .with_standard_manager( |
| chat_client=manager_client, |
| max_round_count=self._max_rounds, # 10 - can hit this limit |
| max_stall_count=3, # If agents repeat 3x |
| max_reset_count=2, # Workflow reset limit |
| ) |
| ``` |
| |
| ### Failure Modes |
| |
| | Scenario | What Happens | User Sees | |
| |----------|--------------|-----------| |
| | `MagenticFinalResultEvent` emitted | `_process_event` yields "complete" | Final report | |
| | Max rounds (10) reached, no final event | Loop ends silently | **Nothing** | |
| | `max_stall_count` triggered | Workflow ends | **Nothing** | |
| | `max_reset_count` triggered | Workflow ends | **Nothing** | |
| | OpenAI API error | Exception caught | Error message | |
| |
| --- |
| |
| ## The Fix |
| |
| Add guaranteed termination event after the loop: |
| |
| ```python |
| iteration = 0 |
| final_event_received = False |
| |
| try: |
| async for event in workflow.run_stream(task): |
| agent_event = self._process_event(event, iteration) |
| if agent_event: |
| if isinstance(event, MagenticAgentMessageEvent): |
| iteration += 1 |
| if agent_event.type == "complete": |
| final_event_received = True |
| yield agent_event |
| |
| except Exception as e: |
| logger.error("Magentic workflow failed", error=str(e)) |
| yield AgentEvent( |
| type="error", |
| message=f"Workflow error: {e!s}", |
| iteration=iteration, |
| ) |
| final_event_received = True # Error is a form of termination |
| |
| finally: |
| # GUARANTEE: Always emit termination event |
| if not final_event_received: |
| logger.warning( |
| "Workflow ended without final event", |
| iterations=iteration, |
| ) |
| yield AgentEvent( |
| type="complete", |
| message=( |
| f"Research completed after {iteration} agent rounds. " |
| "Max iterations reached - results may be partial. " |
| "Try a more specific query for better results." |
| ), |
| data={"iterations": iteration, "reason": "max_rounds_reached"}, |
| iteration=iteration, |
| ) |
| ``` |
| |
| --- |
|
|
| ## Alternative: Increase Max Rounds |
|
|
| The default `max_rounds=10` might be too low for complex queries. |
|
|
| In `src/orchestrator_factory.py:52-53`: |
| ```python |
| return orchestrator_cls( |
| max_rounds=config.max_iterations if config else 10, # Could increase to 15-20 |
| api_key=api_key, |
| ) |
| ``` |
|
|
| **Trade-off:** More rounds = more API cost, but better chance of complete results. |
|
|
| --- |
|
|
| ## Test Plan |
|
|
| - [ ] Add fallback yield after async for loop |
| - [ ] Add `final_event_received` flag tracking |
| - [ ] Log warning when fallback is used |
| - [ ] Test with `max_rounds=2` to force hitting limit |
| - [ ] Verify user always sees termination event |
| - [ ] `make check` passes |
|
|
| --- |
|
|
| ## Related Files |
|
|
| - `src/orchestrator_magentic.py` - Main fix location |
| - `src/orchestrator_factory.py` - Max rounds configuration |
| - `src/utils/models.py` - AgentEvent types |
| - `docs/bugs/P2_MAGENTIC_THINKING_STATE.md` - Related UX issue (implemented) |
|
|
| --- |
|
|
| ## Priority Justification |
|
|
| **P3** because: |
| - Advanced mode is working for most queries |
| - Only hits edge case when max rounds reached without synthesis |
| - User CAN retry with different query |
| - Not blocking hackathon demo (free tier Simple mode works) |
|
|
| Would be P2 if: |
| - This happened frequently |
| - No workaround existed |
|
|