zico-agent / src /agents /memory /windowing.py
github-actions[bot]
Deploy from GitHub Actions: 2ff5de7ae055ac2616ccbfd2ad88672ed21de44e
b7f63db
"""
Conversation windowing — keeps context size bounded.
Strategy:
1. Always preserve the last *max_recent* messages in full.
2. Older messages are summarised into a single system block using a
lightweight (FAST tier) LLM call.
3. If no summariser is provided, older messages are simply dropped.
"""
from __future__ import annotations
import logging
from typing import Any, Dict, List, Optional
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import HumanMessage, SystemMessage
logger = logging.getLogger(__name__)
DEFAULT_MAX_RECENT = 8
def prepare_context(
messages: List[Dict[str, Any]],
*,
max_recent: int = DEFAULT_MAX_RECENT,
summarizer_llm: Optional[BaseChatModel] = None,
) -> List[Dict[str, Any]]:
"""
Prepare a bounded context window from *messages*.
Returns a list of message dicts ready for conversion to LangChain objects.
"""
if len(messages) <= max_recent:
return messages
recent = messages[-max_recent:]
older = messages[:-max_recent]
if summarizer_llm and older:
summary = _summarize(older, summarizer_llm)
if summary:
summary_msg: Dict[str, Any] = {
"role": "system",
"content": f"[Conversation summary so far]\n{summary}",
}
return [summary_msg] + recent
# No summariser → just keep the recent window
return recent
def _summarize(
messages: List[Dict[str, Any]],
llm: BaseChatModel,
) -> Optional[str]:
"""Summarise *messages* into a compact paragraph."""
try:
transcript_lines: list[str] = []
for msg in messages:
role = msg.get("role", "user")
content = (msg.get("content") or "")[:500] # cap per message
transcript_lines.append(f"{role}: {content}")
transcript = "\n".join(transcript_lines[-30:]) # last 30 msgs max
prompt = (
"Summarise the following conversation excerpt in 2-4 concise "
"sentences. Preserve any DeFi operation context (tokens, "
"networks, amounts, actions) if present. Reply in English.\n\n"
f"{transcript}"
)
response = llm.invoke([
SystemMessage(content="You are a concise conversation summariser."),
HumanMessage(content=prompt),
])
text = getattr(response, "content", None)
if isinstance(text, str) and text.strip():
return text.strip()
except Exception:
logger.exception("Conversation summarisation failed; skipping.")
return None