fix: use coerce_tag_value in LoggingTracer to serialize tag values (#9251)

* fix: use coerce_tag_value in LoggingTracer to serialize tag values

* add rn

* fix tests

---------

Co-authored-by: Sebastian Husch Lee <sjrl@users.noreply.github.com>
This commit is contained in:
Mohammed Abdul Razak Wahab 2025-04-22 19:48:24 +05:30 committed by GitHub
parent c1e4ea0c52
commit ddd7318ae8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 107 deletions

View File

@ -8,6 +8,7 @@ from typing import Any, Dict, Iterator, Optional
from haystack import logging
from haystack.tracing import Span, Tracer
from haystack.tracing.utils import coerce_tag_value
logger = logging.getLogger(__name__)
@ -75,8 +76,9 @@ class LoggingTracer(Tracer):
logger.debug("Operation: {operation_name}", operation_name=operation_name)
for tag_name, tag_value in tags.items():
color_string = self.tags_color_strings.get(tag_name, "")
coerced_value = coerce_tag_value(tag_value)
logger.debug(
color_string + "{tag_name}={tag_value}" + RESET_COLOR, tag_name=tag_name, tag_value=tag_value
color_string + "{tag_name}={tag_value}" + RESET_COLOR, tag_name=tag_name, tag_value=coerced_value
)
def current_span(self) -> Optional[Span]:

View File

@ -0,0 +1,4 @@
---
fixes:
- |
use coerce_tag_value in LoggingTracer to serialize tag values

View File

@ -817,59 +817,18 @@ class TestAgentTracing:
expected_tag_values = [
"chat_generator",
"MockChatGeneratorWithoutRunAsync",
{"messages": "list", "tools": "list"},
{},
{},
{
"messages": [ChatMessage.from_user(text="What's the weather in Paris?")],
"tools": [
Tool(
name="weather_tool",
description="Provides weather information for a given location.",
parameters={
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
function=weather_function,
outputs_to_string=None,
inputs_from_state=None,
outputs_to_state=None,
)
],
},
'{"messages": "list", "tools": "list"}',
"{}",
"{}",
'{"messages": [{"role": "user", "meta": {}, "name": null, "content": [{"text": "What\'s the weather in Paris?"}]}], "tools": [{"type": "haystack.tools.tool.Tool", "data": {"name": "weather_tool", "description": "Provides weather information for a given location.", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}, "function": "test_agent.weather_function", "outputs_to_string": null, "inputs_from_state": null, "outputs_to_state": null}}]}',
1,
{"replies": [ChatMessage.from_assistant(text="Hello")]},
'{"replies": [{"role": "assistant", "meta": {}, "name": null, "content": [{"text": "Hello"}]}]}',
100,
[
Tool(
name="weather_tool",
description="Provides weather information for a given location.",
parameters={
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
function=weather_function,
outputs_to_string=None,
inputs_from_state=None,
outputs_to_state=None,
)
],
["text"],
{
"messages": {
"type": "typing.List[haystack.dataclasses.chat_message.ChatMessage]",
"handler": "haystack.dataclasses.state_utils.merge_lists",
}
},
{"messages": [ChatMessage.from_user(text="What's the weather in Paris?")], "streaming_callback": None},
{
"messages": [
ChatMessage.from_user(text="What's the weather in Paris?"),
ChatMessage.from_assistant(text="Hello"),
]
},
'[{"type": "haystack.tools.tool.Tool", "data": {"name": "weather_tool", "description": "Provides weather information for a given location.", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}, "function": "test_agent.weather_function", "outputs_to_string": null, "inputs_from_state": null, "outputs_to_state": null}}]',
'["text"]',
'{"messages": {"type": "typing.List[haystack.dataclasses.chat_message.ChatMessage]", "handler": "haystack.dataclasses.state_utils.merge_lists"}}',
'{"messages": [{"role": "user", "meta": {}, "name": null, "content": [{"text": "What\'s the weather in Paris?"}]}], "streaming_callback": null}',
'{"messages": [{"role": "user", "meta": {}, "name": null, "content": [{"text": "What\'s the weather in Paris?"}]}, {"role": "assistant", "meta": {}, "name": null, "content": [{"text": "Hello"}]}]}',
1,
]
for idx, record in enumerate(tags_records):
@ -918,59 +877,18 @@ class TestAgentTracing:
expected_tag_values = [
"chat_generator",
"MockChatGeneratorWithRunAsync",
{"messages": "list", "tools": "list"},
{},
{},
{
"messages": [ChatMessage.from_user(text="What's the weather in Paris?")],
"tools": [
Tool(
name="weather_tool",
description="Provides weather information for a given location.",
parameters={
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
function=weather_function,
outputs_to_string=None,
inputs_from_state=None,
outputs_to_state=None,
)
],
},
'{"messages": "list", "tools": "list"}',
"{}",
"{}",
'{"messages": [{"role": "user", "meta": {}, "name": null, "content": [{"text": "What\'s the weather in Paris?"}]}], "tools": [{"type": "haystack.tools.tool.Tool", "data": {"name": "weather_tool", "description": "Provides weather information for a given location.", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}, "function": "test_agent.weather_function", "outputs_to_string": null, "inputs_from_state": null, "outputs_to_state": null}}]}',
1,
{"replies": [ChatMessage.from_assistant(text="Hello from run_async")]},
'{"replies": [{"role": "assistant", "meta": {}, "name": null, "content": [{"text": "Hello from run_async"}]}]}',
100,
[
Tool(
name="weather_tool",
description="Provides weather information for a given location.",
parameters={
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
function=weather_function,
outputs_to_string=None,
inputs_from_state=None,
outputs_to_state=None,
)
],
["text"],
{
"messages": {
"type": "typing.List[haystack.dataclasses.chat_message.ChatMessage]",
"handler": "haystack.dataclasses.state_utils.merge_lists",
}
},
{"messages": [ChatMessage.from_user(text="What's the weather in Paris?")], "streaming_callback": None},
{
"messages": [
ChatMessage.from_user(text="What's the weather in Paris?"),
ChatMessage.from_assistant(text="Hello from run_async"),
]
},
'[{"type": "haystack.tools.tool.Tool", "data": {"name": "weather_tool", "description": "Provides weather information for a given location.", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}, "function": "test_agent.weather_function", "outputs_to_string": null, "inputs_from_state": null, "outputs_to_state": null}}]',
'["text"]',
'{"messages": {"type": "typing.List[haystack.dataclasses.chat_message.ChatMessage]", "handler": "haystack.dataclasses.state_utils.merge_lists"}}',
'{"messages": [{"role": "user", "meta": {}, "name": null, "content": [{"text": "What\'s the weather in Paris?"}]}], "streaming_callback": null}',
'{"messages": [{"role": "user", "meta": {}, "name": null, "content": [{"text": "What\'s the weather in Paris?"}]}, {"role": "assistant", "meta": {}, "name": null, "content": [{"text": "Hello from run_async"}]}]}',
1,
]
for idx, record in enumerate(tags_records):

View File

@ -58,13 +58,13 @@ class TestLoggingTracer:
span.set_tag("key", {"a": 1, "b": [2, 3, 4]})
assert "Operation: test" in caplog.text
assert "key={'a': 1, 'b': [2, 3, 4]}" in caplog.text
assert 'key={"a": 1, "b": [2, 3, 4]}' in caplog.text
assert len(caplog.records) == 2
# structured logging
assert caplog.records[0].operation_name == "test"
assert caplog.records[1].tag_name == "key"
assert caplog.records[1].tag_value == {"a": 1, "b": [2, 3, 4]}
assert caplog.records[1].tag_value == '{"a": 1, "b": [2, 3, 4]}'
def test_apply_color_strings(self, caplog) -> None:
tracer = LoggingTracer(tags_color_strings={"key": "color_string"})
@ -119,12 +119,12 @@ class TestLoggingTracer:
input_tag_value = [
record.tag_value for record in tags_records if record.tag_name == "haystack.component.input"
][0]
assert input_tag_value == {"word": "world"}
assert input_tag_value == '{"word": "world"}'
output_tag_value = [
record.tag_value for record in tags_records if record.tag_name == "haystack.component.output"
][0]
assert output_tag_value == {"output": "Hello, world!"}
assert output_tag_value == '{"output": "Hello, world!"}'
tracing.tracer.is_content_tracing_enabled = False
tracing.disable_tracing()