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

150 lines
4.8 KiB
Python

from __future__ import annotations
import json
import httpx
from opensquilla.tools.envelope import build_tool_failure_envelope
from opensquilla.tools.types import RetryableToolInputError, SafeToolError
def test_type_error_tool_failure_is_model_retriable_without_raw_traceback() -> None:
envelope = build_tool_failure_envelope(
TypeError("secret raw argument detail"),
"write_file",
)
assert envelope["status"] == "error"
assert envelope["tool"] == "write_file"
assert envelope["error_class"] == "TypeError"
assert envelope["retry_allowed"] is True
assert "tool schema" in envelope["user_message"]
assert "secret raw argument detail" not in envelope["user_message"]
def test_httpx_timeout_exception_is_retriable_with_specific_message() -> None:
envelope = build_tool_failure_envelope(
httpx.TimeoutException("secret endpoint"),
"web_fetch",
)
assert envelope["error_class"] == "TimeoutException"
assert envelope["retry_allowed"] is True
assert "timed out" in envelope["user_message"]
assert "secret endpoint" not in envelope["user_message"]
def test_httpx_connect_error_is_retriable_with_specific_message() -> None:
envelope = build_tool_failure_envelope(
httpx.ConnectError("secret host"),
"web_fetch",
)
assert envelope["error_class"] == "ConnectError"
assert envelope["retry_allowed"] is True
assert "could not connect" in envelope["user_message"]
assert "secret host" not in envelope["user_message"]
def test_json_decode_error_has_specific_message() -> None:
envelope = build_tool_failure_envelope(
json.JSONDecodeError("secret payload", "not-json", 0),
"web_fetch",
)
assert envelope["error_class"] == "JSONDecodeError"
assert envelope["retry_allowed"] is False
assert "invalid response payload" in envelope["user_message"]
assert "secret payload" not in envelope["user_message"]
def test_policy_denial_envelope_has_exactly_five_keys() -> None:
envelope = build_tool_failure_envelope(
PermissionError("raw denied detail"),
"exec_command",
policy_denial=True,
error_class_override="PolicyDenied",
user_message_override="Blocked by policy.",
)
assert set(envelope) == {
"status",
"tool",
"error_class",
"user_message",
"retry_allowed",
}
assert envelope["status"] == "error"
assert envelope["tool"] == "exec_command"
assert envelope["error_class"] == "PolicyDenied"
assert envelope["user_message"] == "Blocked by policy."
assert envelope["retry_allowed"] is False
def test_safe_tool_error_instance_message_preserves_five_key_shape() -> None:
envelope = build_tool_failure_envelope(
SafeToolError("PDF file not found: input.pdf (resolved=/workspace/input.pdf)", "secret"),
"pdf",
)
assert set(envelope) == {
"status",
"tool",
"error_class",
"user_message",
"retry_allowed",
}
assert envelope["status"] == "error"
assert envelope["tool"] == "pdf"
assert envelope["error_class"] == "SafeToolError"
assert "PDF file not found" in envelope["user_message"]
assert "secret" not in envelope["user_message"]
def test_retryable_tool_input_error_marks_model_correctable_failures() -> None:
envelope = build_tool_failure_envelope(
RetryableToolInputError(
"edit_file could not find old_text. Read the file and retry with exact text."
),
"edit_file",
)
assert set(envelope) == {
"status",
"tool",
"error_class",
"user_message",
"retry_allowed",
}
assert envelope["tool"] == "edit_file"
assert envelope["error_class"] == "RetryableToolInputError"
assert envelope["retry_allowed"] is True
assert "retry with exact text" in envelope["user_message"]
def test_image_attachment_path_safe_tool_error_is_not_generic_internal_error() -> None:
envelope = build_tool_failure_envelope(
SafeToolError(
"Image path is not accessible by the image tool: ab367.png. "
"Pass a real local file path or HTTP(S) URL. If this is a chat attachment, "
"answer from the attached image directly instead of calling the image tool.",
"resolved=/secret/workspace/ab367.png",
),
"image",
)
assert set(envelope) == {
"status",
"tool",
"error_class",
"user_message",
"retry_allowed",
}
assert envelope["tool"] == "image"
assert envelope["error_class"] == "SafeToolError"
assert envelope["retry_allowed"] is False
assert "not accessible by the image tool" in envelope["user_message"]
assert "chat attachment" in envelope["user_message"]
assert "internal error" not in envelope["user_message"]
assert "secret" not in envelope["user_message"]