|
| 1 | +import json |
| 2 | + |
1 | 3 | import pytest |
2 | 4 |
|
3 | 5 | from opentelemetry.sdk.trace import TracerProvider |
| 6 | +from opentelemetry.semconv._incubating.attributes import ( |
| 7 | + gen_ai_attributes as GenAI, |
| 8 | +) |
| 9 | +from opentelemetry.util.genai.attributes import ( |
| 10 | + GEN_AI_INPUT_MESSAGES, |
| 11 | + GEN_AI_OUTPUT_MESSAGES, |
| 12 | +) |
4 | 13 | from opentelemetry.util.genai.emitters.composite import CompositeEmitter |
5 | 14 | from opentelemetry.util.genai.emitters.content_events import ( |
6 | 15 | ContentEventsEmitter, |
|
12 | 21 | LLMInvocation, |
13 | 22 | OutputMessage, |
14 | 23 | Text, |
| 24 | + Workflow, |
15 | 25 | ) |
16 | 26 |
|
17 | 27 |
|
@@ -75,7 +85,7 @@ class _RecordingEvaluationEmitter: |
75 | 85 | role = "evaluation" |
76 | 86 |
|
77 | 87 | def __init__(self) -> None: |
78 | | - self.call_log = [] |
| 88 | + self.call_log: list[tuple[str, object]] = [] |
79 | 89 |
|
80 | 90 | def on_evaluation_results(self, results, obj=None): |
81 | 91 | self.call_log.append(("results", list(results))) |
@@ -167,3 +177,40 @@ def test_span_emitter_filters_non_gen_ai_attributes(): |
167 | 177 | assert "traceloop.association.properties.ls_temperature" not in attrs |
168 | 178 | assert all(not key.startswith("traceloop.") for key in attrs.keys()) |
169 | 179 | assert any(key.startswith("gen_ai.") for key in attrs) |
| 180 | + |
| 181 | + |
| 182 | +def test_span_emitter_workflow_captures_content(): |
| 183 | + provider = TracerProvider() |
| 184 | + tracer = provider.get_tracer(__name__) |
| 185 | + emitter = SpanEmitter(tracer=tracer, capture_content=True) |
| 186 | + |
| 187 | + workflow = Workflow( |
| 188 | + name="trip_planner", |
| 189 | + workflow_type="sequential", |
| 190 | + initial_input="Plan a trip to Rome", |
| 191 | + final_output="Here is your itinerary", |
| 192 | + ) |
| 193 | + |
| 194 | + emitter.on_start(workflow) |
| 195 | + emitter.on_end(workflow) |
| 196 | + |
| 197 | + span = workflow.span |
| 198 | + assert span is not None |
| 199 | + attrs = getattr(span, "attributes", None) or getattr( |
| 200 | + span, "_attributes", {} |
| 201 | + ) |
| 202 | + |
| 203 | + operation_value = attrs.get(GenAI.GEN_AI_OPERATION_NAME) |
| 204 | + assert operation_value == "invoke_workflow" |
| 205 | + |
| 206 | + input_messages_raw = attrs.get(GEN_AI_INPUT_MESSAGES) |
| 207 | + assert input_messages_raw is not None |
| 208 | + input_messages = json.loads(input_messages_raw) |
| 209 | + assert input_messages[0]["parts"][0]["content"] == "Plan a trip to Rome" |
| 210 | + |
| 211 | + output_messages_raw = attrs.get(GEN_AI_OUTPUT_MESSAGES) |
| 212 | + assert output_messages_raw is not None |
| 213 | + output_messages = json.loads(output_messages_raw) |
| 214 | + assert ( |
| 215 | + output_messages[0]["parts"][0]["content"] == "Here is your itinerary" |
| 216 | + ) |
0 commit comments