mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-22 09:32:33 +00:00
test(provider): cover duplicate streaming tool call ids
This commit is contained in:
parent
77ec55bf8e
commit
3d3ebf1110
@ -10,7 +10,6 @@ from dataclasses import dataclass, field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Awaitable, Callable
|
from typing import Any, Awaitable, Callable
|
||||||
|
|
||||||
|
|
||||||
TRACKED_FILE_EDIT_TOOLS = frozenset({"write_file", "edit_file", "notebook_edit"})
|
TRACKED_FILE_EDIT_TOOLS = frozenset({"write_file", "edit_file", "notebook_edit"})
|
||||||
_MAX_SNAPSHOT_BYTES = 2 * 1024 * 1024
|
_MAX_SNAPSHOT_BYTES = 2 * 1024 * 1024
|
||||||
_LIVE_EMIT_INTERVAL_S = 0.18
|
_LIVE_EMIT_INTERVAL_S = 0.18
|
||||||
|
|||||||
@ -56,6 +56,35 @@ def test_custom_provider_parse_chunks_accepts_plain_text_chunks() -> None:
|
|||||||
assert result.content == "hello world"
|
assert result.content == "hello world"
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_provider_parse_chunks_deduplicates_parallel_tool_call_ids() -> None:
|
||||||
|
chunks = [{
|
||||||
|
"choices": [{
|
||||||
|
"finish_reason": "tool_calls",
|
||||||
|
"delta": {
|
||||||
|
"tool_calls": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"id": "call_dup",
|
||||||
|
"function": {"name": "read_file", "arguments": '{"path":"a.txt"}'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"id": "call_dup",
|
||||||
|
"function": {"name": "read_file", "arguments": '{"path":"b.txt"}'},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}]
|
||||||
|
|
||||||
|
result = OpenAICompatProvider._parse_chunks(chunks)
|
||||||
|
ids = [tool_call.id for tool_call in result.tool_calls or []]
|
||||||
|
|
||||||
|
assert ids[0] == "call_dup"
|
||||||
|
assert len(ids) == 2
|
||||||
|
assert len(set(ids)) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_local_provider_502_error_includes_reachability_hint() -> None:
|
def test_local_provider_502_error_includes_reachability_hint() -> None:
|
||||||
spec = find_by_name("ollama")
|
spec = find_by_name("ollama")
|
||||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI"):
|
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI"):
|
||||||
|
|||||||
@ -5,12 +5,12 @@ from pathlib import Path
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
from nanobot.utils.file_edit_events import (
|
from nanobot.utils.file_edit_events import (
|
||||||
|
StreamingFileEditTracker,
|
||||||
build_file_edit_end_event,
|
build_file_edit_end_event,
|
||||||
build_file_edit_start_event,
|
build_file_edit_start_event,
|
||||||
line_diff_stats,
|
line_diff_stats,
|
||||||
prepare_file_edit_tracker,
|
prepare_file_edit_tracker,
|
||||||
read_file_snapshot,
|
read_file_snapshot,
|
||||||
StreamingFileEditTracker,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -308,6 +308,43 @@ def test_streaming_tracker_applies_canonical_call_id_to_final_tool(tmp_path: Pat
|
|||||||
asyncio.run(run())
|
asyncio.run(run())
|
||||||
|
|
||||||
|
|
||||||
|
def test_streaming_tracker_does_not_restore_duplicate_canonical_ids(tmp_path: Path) -> None:
|
||||||
|
events: list[dict] = []
|
||||||
|
|
||||||
|
async def emit(batch: list[dict]) -> None:
|
||||||
|
events.extend(batch)
|
||||||
|
|
||||||
|
async def run() -> None:
|
||||||
|
tracker = StreamingFileEditTracker(workspace=tmp_path, tools={}, emit=emit)
|
||||||
|
await tracker.update({
|
||||||
|
"index": 0,
|
||||||
|
"call_id": "call_dup",
|
||||||
|
"name": "write_file",
|
||||||
|
"arguments_delta": '{"path":"a.md","content":"one\\n"}',
|
||||||
|
})
|
||||||
|
await tracker.update({
|
||||||
|
"index": 1,
|
||||||
|
"call_id": "call_dup",
|
||||||
|
"name": "write_file",
|
||||||
|
"arguments_delta": '{"path":"b.md","content":"two\\n"}',
|
||||||
|
})
|
||||||
|
final_a = SimpleNamespace(
|
||||||
|
id="call_dup",
|
||||||
|
name="write_file",
|
||||||
|
arguments={"path": "a.md", "content": "one\n"},
|
||||||
|
)
|
||||||
|
final_b = SimpleNamespace(
|
||||||
|
id="call_unique",
|
||||||
|
name="write_file",
|
||||||
|
arguments={"path": "b.md", "content": "two\n"},
|
||||||
|
)
|
||||||
|
tracker.apply_final_call_ids([final_a, final_b])
|
||||||
|
assert final_a.id == "call_dup"
|
||||||
|
assert final_b.id == "call_unique"
|
||||||
|
|
||||||
|
asyncio.run(run())
|
||||||
|
|
||||||
|
|
||||||
def test_streaming_edit_file_tracker_flushes_small_pending_count(tmp_path: Path) -> None:
|
def test_streaming_edit_file_tracker_flushes_small_pending_count(tmp_path: Path) -> None:
|
||||||
target = tmp_path / "small.py"
|
target = tmp_path / "small.py"
|
||||||
target.write_text("old\n", encoding="utf-8")
|
target.write_text("old\n", encoding="utf-8")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user