Add optional dedicated LLM for context summarization

Adds an  field to LLMContextSummarizationConfig that allows
  routing summarization to a separate LLM service (e.g., Gemini Flash)
  instead of the pipeline's primary model. This avoids paying for
  expensive inference when compressing context in long-running sessions.
This commit is contained in:
Mark Backman
2026-02-26 21:22:52 -05:00
parent 945a523eed
commit a489bfaf00
2 changed files with 60 additions and 4 deletions

View File

@@ -16,7 +16,7 @@ import json
import warnings import warnings
from abc import abstractmethod from abc import abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Dict, List, Literal, Optional, Set, Type from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Set, Type
from loguru import logger from loguru import logger
@@ -39,6 +39,7 @@ from pipecat.frames.frames import (
LLMContextAssistantTimestampFrame, LLMContextAssistantTimestampFrame,
LLMContextFrame, LLMContextFrame,
LLMContextSummaryRequestFrame, LLMContextSummaryRequestFrame,
LLMContextSummaryResultFrame,
LLMFullResponseEndFrame, LLMFullResponseEndFrame,
LLMFullResponseStartFrame, LLMFullResponseStartFrame,
LLMMessagesAppendFrame, LLMMessagesAppendFrame,
@@ -83,6 +84,9 @@ from pipecat.utils.context.llm_context_summarization import LLMContextSummarizat
from pipecat.utils.string import TextPartForConcatenation, concatenate_aggregated_text from pipecat.utils.string import TextPartForConcatenation, concatenate_aggregated_text
from pipecat.utils.time import time_now_iso8601 from pipecat.utils.time import time_now_iso8601
if TYPE_CHECKING:
from pipecat.services.llm_service import LLMService
@dataclass @dataclass
class LLMUserAggregatorParams: class LLMUserAggregatorParams:
@@ -1248,13 +1252,56 @@ class LLMAssistantAggregator(LLMContextAggregator):
): ):
"""Handle summarization request from the summarizer. """Handle summarization request from the summarizer.
Push the request frame UPSTREAM to the LLM service for processing. If a dedicated summarization LLM is configured, generates the summary
directly and feeds the result to the summarizer. Otherwise, pushes the
request frame upstream to the pipeline's primary LLM service.
Args: Args:
summarizer: The summarizer that generated the request. summarizer: The summarizer that generated the request.
frame: The summarization request frame to broadcast. frame: The summarization request frame to broadcast.
""" """
await self.push_frame(frame, FrameDirection.UPSTREAM) summarization_llm = (
self._params.context_summarization_config.llm
if self._params.context_summarization_config
else None
)
if summarization_llm:
self.create_task(self._generate_summary_with_dedicated_llm(summarization_llm, frame))
else:
await self.push_frame(frame, FrameDirection.UPSTREAM)
async def _generate_summary_with_dedicated_llm(
self, llm: "LLMService", frame: LLMContextSummaryRequestFrame
):
"""Generate summary using a dedicated LLM service.
Calls the dedicated LLM's _generate_summary directly and feeds the
result back to the summarizer, bypassing the pipeline.
Args:
llm: The dedicated LLM service to use for summarization.
frame: The summarization request frame.
"""
try:
summary, last_index = await llm._generate_summary(frame)
result_frame = LLMContextSummaryResultFrame(
request_id=frame.request_id,
summary=summary,
last_summarized_index=last_index,
)
except Exception as e:
error = f"Error generating context summary: {e}"
await self.push_error(error, exception=e)
result_frame = LLMContextSummaryResultFrame(
request_id=frame.request_id,
summary="",
last_summarized_index=-1,
error=f"Error generating context summary: {e}",
)
if self._summarizer:
await self._summarizer.process_frame(result_frame)
class LLMContextAggregatorPair: class LLMContextAggregatorPair:

View File

@@ -11,7 +11,10 @@ context when token limits are reached, enabling efficient long-running conversat
""" """
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Optional from typing import TYPE_CHECKING, List, Optional
if TYPE_CHECKING:
from pipecat.services.llm_service import LLMService
from loguru import logger from loguru import logger
@@ -78,6 +81,11 @@ class LLMContextSummarizationConfig:
for the generated summary text. Allows applications to wrap the for the generated summary text. Allows applications to wrap the
summary in custom delimiters (e.g., XML tags) so that system summary in custom delimiters (e.g., XML tags) so that system
prompts can distinguish summaries from live conversation. prompts can distinguish summaries from live conversation.
llm: Optional separate LLM service for generating summaries. When set,
summarization requests are sent to this service instead of the
pipeline's primary LLM. Useful for routing summarization to a
cheaper/faster model (e.g., Gemini Flash) while keeping an
expensive model for conversation. If None, uses the pipeline LLM.
""" """
max_context_tokens: int = 8000 max_context_tokens: int = 8000
@@ -86,6 +94,7 @@ class LLMContextSummarizationConfig:
min_messages_after_summary: int = 4 min_messages_after_summary: int = 4
summarization_prompt: Optional[str] = None summarization_prompt: Optional[str] = None
summary_message_template: str = "Conversation summary: {summary}" summary_message_template: str = "Conversation summary: {summary}"
llm: Optional["LLMService"] = None
def __post_init__(self): def __post_init__(self):
"""Validate configuration parameters.""" """Validate configuration parameters."""