Module agents.llm.context.window
Functions
async def generate_summary(llm: "'LLM'", conversation_text: str) ‑> str-
Expand source code
async def generate_summary(llm: "LLM", conversation_text: str) -> str: """Call the LLM to summarize a conversation excerpt.""" summary_ctx = ChatContext() summary_ctx.add_message(role=ChatRole.SYSTEM, content=SUMMARIZE_PROMPT) summary_ctx.add_message(role=ChatRole.USER, content=conversation_text) result_parts = [] try: async for chunk in llm.chat(summary_ctx): if chunk and chunk.content: result_parts.append(chunk.content) except Exception as e: logger.error(f"Error during summary generation: {e}") return "" return "".join(result_parts)Call the LLM to summarize a conversation excerpt.
def render_items(items: list) ‑> str-
Expand source code
def render_items(items: list) -> str: """Render chat items as human-readable text for a summarization prompt.""" lines = [] for item in items: if isinstance(item, ChatMessage) and item.extra.get("summary"): lines.append(f"[Previous Summary]: {_extract_text(item)}") elif isinstance(item, ChatMessage): role_label = item.role.value.capitalize() lines.append(f"{role_label}: {_extract_text(item)}") elif isinstance(item, FunctionCall): lines.append(f"[Tool Call: {item.name}] args={item.arguments}") elif isinstance(item, FunctionCallOutput): output_preview = item.output[:200] if item.output else "" label = "Tool Error" if item.is_error else "Tool Result" lines.append(f"[{label}: {item.name}] {output_preview}") return "\n".join(lines)Render chat items as human-readable text for a summarization prompt.
Classes
class ContextWindow (*,
max_tokens: int | None = None,
max_context_items: int | None = None,
keep_recent_turns: int = 3,
max_tool_calls_per_turn: int = 10,
summary_llm: "'LLM | None'" = None)-
Expand source code
class ContextWindow: """Policy layer that decides *when* to compress/truncate a ChatContext. Compression and truncation primitives live on ChatContext; this class holds the budgets and delegates to them. Args: max_tokens: Maximum estimated token budget. max_context_items: Maximum number of items. keep_recent_turns: Recent user-assistant exchanges kept verbatim during compression. max_tool_calls_per_turn: Retained for compatibility with callers. summary_llm: Optional dedicated LLM for summaries; falls back to the agent's main LLM. """ def __init__( self, *, max_tokens: int | None = None, max_context_items: int | None = None, keep_recent_turns: int = 3, max_tool_calls_per_turn: int = 10, summary_llm: "LLM | None" = None, ) -> None: self.max_tokens = max_tokens self.max_context_items = max_context_items self.max_tool_calls_per_turn = max_tool_calls_per_turn self._keep_recent = keep_recent_turns self._summary_llm = summary_llm async def manage(self, ctx: ChatContext, llm: "LLM") -> None: """Run the full context management cycle: compress then truncate.""" if self._needs_compression(ctx): try: active_llm = self._summary_llm or llm if active_llm is not None: logger.info("Compressing context via summary generation") await ctx.summarize(active_llm, keep_recent_turns=self._keep_recent) logger.info( f"Context compression complete. Size: {len(ctx.items)} items" ) else: logger.warning("No LLM available for context compression") except Exception as e: logger.error(f"Error during context compression: {e}", exc_info=True) if self.max_tokens is not None or self.max_context_items is not None: before = len(ctx.items) ctx.truncate(max_items=self.max_context_items, max_tokens=self.max_tokens) after = len(ctx.items) if after < before: logger.info(f"Truncated context from {before} to {after} items") def _needs_compression(self, ctx: ChatContext) -> bool: """Check if context exceeds budget and has enough old turns to compress.""" est = ctx.estimated_tokens() items = len(ctx.items) turns = ctx.turn_count() exceeds_tokens = self.max_tokens is not None and est > self.max_tokens exceeds_items = ( self.max_context_items is not None and items > self.max_context_items ) enough_turns = turns > self._keep_recent + 1 needed = (exceeds_tokens or exceeds_items) and enough_turns logger.debug( f"Compression check: {est} tokens (max={self.max_tokens}), " f"{items} items (max={self.max_context_items}), {turns} turns, needed={needed}" ) return neededPolicy layer that decides when to compress/truncate a ChatContext.
Compression and truncation primitives live on ChatContext; this class holds the budgets and delegates to them.
Args
max_tokens- Maximum estimated token budget.
max_context_items- Maximum number of items.
keep_recent_turns- Recent user-assistant exchanges kept verbatim during compression.
max_tool_calls_per_turn- Retained for compatibility with callers.
summary_llm- Optional dedicated LLM for summaries; falls back to the agent's main LLM.
Methods
async def manage(self, ctx: ChatContext, llm: "'LLM'") ‑> None-
Expand source code
async def manage(self, ctx: ChatContext, llm: "LLM") -> None: """Run the full context management cycle: compress then truncate.""" if self._needs_compression(ctx): try: active_llm = self._summary_llm or llm if active_llm is not None: logger.info("Compressing context via summary generation") await ctx.summarize(active_llm, keep_recent_turns=self._keep_recent) logger.info( f"Context compression complete. Size: {len(ctx.items)} items" ) else: logger.warning("No LLM available for context compression") except Exception as e: logger.error(f"Error during context compression: {e}", exc_info=True) if self.max_tokens is not None or self.max_context_items is not None: before = len(ctx.items) ctx.truncate(max_items=self.max_context_items, max_tokens=self.max_tokens) after = len(ctx.items) if after < before: logger.info(f"Truncated context from {before} to {after} items")Run the full context management cycle: compress then truncate.