Stop flagging snake_case is_error results as tool errors in OTel span (#2971)
This commit is contained in:
parent
1b1abf6ab6
commit
96bf22e57a
2 changed files with 12 additions and 5 deletions
|
|
@ -59,8 +59,12 @@ class OpenTelemetryMiddleware(ServerMiddleware[Any]):
|
|||
span.set_status(StatusCode.ERROR, str(e))
|
||||
raise
|
||||
if ctx.method == "tools/call":
|
||||
# Tool errors are detected pre-serialization, so only shapes that reach the wire as an error
|
||||
# count: the model, or the camelCase alias (`is_error` is dropped by the alias-only wire
|
||||
# validation). A raw-dict `isError` is matched as a literal bool only - non-bool coercible
|
||||
# values (1, "true") would serialize to an error but are rare enough to leave undetected.
|
||||
match result:
|
||||
case CallToolResult(is_error=True) | {"isError": True} | {"is_error": True}:
|
||||
case CallToolResult(is_error=True) | {"isError": True}:
|
||||
span.set_attribute("error.type", "tool_error")
|
||||
span.set_status(StatusCode.ERROR)
|
||||
case _:
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@ async def test_tool_error_model_result_sets_error_type(server: SrvT, spans: Span
|
|||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tool_error_snake_case_dict_result_sets_error_type(server: SrvT, spans: SpanCapture):
|
||||
async def test_snake_case_dict_result_is_not_a_tool_error(server: SrvT, spans: SpanCapture):
|
||||
# `is_error` is alias-only on the wire, so serialization drops it; the result reaches the
|
||||
# client as a success and the span must not contradict that.
|
||||
async def err_tool(ctx: Ctx, params: CallToolRequestParams) -> dict[str, Any]:
|
||||
return {"content": [], "is_error": True}
|
||||
|
||||
|
|
@ -100,11 +102,12 @@ async def test_tool_error_snake_case_dict_result_sets_error_type(server: SrvT, s
|
|||
server.middleware.append(OpenTelemetryMiddleware())
|
||||
async with connected_runner(server) as (client, _):
|
||||
spans.clear()
|
||||
await client.send_raw_request("tools/call", {"name": "mytool", "arguments": {}})
|
||||
result = await client.send_raw_request("tools/call", {"name": "mytool", "arguments": {}})
|
||||
assert result == {"content": []}
|
||||
[span] = [s for s in spans.finished() if s.kind == SpanKind.SERVER]
|
||||
assert span.attributes is not None
|
||||
assert span.attributes["error.type"] == "tool_error"
|
||||
assert span.status.status_code == StatusCode.ERROR
|
||||
assert "error.type" not in span.attributes
|
||||
assert span.status.status_code == StatusCode.UNSET
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
|
|||
Loading…
Reference in a new issue