Merge pull request #1886 from pipecat-ai/mb/add-otel-llm-output
Add LLM response tracing to OTel tracing
This commit is contained in:
@@ -171,6 +171,7 @@ def add_llm_span_attributes(
|
|||||||
model: str,
|
model: str,
|
||||||
stream: bool = True,
|
stream: bool = True,
|
||||||
messages: Optional[str] = None,
|
messages: Optional[str] = None,
|
||||||
|
output: Optional[str] = None,
|
||||||
tools: Optional[str] = None,
|
tools: Optional[str] = None,
|
||||||
tool_count: Optional[int] = None,
|
tool_count: Optional[int] = None,
|
||||||
tool_choice: Optional[str] = None,
|
tool_choice: Optional[str] = None,
|
||||||
@@ -188,6 +189,7 @@ def add_llm_span_attributes(
|
|||||||
model: Model name/identifier
|
model: Model name/identifier
|
||||||
stream: Whether streaming is enabled
|
stream: Whether streaming is enabled
|
||||||
messages: JSON-serialized messages
|
messages: JSON-serialized messages
|
||||||
|
output: Aggregated output text from the LLM
|
||||||
tools: JSON-serialized tools configuration
|
tools: JSON-serialized tools configuration
|
||||||
tool_count: Number of tools available
|
tool_count: Number of tools available
|
||||||
tool_choice: Tool selection configuration
|
tool_choice: Tool selection configuration
|
||||||
@@ -208,6 +210,9 @@ def add_llm_span_attributes(
|
|||||||
if messages:
|
if messages:
|
||||||
span.set_attribute("input", messages)
|
span.set_attribute("input", messages)
|
||||||
|
|
||||||
|
if output:
|
||||||
|
span.set_attribute("output", output)
|
||||||
|
|
||||||
if tools:
|
if tools:
|
||||||
span.set_attribute("tools", tools)
|
span.set_attribute("tools", tools)
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) -
|
|||||||
- Tool configurations
|
- Tool configurations
|
||||||
- Token usage metrics
|
- Token usage metrics
|
||||||
- Performance metrics like TTFB
|
- Performance metrics like TTFB
|
||||||
|
- Aggregated output text
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
func: The LLM method to trace.
|
func: The LLM method to trace.
|
||||||
@@ -313,6 +314,26 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) -
|
|||||||
span_name, context=parent_context
|
span_name, context=parent_context
|
||||||
) as current_span:
|
) as current_span:
|
||||||
try:
|
try:
|
||||||
|
# Store original method and output aggregator
|
||||||
|
original_push_frame = self.push_frame
|
||||||
|
output_text = "" # Simple string accumulation
|
||||||
|
|
||||||
|
async def traced_push_frame(frame, direction=None):
|
||||||
|
nonlocal output_text
|
||||||
|
# Capture text from LLMTextFrame during streaming
|
||||||
|
if (
|
||||||
|
hasattr(frame, "__class__")
|
||||||
|
and frame.__class__.__name__ == "LLMTextFrame"
|
||||||
|
and hasattr(frame, "text")
|
||||||
|
):
|
||||||
|
output_text += frame.text
|
||||||
|
|
||||||
|
# Call original
|
||||||
|
if direction is not None:
|
||||||
|
return await original_push_frame(frame, direction)
|
||||||
|
else:
|
||||||
|
return await original_push_frame(frame)
|
||||||
|
|
||||||
# For token usage monitoring
|
# For token usage monitoring
|
||||||
original_start_llm_usage_metrics = None
|
original_start_llm_usage_metrics = None
|
||||||
if hasattr(self, "start_llm_usage_metrics"):
|
if hasattr(self, "start_llm_usage_metrics"):
|
||||||
@@ -331,6 +352,9 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) -
|
|||||||
self.start_llm_usage_metrics = wrapped_start_llm_usage_metrics
|
self.start_llm_usage_metrics = wrapped_start_llm_usage_metrics
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Replace push_frame to capture output
|
||||||
|
self.push_frame = traced_push_frame
|
||||||
|
|
||||||
# Detect if we're using Google's service
|
# Detect if we're using Google's service
|
||||||
is_google_service = "google" in service_class_name.lower()
|
is_google_service = "google" in service_class_name.lower()
|
||||||
|
|
||||||
@@ -411,13 +435,24 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) -
|
|||||||
|
|
||||||
# Add all gathered attributes to the span
|
# Add all gathered attributes to the span
|
||||||
add_llm_span_attributes(span=current_span, **attribute_kwargs)
|
add_llm_span_attributes(span=current_span, **attribute_kwargs)
|
||||||
except Exception as e:
|
|
||||||
logging.warning(f"Error adding initial LLM attributes: {e}")
|
|
||||||
|
|
||||||
# Call the original function
|
except Exception as e:
|
||||||
return await f(self, context, *args, **kwargs)
|
logging.warning(f"Error setting up LLM tracing: {e}")
|
||||||
|
# Don't raise - let the function execute anyway
|
||||||
|
|
||||||
|
# Run function with modified push_frame to capture the output
|
||||||
|
result = await f(self, context, *args, **kwargs)
|
||||||
|
|
||||||
|
# Add aggregated output after function completes, if available
|
||||||
|
if output_text:
|
||||||
|
current_span.set_attribute("output", output_text)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Restore the original methods if we overrode them
|
# Always restore the original methods
|
||||||
|
self.push_frame = original_push_frame
|
||||||
|
|
||||||
if (
|
if (
|
||||||
"original_start_llm_usage_metrics" in locals()
|
"original_start_llm_usage_metrics" in locals()
|
||||||
and original_start_llm_usage_metrics
|
and original_start_llm_usage_metrics
|
||||||
|
|||||||
Reference in New Issue
Block a user