Files
2026-07-13 13:12:33 +08:00

207 lines
5.7 KiB
Python

from __future__ import annotations
import pytest
from opensquilla.result_budget import (
ToolRunBudgetExceededError,
ToolRunBudgetPolicy,
ToolRunBudgetTracker,
clamp_tool_arguments,
)
@pytest.mark.asyncio
async def test_web_search_counts_as_external_search() -> None:
tracker = ToolRunBudgetTracker()
reservation = await tracker.reserve_tool_call(
tool_name="web_search",
arguments={"query": "python release", "max_results": 10, "fetch_top_k": 3},
)
await tracker.commit_tool_result(reservation, "x" * 100)
snapshot = await tracker.snapshot()
assert reservation.counted_as_search is True
assert reservation.counted_as_fetch is False
assert reservation.counted_as_external_text is True
assert snapshot["web_search_calls_used"] == 1
assert snapshot["web_fetch_calls_used"] == 0
assert snapshot["external_text_chars_used"] == 100
@pytest.mark.asyncio
async def test_web_discover_counts_as_external_search_budget() -> None:
tracker = ToolRunBudgetTracker(ToolRunBudgetPolicy(max_web_search_calls_per_turn=1))
reservation = await tracker.reserve_tool_call(
tool_name="web_discover",
arguments={"query": "python release", "max_results": 5},
)
snapshot = await tracker.snapshot()
assert reservation.counted_as_search is True
assert reservation.counted_as_fetch is False
assert snapshot["web_search_calls_used"] == 1
with pytest.raises(ToolRunBudgetExceededError) as exc_info:
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={"query": "another search"},
)
assert exc_info.value.tool_name == "web_search"
def test_web_clamps_leave_bool_arguments_for_validation() -> None:
search_args = {
"query": "q",
"max_results": True,
"fetch_top_k": True,
"max_chars_per_source": False,
}
discover_args = {"query": "q", "max_results": True}
fetch_args = {"url": "https://example.com", "max_chars": False}
assert (
clamp_tool_arguments(
"web_search",
search_args,
ToolRunBudgetPolicy(max_web_search_results=8),
)
== search_args
)
assert (
clamp_tool_arguments(
"web_discover",
discover_args,
ToolRunBudgetPolicy(max_web_search_results=8),
)
== discover_args
)
assert (
clamp_tool_arguments(
"web_fetch",
fetch_args,
ToolRunBudgetPolicy(max_single_fetch_chars=900),
)
== fetch_args
)
def test_web_search_clamps_source_backed_arguments() -> None:
clamped = clamp_tool_arguments(
"web_search",
{
"query": "q",
"max_results": 1000,
"fetch_top_k": 1000,
"max_chars_per_source": 1000,
},
ToolRunBudgetPolicy(
max_web_search_results=8,
max_web_search_fetch_top_k=2,
max_web_search_chars_per_source=900,
),
)
assert clamped == {
"query": "q",
"max_results": 8,
"fetch_top_k": 2,
"max_chars_per_source": 900,
}
@pytest.mark.asyncio
async def test_loop_guard_blocks_repeated_identical_web_search() -> None:
tracker = ToolRunBudgetTracker(
ToolRunBudgetPolicy(max_repeated_retrievals_per_turn=2)
)
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={
"query": " Python Release ",
"provider": "tavily",
"mode": "auto",
},
)
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={"query": "python release", "provider": "tavily", "mode": "auto"},
)
with pytest.raises(ToolRunBudgetExceededError) as exc_info:
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={
"query": "PYTHON RELEASE",
"provider": "tavily",
"mode": "auto",
},
)
assert exc_info.value.tool_name == "web_search"
assert "repeated retrieval" in str(exc_info.value).lower()
@pytest.mark.asyncio
async def test_loop_guard_counts_web_search_repeated_queries_too() -> None:
tracker = ToolRunBudgetTracker(
ToolRunBudgetPolicy(max_repeated_retrievals_per_turn=1)
)
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={"query": "OpenSquilla"},
)
with pytest.raises(ToolRunBudgetExceededError):
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={"query": " opensquilla "},
)
@pytest.mark.asyncio
async def test_loop_guard_counts_web_discover_repeated_queries_too() -> None:
tracker = ToolRunBudgetTracker(
ToolRunBudgetPolicy(max_repeated_retrievals_per_turn=1)
)
await tracker.reserve_tool_call(
tool_name="web_discover",
arguments={"query": "OpenSquilla"},
)
with pytest.raises(ToolRunBudgetExceededError):
await tracker.reserve_tool_call(
tool_name="web_discover",
arguments={"query": " opensquilla "},
)
@pytest.mark.asyncio
async def test_loop_guard_snapshot_exposes_counts() -> None:
tracker = ToolRunBudgetTracker()
await tracker.reserve_tool_call(
tool_name="web_search",
arguments={
"query": "Python Release",
"provider": "tavily",
"mode": "news",
},
)
snapshot = await tracker.snapshot()
assert snapshot["retrieval_loop_guard"] == [
{
"tool_name": "web_search",
"query": "python release",
"provider": "tavily",
"mode": "news",
"count": 1,
}
]