From 4c29fbd03d520afdb42148c47f880d11adf6a2da Mon Sep 17 00:00:00 2001 From: Anish Mehta Date: Wed, 7 Oct 2026 05:28:27 +0530 Subject: [PATCH 1/2] Python: read checkpoint item ids in DevUI list_items pagination --- .../agent_framework_devui/_conversations.py | 4 ++- .../devui/tests/devui/test_conversations.py | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/python/packages/devui/agent_framework_devui/_conversations.py b/python/packages/devui/agent_framework_devui/_conversations.py index 8a2d955726f..76a8f348eb1 100644 --- a/python/packages/devui/agent_framework_devui/_conversations.py +++ b/python/packages/devui/agent_framework_devui/_conversations.py @@ -516,7 +516,9 @@ async def list_items( if after: # Find the index after the cursor for i, item in enumerate(items): - if item.id == after: + # Checkpoint items are plain dicts, not models. + item_id = cast(dict[str, Any], item).get("id") if isinstance(item, dict) else item.id + if item_id == after: start_idx = i + 1 break diff --git a/python/packages/devui/tests/devui/test_conversations.py b/python/packages/devui/tests/devui/test_conversations.py index 0639a6eed44..afdfe748aa8 100644 --- a/python/packages/devui/tests/devui/test_conversations.py +++ b/python/packages/devui/tests/devui/test_conversations.py @@ -5,6 +5,7 @@ from typing import cast import pytest +from agent_framework import WorkflowCheckpoint from openai.types.conversations import InputTextContent from openai.types.conversations.message import Message as OpenAIMessage from openai.types.responses import ResponseInputFile, ResponseInputImage, ResponseOutputRefusal @@ -369,6 +370,34 @@ async def test_list_items_pagination(): assert has_more is True +@pytest.mark.asyncio +@pytest.mark.parametrize("order", ["asc", "desc"]) +async def test_list_items_pagination_with_checkpoint_items(order: str): + """Paging with `after` must not fail when checkpoint items (plain dicts) are listed.""" + store = InMemoryConversationStore() + conversation = store.create_conversation() + await store.add_items( + conversation.id, + items=[{"role": "user", "content": "first"}, {"role": "assistant", "content": "second"}], + ) + storage = store._conversations[conversation.id]["checkpoint_storage"] + for _ in range(2): + await storage.save(WorkflowCheckpoint(workflow_name="workflow", graph_signature_hash="hash")) + + all_items, _ = await store.list_items(conversation.id, order=order) + all_ids = [item["id"] if isinstance(item, dict) else item.id for item in all_items] + + paged_ids: list[str] = [] + after = None + has_more = True + while has_more: + page, has_more = await store.list_items(conversation.id, limit=1, after=after, order=order) + after = page[-1]["id"] if isinstance(page[-1], dict) else page[-1].id + paged_ids.append(after) + + assert paged_ids == all_ids + + @pytest.mark.asyncio async def test_list_items_converts_function_calls(): """Test that list_items properly converts function calls to ResponseFunctionToolCallItem.""" From a8ebd3b41f562600e3096e7f013e53098b850615 Mon Sep 17 00:00:00 2001 From: Anish Mehta Date: Thu, 8 Oct 2026 03:26:00 +0530 Subject: [PATCH 2/2] Python: fix type checker errors in the checkpoint pagination test --- .../packages/devui/tests/devui/test_conversations.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/python/packages/devui/tests/devui/test_conversations.py b/python/packages/devui/tests/devui/test_conversations.py index afdfe748aa8..3eb80a881ca 100644 --- a/python/packages/devui/tests/devui/test_conversations.py +++ b/python/packages/devui/tests/devui/test_conversations.py @@ -384,15 +384,19 @@ async def test_list_items_pagination_with_checkpoint_items(order: str): for _ in range(2): await storage.save(WorkflowCheckpoint(workflow_name="workflow", graph_signature_hash="hash")) + def item_id(item: object) -> str: + # Checkpoint items are plain dicts at runtime; the rest are models. + return cast(str, item["id"] if isinstance(item, dict) else getattr(item, "id")) + all_items, _ = await store.list_items(conversation.id, order=order) - all_ids = [item["id"] if isinstance(item, dict) else item.id for item in all_items] + all_ids = [item_id(item) for item in all_items] paged_ids: list[str] = [] - after = None + after: str | None = None has_more = True while has_more: page, has_more = await store.list_items(conversation.id, limit=1, after=after, order=order) - after = page[-1]["id"] if isinstance(page[-1], dict) else page[-1].id + after = item_id(page[-1]) paged_ids.append(after) assert paged_ids == all_ids