DeepBoner / docs /specs /SPEC_17_ACCUMULATOR_PATTERN.md
VibecoderMcSwaggins's picture
fix(P0): Implement Accumulator Pattern to resolve Repr Bug (#117)
c6e9843 unverified
|
raw
history blame
3.13 kB

SPEC 17: Accumulator Pattern for Agent Events

Status: IMPLEMENTED Created: 2025-12-02 Author: AI Agent Related: P0_REPR_BUG_ROOT_CAUSE_ANALYSIS.md

1. Context

The Microsoft Agent Framework event model has a specific intended usage pattern:

  • MagenticAgentDeltaEvent.text β†’ Content Source (Streaming)
  • MagenticAgentMessageEvent β†’ Completion Signal (End of Turn)

Our previous implementation incorrectly attempted to extract content from MagenticAgentMessageEvent.message. This property is not designed for content extraction and can contain internal representation data (repr strings) for tool-only messages. This led to the "repr bug" where users saw raw Python object strings in the UI.

The Accumulator Pattern aligns our codebase with Microsoft's intended architecture (as demonstrated in their 04_magentic_one.py sample) and resolves the display issues by using the correct event data source.

2. The Solution: Accumulator Pattern

Instead of relying on the final message event for content, we adopt the Accumulator Pattern, which aligns with the Microsoft Agent Framework's intended usage (as seen in their sample 04_magentic_one.py).

2.1 Core Concept

  1. Streaming is Truth: MagenticAgentDeltaEvent is the exclusive source of text content. These events are not affected by the upstream bug.
  2. Accumulation: The orchestrator maintains a stateful buffer (current_message_buffer) that appends text from delta events.
  3. Signal Processing: MagenticAgentMessageEvent is treated solely as a completion signal ("end of turn"). When received, we consume the buffer to form the final UI message and then clear the buffer.

2.2 Logic Flow

current_message_buffer = ""

for event in stream:
    if event is DeltaEvent:
        current_message_buffer += event.text
        emit_streaming_event(event.text)
    
    elif event is MessageEvent:
        # IGNORE event.message (it might be corrupted)
        final_text = current_message_buffer
        if not final_text:
             final_text = "Action completed (Tool Call)"
        
        emit_complete_event(final_text)
        current_message_buffer = ""

3. Test Plan

To verify this pattern ensures correct output regardless of upstream bugs, we define the following test scenarios:

3.1 Scenario A: Standard Text Message

  • Input: Sequence of MagenticAgentDeltaEvent (with text parts) -> MagenticAgentMessageEvent (with corrupted repr).
  • Expected Output: The AgentEvent emitted at the end must contain the concatenated text from the deltas, NOT the repr string.

3.2 Scenario B: Tool Call (No Text)

  • Input: No text deltas -> MagenticAgentMessageEvent (with corrupted repr).
  • Expected Output: The AgentEvent should contain a fallback message (e.g., "Action completed (Tool Call)"), NOT the repr string.

4. Implementation Details

The pattern is implemented in src/orchestrators/advanced.py within the run() method loop. It bypasses _process_event for these specific event types to ensure strict control over data flow.