fix(tools): bound the exception text dispatch writes into its own log line
dispatch() called logger.exception with the exception interpolated into the message. exc_info renders the same exception again in the traceback, so a failing tool wrote its error body to the log twice. Every tool exception passes through this one handler, so a large HTTP error body from any tool landed here at full size. Bound the message copy. The traceback still renders the exception once, which is what an operator needs to place the failure. Same double-write @arimu1 fixed in the vision, image, and TTS handlers in #75938.
This commit is contained in:
parent
84bc430073
commit
ad59d55338
|
|
@ -237,6 +237,27 @@ class TestDispatchBoundsDirectErrorResults:
|
|||
assert reg.dispatch("nested", {}) == payload
|
||||
|
||||
|
||||
class TestDispatchExceptionLogging:
|
||||
def test_raising_handler_logs_bounded_message(self, caplog):
|
||||
import logging
|
||||
body = "upstream said: " + "Q" * 200_000
|
||||
reg = ToolRegistry()
|
||||
reg.register(
|
||||
name="boom",
|
||||
toolset="core",
|
||||
schema=_make_schema("boom"),
|
||||
handler=lambda args, **kw: (_ for _ in ()).throw(RuntimeError(body)),
|
||||
)
|
||||
with caplog.at_level(logging.ERROR, logger="tools.registry"):
|
||||
result = json.loads(reg.dispatch("boom", {}))
|
||||
messages = [r.getMessage() for r in caplog.records]
|
||||
assert messages, "dispatch should log the failure"
|
||||
for message in messages:
|
||||
assert len(message) < _MAX_LOGGED_ERROR_CHARS + 200
|
||||
assert body not in message
|
||||
assert len(result["error"]) < _MAX_TOOL_ERROR_CHARS + 200
|
||||
|
||||
|
||||
class TestToolsetAvailability:
|
||||
def test_no_check_fn_is_available(self):
|
||||
reg = ToolRegistry()
|
||||
|
|
|
|||
|
|
@ -818,7 +818,10 @@ class ToolRegistry:
|
|||
result = entry.handler(args, **kwargs)
|
||||
return self._normalize_handler_result(name, result)
|
||||
except Exception as e:
|
||||
logger.exception("Tool %s dispatch error: %s", name, e)
|
||||
# exc_info already renders the exception, so keep the message copy bounded.
|
||||
logger.exception(
|
||||
"Tool %s dispatch error: %s", name, _bound_error_text(str(e))
|
||||
)
|
||||
# Route through the sanitizer so framing tokens / CDATA / fences
|
||||
# in exception strings don't reach the model as structural noise.
|
||||
# See model_tools._sanitize_tool_error for rationale.
|
||||
|
|
|
|||
Loading…
Reference in New Issue