Skip to content

Commit 7ca8a3b

Browse files
committed
fix: preserve wrapper-compatible compaction delegation
1 parent 6dbdcde commit 7ca8a3b

2 files changed

Lines changed: 68 additions & 5 deletions

File tree

src/agents/memory/openai_responses_compaction_session.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,36 @@
2626
OpenAIResponsesCompactionMode = Literal["previous_response_id", "input", "auto"]
2727

2828

29-
def _method_accepts_wrapper(method: Any) -> bool:
29+
def _method_signature(method: Any) -> tuple[inspect.Parameter, ...]:
3030
try:
31-
parameters = tuple(inspect.signature(method).parameters.values())
31+
return tuple(inspect.signature(method).parameters.values())
3232
except (TypeError, ValueError):
33-
return False
33+
return ()
34+
35+
3436

37+
def _method_accepts_wrapper(method: Any) -> bool:
38+
parameters = _method_signature(method)
3539
return any(
3640
parameter.kind is inspect.Parameter.VAR_KEYWORD or parameter.name == "wrapper"
3741
for parameter in parameters
3842
)
3943

4044

45+
46+
def _method_accepts_limit(method: Any) -> bool:
47+
parameters = _method_signature(method)
48+
return any(
49+
(
50+
parameter.kind
51+
in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY)
52+
and parameter.name == "limit"
53+
)
54+
or parameter.kind is inspect.Parameter.VAR_KEYWORD
55+
for parameter in parameters
56+
)
57+
58+
4159
def select_compaction_candidate_items(
4260
items: list[TResponseInputItem],
4361
) -> list[TResponseInputItem]:
@@ -255,8 +273,17 @@ async def get_items(
255273
limit: int | None = None,
256274
wrapper: RunContextWrapper[Any] | None = None,
257275
) -> list[TResponseInputItem]:
258-
if wrapper is not None and _method_accepts_wrapper(self.underlying_session.get_items):
259-
return await self.underlying_session.get_items(limit, wrapper=wrapper)
276+
accepts_wrapper = wrapper is not None and _method_accepts_wrapper(
277+
self.underlying_session.get_items
278+
)
279+
280+
if limit is None:
281+
if accepts_wrapper:
282+
return await self.underlying_session.get_items(wrapper=wrapper)
283+
return await self.underlying_session.get_items()
284+
285+
if accepts_wrapper and _method_accepts_limit(self.underlying_session.get_items):
286+
return await self.underlying_session.get_items(limit=limit, wrapper=wrapper)
260287
return await self.underlying_session.get_items(limit)
261288

262289
async def _defer_compaction(self, response_id: str, store: bool | None = None) -> None:

tests/memory/test_openai_responses_compaction_session.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,42 @@ def test_init_accepts_valid_model(self) -> None:
107107
)
108108
assert session.model == "gpt-4.1"
109109

110+
@pytest.mark.asyncio
111+
async def test_get_items_preserves_legacy_wrapper_only_delegate_shape(self) -> None:
112+
class WrapperOnlySession:
113+
session_id = "test-session"
114+
115+
def __init__(self) -> None:
116+
self.calls: list[tuple[int | None, Any]] = []
117+
118+
async def get_items(
119+
self,
120+
wrapper: Any = None,
121+
) -> list[TResponseInputItem]:
122+
self.calls.append((None, wrapper))
123+
return []
124+
125+
async def add_items(self, items: list[TResponseInputItem]) -> None:
126+
return None
127+
128+
async def pop_item(self) -> TResponseInputItem | None:
129+
return None
130+
131+
async def clear_session(self) -> None:
132+
return None
133+
134+
underlying = cast(Session, WrapperOnlySession())
135+
session = OpenAIResponsesCompactionSession(
136+
session_id="test",
137+
underlying_session=underlying,
138+
)
139+
140+
wrapper = SimpleNamespace(context={"request_id": "abc"})
141+
items = await session.get_items(wrapper=wrapper)
142+
143+
assert items == []
144+
assert underlying.calls == [(None, wrapper)]
145+
110146
@pytest.mark.asyncio
111147
async def test_add_items_delegates(self) -> None:
112148
mock_session = self.create_mock_session()

0 commit comments

Comments
 (0)