Skip to content

fix(aggregators): forward StartFrame once in GatedLLMContextAggregator - #5911

Open
BlueX888 wants to merge 1 commit into
pipecat-ai:mainfrom
BlueX888:fix/prep-gated-llm-context-double-startframe
Open

BlueX888 wants to merge 1 commit into
pipecat-ai:mainfrom
BlueX888:fix/prep-gated-llm-context-double-startframe

Conversation

@BlueX888

Copy link
Copy Markdown

Please describe the changes in your PR. If it is addressing an issue, please reference that as well.

GatedLLMContextAggregator forwarded StartFrame downstream twice. This PR makes it forward the frame exactly once. No linked issue — found while reading the processor, with a regression test.

Why

In process_frame() (src/pipecat/processors/aggregators/gated_llm_context.py:45), the StartFrame branch was a standalone if:

if isinstance(frame, StartFrame):
    await self.push_frame(frame)
    await self._start()
if isinstance(frame, (EndFrame, CancelFrame)):
    ...
elif isinstance(frame, LLMContextFrame):
    ...
else:
    await self.push_frame(frame, direction)

A StartFrame is neither an EndFrame/CancelFrame nor an LLMContextFrame, so after the first branch pushed the frame and started the gate task, control fell through to the catch-all else (gated_llm_context.py:57), which pushed the same frame a second time.

Every processor downstream of the aggregator then received two StartFrames and re-ran its start handling — for example FrameProcessor.process_frame() creates its process task on StartFrame (src/pipecat/processors/frame_processor.py:836), and BaseOutputTransport.process_frame() calls start() again (src/pipecat/transports/base_output.py:349).

Changes

  • src/pipecat/processors/aggregators/gated_llm_context.py: the StartFrame branch now belongs to the same if/elif chain as the other frame types (if → elif), so a StartFrame no longer falls through to the catch-all else. One token changed.
  • tests/test_aggregators.py: added TestGatedLLMContextAggregator, which sends a TextFrame and a gated LLMContextFrame through the aggregator with ignore_start=False and expects exactly one StartFrame downstream.
  • changelog/5910.fixed.md: changelog fragment.

Testing

uv run pytest tests/test_aggregators.py::TestGatedLLMContextAggregator — fails on main:

received DOWN frames = [StartFrame, StartFrame, TextFrame, ]
expected DOWN frames = [StartFrame, TextFrame, ]
...
>           assert len(received_down_frames) == len(expected_down_frames)
E           AssertionError
src/pipecat/tests/utils.py:236: AssertionError
============================== 1 failed in 0.36s ===============================

passes with the fix:

============================== 1 passed in 0.15s ===============================

Also run (--no-sync because this environment has no network access; same project venv that uv sync produces):

uv run pytest tests/test_aggregators.py                                                  # 3 passed
uv run pytest -q --continue-on-collection-errors                                         # 4398 passed, 151 skipped
uv run ruff check                                                                        # All checks passed!
uv run ruff format --check                                                               # 1458 files already formatted
uv run pyright src/pipecat/processors/aggregators/gated_llm_context.py tests/test_aggregators.py   # 0 errors, 0 warnings

The full-suite run's 28 failures and 33 collection errors are byte-identical on unpatched main in this environment (missing optional service extras such as google-genai, anthropic, httpx, fastapi), so this change introduces no regressions.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant