mirror of
https://github.com/langchain-ai/deepagents.git
synced 2026-07-21 17:25:26 -04:00
fix(sdk): pass through summarization factory prompt knobs (#3559)
Closes #2622 --- Adds keyword-only controls for summarization prompt, trim limit, token counter, and compact-tool system prompt while preserving model-aware trigger/keep defaults. Brings to parity with class init. _Opened collaboratively by Mason Daugherty and open-swe._ Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com> Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com> Co-authored-by: Mason Daugherty <mason@langchain.dev>
This commit is contained in:
@@ -1158,6 +1158,10 @@ This is the name external callers should import and reference.
|
||||
def create_summarization_middleware(
|
||||
model: BaseChatModel,
|
||||
backend: BACKEND_TYPES,
|
||||
*,
|
||||
summary_prompt: str = DEFAULT_SUMMARY_PROMPT,
|
||||
trim_tokens_to_summarize: int | None = None,
|
||||
token_counter: TokenCounter = count_tokens_approximately,
|
||||
) -> _DeepAgentsSummarizationMiddleware:
|
||||
"""Create a Deep Agents `SummarizationMiddleware` with model-aware defaults.
|
||||
|
||||
@@ -1199,6 +1203,9 @@ def create_summarization_middleware(
|
||||
|
||||
Use `resolve_model()` first if needed for model strings.
|
||||
backend: Backend instance or factory for persisting conversation history.
|
||||
summary_prompt: Prompt template for generating summaries.
|
||||
trim_tokens_to_summarize: Max tokens to include when generating summary.
|
||||
token_counter: Function to count tokens in messages.
|
||||
|
||||
Returns:
|
||||
Configured `SummarizationMiddleware` instance.
|
||||
@@ -1218,7 +1225,9 @@ def create_summarization_middleware(
|
||||
backend=backend,
|
||||
trigger=defaults["trigger"],
|
||||
keep=defaults["keep"],
|
||||
trim_tokens_to_summarize=None,
|
||||
token_counter=token_counter,
|
||||
summary_prompt=summary_prompt,
|
||||
trim_tokens_to_summarize=trim_tokens_to_summarize,
|
||||
truncate_args_settings=defaults["truncate_args_settings"],
|
||||
)
|
||||
|
||||
@@ -1226,6 +1235,8 @@ def create_summarization_middleware(
|
||||
def create_summarization_tool_middleware(
|
||||
model: str | BaseChatModel,
|
||||
backend: BACKEND_TYPES,
|
||||
*,
|
||||
system_prompt: str | None = SUMMARIZATION_SYSTEM_PROMPT,
|
||||
) -> SummarizationToolMiddleware:
|
||||
"""Create a `SummarizationToolMiddleware` with model-aware defaults.
|
||||
|
||||
@@ -1257,6 +1268,8 @@ def create_summarization_tool_middleware(
|
||||
model: Chat model instance, or a model string
|
||||
(e.g. `"anthropic:claude-sonnet-4-6"`).
|
||||
backend: Backend instance or factory for persisting conversation history.
|
||||
system_prompt: System-prompt fragment nudging the model to call
|
||||
`compact_conversation`. Pass `None` to skip appending the nudge.
|
||||
|
||||
Returns:
|
||||
Configured `SummarizationToolMiddleware` instance.
|
||||
@@ -1307,7 +1320,7 @@ def create_summarization_tool_middleware(
|
||||
if isinstance(model, str):
|
||||
model = resolve_model(model)
|
||||
summarization = create_summarization_middleware(model, backend)
|
||||
return SummarizationToolMiddleware(summarization)
|
||||
return SummarizationToolMiddleware(summarization, system_prompt=system_prompt)
|
||||
|
||||
|
||||
class SummarizationToolMiddleware(AgentMiddleware):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from inspect import Parameter, signature
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, NonCallableMagicMock, patch
|
||||
|
||||
@@ -640,6 +641,22 @@ def test_create_summarization_tool_middleware_returns_instance() -> None:
|
||||
assert mw.tools[0].name == "compact_conversation"
|
||||
|
||||
|
||||
def test_create_summarization_tool_middleware_accepts_system_prompt() -> None:
|
||||
"""Factory passes explicit `system_prompt` through to the tool middleware."""
|
||||
model = GenericFakeChatModel(messages=iter([AIMessage(content="ok")]))
|
||||
model.profile = {"max_input_tokens": 120_000}
|
||||
mw = create_summarization_tool_middleware(model, MagicMock(), system_prompt="custom nudge")
|
||||
|
||||
assert mw.system_prompt == "custom nudge"
|
||||
|
||||
|
||||
def test_create_summarization_tool_middleware_system_prompt_is_keyword_only() -> None:
|
||||
"""Requires the optional tool nudge to be passed by name."""
|
||||
params = signature(create_summarization_tool_middleware).parameters
|
||||
|
||||
assert params["system_prompt"].kind is Parameter.KEYWORD_ONLY
|
||||
|
||||
|
||||
# --- system_prompt override / suppression --------------------------------
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"""Unit tests for the summarization middleware factory."""
|
||||
|
||||
from collections.abc import Iterable
|
||||
from inspect import Parameter, signature
|
||||
from typing import Any, cast
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_core.messages import AIMessage, MessageLikeRepresentation
|
||||
|
||||
from deepagents.middleware.summarization import create_summarization_middleware
|
||||
from tests.unit_tests.chat_model import GenericFakeChatModel
|
||||
@@ -42,6 +44,35 @@ def test_factory_uses_fallback_defaults_without_profile() -> None:
|
||||
assert middleware._truncate_args_keep == ("messages", 20)
|
||||
|
||||
|
||||
def test_factory_surfaces_summarization_knobs() -> None:
|
||||
"""Passes explicit summarization settings through to the middleware."""
|
||||
model = _make_model(with_profile_limit=120_000)
|
||||
|
||||
def token_counter(messages: Iterable[MessageLikeRepresentation]) -> int:
|
||||
return len(list(messages))
|
||||
|
||||
middleware = create_summarization_middleware(
|
||||
model,
|
||||
cast("Any", MagicMock()),
|
||||
summary_prompt="custom summary prompt: {messages}",
|
||||
trim_tokens_to_summarize=123,
|
||||
token_counter=token_counter,
|
||||
)
|
||||
|
||||
assert middleware._lc_helper.summary_prompt == "custom summary prompt: {messages}"
|
||||
assert middleware._lc_helper.trim_tokens_to_summarize == 123
|
||||
assert middleware._lc_helper.token_counter is token_counter
|
||||
|
||||
|
||||
def test_factory_summarization_knobs_are_keyword_only() -> None:
|
||||
"""Requires optional factory controls to be passed by name."""
|
||||
params = signature(create_summarization_middleware).parameters
|
||||
|
||||
assert params["summary_prompt"].kind is Parameter.KEYWORD_ONLY
|
||||
assert params["trim_tokens_to_summarize"].kind is Parameter.KEYWORD_ONLY
|
||||
assert params["token_counter"].kind is Parameter.KEYWORD_ONLY
|
||||
|
||||
|
||||
def test_factory_rejects_string_model() -> None:
|
||||
"""Raises `TypeError` when called with a string model name."""
|
||||
with pytest.raises(TypeError, match="BaseChatModel"):
|
||||
|
||||
Reference in New Issue
Block a user