Files
NCBM 7d6e367143 tools: keep tool output whole and let the request-prep pass cap it
run_argv, run_argv_batch, fetch, fetch_local, and the vcs tools now return
their output whole, so the request-prep cap trims it on the wire and names
its document. A fetched page or a long log reads on from the tool message,
with no re-fetch and no re-run.

The run_argv_batch run budget stays as an execution guard: it stops a
runaway batch from spawning further huge-output steps.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-10-29 14:13:36 +08:00

72 lines
2.4 KiB
Python

"""The remote ``fetch`` tool (DeepSeek's Fetch API, Markdown pages)."""
from __future__ import annotations
import pytest
from plyngent.agent.tools import ToolTag
from plyngent.lmproto.deepseek.webtools import FetchResponse
from plyngent.tools.net.fetch_remote import FETCH_TOOL_NAME, build_fetch_tool
class _Client:
"""Stands in for DeepseekWebToolsClient.fetch."""
def __init__(self, *, page: FetchResponse | None = None, error: str | None = None) -> None:
self.page = page or FetchResponse(title="T", content="body")
self.error = error
self.urls: list[str] = []
async def fetch(self, url: str) -> FetchResponse:
self.urls.append(url)
if self.error is not None:
raise RuntimeError(self.error)
return self.page
def _tool(client: _Client):
return build_fetch_tool(client)
def test_definition_shape() -> None:
definition = _tool(_Client())
assert definition.name == FETCH_TOOL_NAME == "fetch"
assert definition.tags == ToolTag.LOCAL | ToolTag.READ_ONLY
properties = definition.parameters["properties"]
assert set(properties) == {"url", "offset"}
assert definition.parameters["required"] == ["url"]
assert "fetch_local" in definition.description
@pytest.mark.asyncio
async def test_handler_renders_title_url_and_content() -> None:
client = _Client(page=FetchResponse(title=" Example article ", content="# Heading\n\nBody."))
result = await _tool(client).handler(url="https://example.com/article")
assert client.urls == ["https://example.com/article"]
assert result.splitlines() == [
"title=Example article",
"url=https://example.com/article",
"chars=16",
"--- content ---",
"# Heading",
"",
"Body.",
]
@pytest.mark.asyncio
async def test_offset_skips_leading_content() -> None:
client = _Client(page=FetchResponse(title="", content="0123456789"))
result = await _tool(client).handler(url="https://example.com/", offset=5)
assert "title=(untitled)" in result
assert "--- content ---\n56789" in result
assert "truncate_token=" not in result
@pytest.mark.asyncio
async def test_handler_reports_a_client_error() -> None:
result = await _tool(_Client(error="HTTP 404: no usable content")).handler(url="https://example.com/x")
assert result == "error: fetch failed: HTTP 404: no usable content"