Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Internal builds may append content to the Unreleased section.
Only write entries that are worth mentioning to users.
-->

## [Unreleased]

- Tool: Fix FetchURL tool incorrect output when fetching via service fails

## [0.62] - 2025-12-08

- ACP: Fix tool results (including Shell tool output) not being displayed in ACP clients like Zed
Expand Down
62 changes: 37 additions & 25 deletions src/kimi_cli/tools/web/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ def __init__(self, config: Config):

@override
async def __call__(self, params: Params) -> ToolReturnValue:
builder = ToolResultBuilder(max_line_length=None)

if self._service_config:
result = await self._fetch_with_service(params, builder)
if isinstance(result, ToolOk):
return result
logger.warning("Failed to fetch URL via service: {error}", error=result.message)
ret = await self._fetch_with_service(params)
if isinstance(ret, ToolOk):
return ret
logger.warning("Failed to fetch URL via service: {error}", error=ret.message)
# fallback to local fetch if service fetch fails
return await self.fetch_with_http_get(params)

@staticmethod
async def fetch_with_http_get(params: Params) -> ToolReturnValue:
builder = ToolResultBuilder()
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ToolResultBuilder() is now created with default parameters, which includes max_line_length=2000. Previously, the builder was created with max_line_length=None to avoid truncating long lines in web content. This change might cause long lines in fetched content to be truncated.

Consider using ToolResultBuilder(max_line_length=None) here to maintain the previous behavior, especially since:

  1. Web content (HTML/markdown) often has long lines
  2. The SearchWeb tool in the same module uses max_line_length=None for similar reasons
  3. Extracted text from trafilatura can contain long lines
Suggested change
builder = ToolResultBuilder()
builder = ToolResultBuilder(max_line_length=None)

Copilot uses AI. Check for mistakes.
try:
async with (
new_client_session() as session,
Expand Down Expand Up @@ -103,14 +105,13 @@ async def __call__(self, params: Params) -> ToolReturnValue:
builder.write(extracted_text)
return builder.ok("The returned content is the main text content extracted from the page.")

async def _fetch_with_service(
self, params: Params, builder: ToolResultBuilder
) -> ToolReturnValue:
async def _fetch_with_service(self, params: Params) -> ToolReturnValue:
assert self._service_config is not None

tool_call = get_current_tool_call_or_none()
assert tool_call is not None, "Tool call is expected to be set"

builder = ToolResultBuilder()
headers = {
"User-Agent": USER_AGENT,
"Authorization": f"Bearer {self._service_config.api_key.get_secret_value()}",
Expand All @@ -119,20 +120,31 @@ async def _fetch_with_service(
**(self._service_config.custom_headers or {}),
}

async with (
new_client_session() as session,
session.post(
self._service_config.base_url,
headers=headers,
json={"url": params.url},
) as response,
):
if response.status != 200:
return builder.error(
f"Failed to fetch URL via service. Status: {response.status}.",
brief="Failed to fetch URL via fetch service",
)
try:
async with (
new_client_session() as session,
session.post(
self._service_config.base_url,
headers=headers,
json={"url": params.url},
) as response,
):
if response.status != 200:
return builder.error(
f"Failed to fetch URL via service. Status: {response.status}.",
brief="Failed to fetch URL via fetch service",
)

content = await response.text()
builder.write(content)
return builder.ok("The returned content is the main content extracted from the page.")
content = await response.text()
builder.write(content)
return builder.ok(
"The returned content is the main content extracted from the page."
)
except aiohttp.ClientError as e:
return builder.error(
(
f"Failed to fetch URL via service due to network error: {str(e)}. "
"This may indicate the service is unreachable."
),
brief="Network error when calling fetch service",
)
Comment on lines +143 to +150
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new error handling for network errors when calling the fetch service lacks test coverage. While there's a test for successful service calls (test_fetch_url_with_service in tests/test_fetch_url.py), there's no test for when:

  1. The service is unreachable (network error)
  2. The service returns a non-200 status code with the new error path

Consider adding tests to verify:

  • The fallback to HTTP GET works when the service encounters a network error
  • The error message format is correct for network errors
  • The logging behavior (warning message at line 36)

Copilot uses AI. Check for mistakes.
Loading