From 6a27c262577b612a72db80750b43c1dd9c1338a3 Mon Sep 17 00:00:00 2001 From: Xubin Ren <52506698+Re-bin@users.noreply.github.com> Date: Fri, 26 Jun 2026 11:10:03 +0800 Subject: [PATCH] test: cover non-stream duplicate tool call ids --- tests/agent/test_gemini_thought_signature.py | 42 +++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tests/agent/test_gemini_thought_signature.py b/tests/agent/test_gemini_thought_signature.py index 3928ce11..63039252 100644 --- a/tests/agent/test_gemini_thought_signature.py +++ b/tests/agent/test_gemini_thought_signature.py @@ -11,7 +11,6 @@ from unittest.mock import patch from nanobot.providers.base import ToolCallRequest from nanobot.providers.openai_compat_provider import OpenAICompatProvider - GEMINI_EXTRA = {"google": {"thought_signature": "sig-abc-123"}} @@ -125,6 +124,47 @@ def test_parse_dict_preserves_extra_content() -> None: assert payload["extra_content"] == GEMINI_EXTRA +def test_parse_dict_deduplicates_duplicate_tool_call_ids() -> None: + with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI"): + provider = OpenAICompatProvider() + + response_dict = { + "choices": [ + { + "message": { + "content": None, + "tool_calls": [{ + "id": "call_same", + "type": "function", + "function": {"name": "read_file", "arguments": '{"path":"a.txt"}'}, + }], + }, + "finish_reason": "tool_calls", + }, + { + "message": { + "content": None, + "tool_calls": [{ + "id": "call_same", + "type": "function", + "function": {"name": "read_file", "arguments": '{"path":"b.txt"}'}, + }], + }, + "finish_reason": "tool_calls", + }, + ], + } + + result = provider._parse(response_dict) + + ids = [tc.id for tc in result.tool_calls] + assert len(ids) == 2 + assert ids[0] == "call_same" + assert ids[1] != "call_same" + assert len(set(ids)) == 2 + assert [tc.arguments for tc in result.tool_calls] == [{"path": "a.txt"}, {"path": "b.txt"}] + + # ── _parse_chunks: streaming round-trip ─────────────────────────────── def test_parse_chunks_sdk_preserves_extra_content() -> None: