From 01d16f1afd24e1150eb2dfdd0747245474cbde06 Mon Sep 17 00:00:00 2001 From: raychen <815315825@qq.com> Date: Thu, 11 Jun 2026 15:11:43 +0800 Subject: [PATCH] =?UTF-8?q?bugfix:=20=E8=A7=A3=E5=86=B3summary=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E6=9D=A1=E4=BB=B6=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 由于summary之后会保留历史数据,通过标志位进行过滤;但是当前的checker函数还是使用的所有数据;存在误判 --- tests/sessions/test_summarizer_checker.py | 102 ++++++++++++++++++ .../sessions/_summarizer_checker.py | 42 +++++++- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/tests/sessions/test_summarizer_checker.py b/tests/sessions/test_summarizer_checker.py index 2eb87e2c..658dddb8 100644 --- a/tests/sessions/test_summarizer_checker.py +++ b/tests/sessions/test_summarizer_checker.py @@ -55,6 +55,13 @@ def _make_event_with_usage(total_tokens: int) -> Event: return event +def _make_summary_event() -> Event: + event = _make_event_with_text("Previous conversation summary") + event.set_summary_event(True) + event.set_model_visible(True) + return event + + def _make_event_with_text(text: str) -> Event: return Event( invocation_id="inv-1", @@ -90,6 +97,29 @@ def test_no_usage_metadata(self): session = _make_session(events=events) assert checker(session) is False + def test_ignores_tokens_before_latest_summary(self): + checker = set_summarizer_token_threshold(100) + old_event = _make_event_with_usage(1000) + summary_event = _make_summary_event() + new_event = _make_event_with_usage(10) + old_event.timestamp = 1.0 + summary_event.timestamp = 2.0 + new_event.timestamp = 3.0 + session = _make_session(events=[old_event, summary_event, new_event]) + + assert checker(session) is False + + def test_ignores_invisible_tokens_after_latest_summary(self): + checker = set_summarizer_token_threshold(100) + summary_event = _make_summary_event() + invisible_event = _make_event_with_usage(1000) + summary_event.timestamp = 1.0 + invisible_event.timestamp = 2.0 + invisible_event.set_model_visible(False) + session = _make_session(events=[summary_event, invisible_event]) + + assert checker(session) is False + class TestEventsCountThreshold: """Test set_summarizer_events_count_threshold.""" @@ -118,6 +148,34 @@ def test_default_threshold(self): session = _make_session(events=events) assert checker(session) is True + def test_counts_only_visible_events_after_latest_summary(self): + checker = set_summarizer_events_count_threshold(2) + old_events = [_make_event_with_text(f"old{i}") for i in range(10)] + summary_event = _make_summary_event() + new_events = [_make_event_with_text("new1"), _make_event_with_text("new2")] + for idx, event in enumerate(old_events): + event.timestamp = float(idx) + summary_event.timestamp = 20.0 + new_events[0].timestamp = 21.0 + new_events[1].timestamp = 22.0 + session = _make_session(events=[*old_events, summary_event, *new_events]) + + assert checker(session) is False + + def test_counts_after_the_latest_summary_event(self): + checker = set_summarizer_events_count_threshold(1) + first_summary = _make_summary_event() + event_after_first_summary = _make_event_with_text("already summarized") + latest_summary = _make_summary_event() + new_event = _make_event_with_text("new") + first_summary.timestamp = 1.0 + event_after_first_summary.timestamp = 2.0 + latest_summary.timestamp = 3.0 + new_event.timestamp = 4.0 + session = _make_session(events=[first_summary, event_after_first_summary, latest_summary, new_event]) + + assert checker(session) is False + class TestTimeIntervalThreshold: """Test set_summarizer_time_interval_threshold.""" @@ -143,6 +201,27 @@ def test_default_threshold(self): session = _make_session(events=[event]) assert checker(session) is False + def test_requires_visible_events_after_latest_summary(self): + checker = set_summarizer_time_interval_threshold(10.0) + old_event = _make_event_with_text("old") + summary_event = _make_summary_event() + old_event.timestamp = time.time() - 100.0 + summary_event.timestamp = time.time() - 20.0 + session = _make_session(events=[old_event, summary_event]) + + assert checker(session) is False + + def test_ignores_invisible_events_after_latest_summary(self): + checker = set_summarizer_time_interval_threshold(10.0) + summary_event = _make_summary_event() + invisible_event = _make_event_with_text("invisible") + summary_event.timestamp = time.time() - 20.0 + invisible_event.timestamp = time.time() - 20.0 + invisible_event.set_model_visible(False) + session = _make_session(events=[summary_event, invisible_event]) + + assert checker(session) is False + class TestImportantContentThreshold: """Test set_summarizer_important_content_threshold.""" @@ -182,6 +261,29 @@ def test_event_with_whitespace_only(self): session = _make_session(events=events) assert checker(session) is False + def test_ignores_important_content_before_latest_summary(self): + checker = set_summarizer_important_content_threshold(5) + old_important_event = _make_event_with_text("This old content is important") + summary_event = _make_summary_event() + new_short_event = _make_event_with_text("short") + old_important_event.timestamp = 1.0 + summary_event.timestamp = 2.0 + new_short_event.timestamp = 3.0 + session = _make_session(events=[old_important_event, summary_event, new_short_event]) + + assert checker(session) is False + + def test_ignores_invisible_important_content_after_latest_summary(self): + checker = set_summarizer_important_content_threshold(5) + summary_event = _make_summary_event() + invisible_important_event = _make_event_with_text("This invisible content is important") + summary_event.timestamp = 1.0 + invisible_important_event.timestamp = 2.0 + invisible_important_event.set_model_visible(False) + session = _make_session(events=[summary_event, invisible_important_event]) + + assert checker(session) is False + class TestConversationThreshold: """Test set_summarizer_conversation_threshold.""" diff --git a/trpc_agent_sdk/sessions/_summarizer_checker.py b/trpc_agent_sdk/sessions/_summarizer_checker.py index bcce1bcc..63a72a90 100644 --- a/trpc_agent_sdk/sessions/_summarizer_checker.py +++ b/trpc_agent_sdk/sessions/_summarizer_checker.py @@ -11,6 +11,7 @@ from typing import Callable from typing import List +from trpc_agent_sdk.events import Event from trpc_agent_sdk.log import logger from ._session import Session @@ -18,6 +19,26 @@ CheckSummarizerFunction = Callable[[Session], bool] +def _latest_summary_event(session: Session) -> Event | None: + """Return the latest summary event in the session, if any.""" + for event in reversed(session.events): + if event.is_summary_event(): + return event + return None + + +def _events_after_latest_summary(session: Session) -> List[Event]: + """Return visible events created after the latest summary event.""" + latest_summary_event = _latest_summary_event(session) + if latest_summary_event is None: + return [event for event in session.events if event.is_model_visible()] + + return [ + event for event in session.events if event.is_model_visible() and not event.is_summary_event() + and event.timestamp > latest_summary_event.timestamp + ] + + def set_summarizer_token_threshold(token_count: int) -> CheckSummarizerFunction: """Set the token threshold for summarizer. @@ -30,7 +51,9 @@ def set_summarizer_token_threshold(token_count: int) -> CheckSummarizerFunction: def _decorator(session: Session) -> bool: # Filter events with usage_metadata - events_with_metadata = [event for event in session.events if event.usage_metadata is not None] + events_with_metadata = [ + event for event in _events_after_latest_summary(session) if event.usage_metadata is not None + ] # If no events have usage_metadata, log a warning and return False if not events_with_metadata: @@ -60,7 +83,7 @@ def set_summarizer_events_count_threshold(event_count: int = 30) -> CheckSummari def _decorator(session: Session) -> bool: # Check if we have enough events to warrant summarization - return len(session.events) > event_count + return len(_events_after_latest_summary(session)) > event_count return _decorator @@ -76,8 +99,17 @@ def set_summarizer_time_interval_threshold(time_interval: float = 300.0) -> Chec """ def _decorator(session: Session) -> bool: - # Check if it's been long enough since the last summarization - return time.time() - session.events[-1].timestamp > time_interval + latest_summary_event = _latest_summary_event(session) + events_after_summary = _events_after_latest_summary(session) + if not events_after_summary: + return False + + if latest_summary_event is not None: + # Only trigger after new visible events have accumulated past the latest summary. + return time.time() - latest_summary_event.timestamp > time_interval + + # No summary exists yet; preserve the original inactivity-based behavior. + return time.time() - events_after_summary[-1].timestamp > time_interval return _decorator @@ -94,7 +126,7 @@ def set_summarizer_important_content_threshold(important_content_count: int = 10 def _decorator(session: Session) -> bool: # Check if there's important content to summarize - for event in session.events: + for event in _events_after_latest_summary(session): if event.content and event.content.parts: for part in event.content.parts: if part.text and len(part.text.strip()) > important_content_count: