diff --git a/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/__init__.py b/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/__init__.py index 3d777ec082..2f62a70773 100644 --- a/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/__init__.py +++ b/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/__init__.py @@ -10,6 +10,7 @@ _FunctionCallAExecuteWrapper, ) from opentelemetry.instrumentation.agno.config import Config +from opentelemetry.instrumentation.agno.streaming import AgnoAsyncStream, AgnoStream from opentelemetry.instrumentation.agno.utils import ( dont_throw, should_send_prompts, @@ -149,12 +150,16 @@ def __call__(self, wrapped, instance, args, kwargs): ) or context_api.get_value("suppress_agno_instrumentation"): return wrapped(*args, **kwargs) - span_name = f"{getattr(instance, 'name', 'unknown')}.agent" + is_streaming = kwargs.get("stream", False) + + if is_streaming: + span_name = f"{getattr(instance, 'name', 'unknown')}.agent" + + span = self._tracer.start_span( + span_name, + kind=SpanKind.CLIENT, + ) - with self._tracer.start_as_current_span( - span_name, - kind=SpanKind.CLIENT, - ) as span: try: span.set_attribute(GenAIAttributes.GEN_AI_SYSTEM, "agno") span.set_attribute( @@ -181,51 +186,100 @@ def __call__(self, wrapped, instance, args, kwargs): start_time = time.time() - result = wrapped(*args, **kwargs) + response = wrapped(*args, **kwargs) - duration = time.time() - start_time + return AgnoStream( + span, + response, + instance, + start_time, + self._duration_histogram, + self._token_histogram, + ) - if hasattr(result, "content") and should_send_prompts(): + except Exception as e: + span.set_status(Status(StatusCode.ERROR, str(e))) + span.record_exception(e) + span.end() + raise + else: + span_name = f"{getattr(instance, 'name', 'unknown')}.agent" + + with self._tracer.start_as_current_span( + span_name, + kind=SpanKind.CLIENT, + ) as span: + try: + span.set_attribute(GenAIAttributes.GEN_AI_SYSTEM, "agno") span.set_attribute( - SpanAttributes.TRACELOOP_ENTITY_OUTPUT, str(result.content) + SpanAttributes.TRACELOOP_SPAN_KIND, + TraceloopSpanKindValues.AGENT.value, ) - if hasattr(result, "run_id"): - span.set_attribute("agno.run.id", result.run_id) + if hasattr(instance, "name"): + span.set_attribute(GenAIAttributes.GEN_AI_AGENT_NAME, instance.name) - if hasattr(result, "metrics"): - metrics = result.metrics - if hasattr(metrics, "input_tokens"): - span.set_attribute( - GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, - metrics.input_tokens, + if hasattr(instance, "model") and instance.model: + model_name = getattr( + instance.model, "id", getattr(instance.model, "name", "unknown") ) - if hasattr(metrics, "output_tokens"): + span.set_attribute(GenAIAttributes.GEN_AI_REQUEST_MODEL, model_name) + + if args and should_send_prompts(): + input_message = str(args[0]) span.set_attribute( - GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, - metrics.output_tokens, + SpanAttributes.TRACELOOP_ENTITY_INPUT, input_message ) - if hasattr(metrics, "total_tokens"): + + import time + + start_time = time.time() + + result = wrapped(*args, **kwargs) + + duration = time.time() - start_time + + if hasattr(result, "content") and should_send_prompts(): span.set_attribute( - SpanAttributes.LLM_USAGE_TOTAL_TOKENS, metrics.total_tokens + SpanAttributes.TRACELOOP_ENTITY_OUTPUT, str(result.content) ) - span.set_status(Status(StatusCode.OK)) - - self._duration_histogram.record( - duration, - attributes={ - GenAIAttributes.GEN_AI_SYSTEM: "agno", - SpanAttributes.TRACELOOP_SPAN_KIND: TraceloopSpanKindValues.AGENT.value, - }, - ) + if hasattr(result, "run_id"): + span.set_attribute("agno.run.id", result.run_id) + + if hasattr(result, "metrics"): + metrics = result.metrics + if hasattr(metrics, "input_tokens"): + span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, + metrics.input_tokens, + ) + if hasattr(metrics, "output_tokens"): + span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, + metrics.output_tokens, + ) + if hasattr(metrics, "total_tokens"): + span.set_attribute( + SpanAttributes.LLM_USAGE_TOTAL_TOKENS, metrics.total_tokens + ) + + span.set_status(Status(StatusCode.OK)) + + self._duration_histogram.record( + duration, + attributes={ + GenAIAttributes.GEN_AI_SYSTEM: "agno", + SpanAttributes.TRACELOOP_SPAN_KIND: TraceloopSpanKindValues.AGENT.value, + }, + ) - return result + return result - except Exception as e: - span.set_status(Status(StatusCode.ERROR, str(e))) - span.record_exception(e) - raise + except Exception as e: + span.set_status(Status(StatusCode.ERROR, str(e))) + span.record_exception(e) + raise class _AgentARunWrapper: @@ -238,19 +292,23 @@ def __init__(self, tracer, duration_histogram, token_histogram): self._token_histogram = token_histogram @dont_throw - async def __call__(self, wrapped, instance, args, kwargs): + def __call__(self, wrapped, instance, args, kwargs): """Wrap the Agent.arun() call with tracing instrumentation.""" if context_api.get_value( context_api._SUPPRESS_INSTRUMENTATION_KEY ) or context_api.get_value("suppress_agno_instrumentation"): - return await wrapped(*args, **kwargs) + return wrapped(*args, **kwargs) - span_name = f"{getattr(instance, 'name', 'unknown')}.agent" + is_streaming = kwargs.get("stream", False) + + if is_streaming: + span_name = f"{getattr(instance, 'name', 'unknown')}.agent" + + span = self._tracer.start_span( + span_name, + kind=SpanKind.CLIENT, + ) - with self._tracer.start_as_current_span( - span_name, - kind=SpanKind.CLIENT, - ) as span: try: span.set_attribute(GenAIAttributes.GEN_AI_SYSTEM, "agno") span.set_attribute( @@ -277,51 +335,103 @@ async def __call__(self, wrapped, instance, args, kwargs): start_time = time.time() - result = await wrapped(*args, **kwargs) + response = wrapped(*args, **kwargs) - duration = time.time() - start_time - - if hasattr(result, "content") and should_send_prompts(): - span.set_attribute( - SpanAttributes.TRACELOOP_ENTITY_OUTPUT, str(result.content) - ) - - if hasattr(result, "run_id"): - span.set_attribute("agno.run.id", result.run_id) + return AgnoAsyncStream( + span, + response, + instance, + start_time, + self._duration_histogram, + self._token_histogram, + ) - if hasattr(result, "metrics"): - metrics = result.metrics - if hasattr(metrics, "input_tokens"): - span.set_attribute( - GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, - metrics.input_tokens, - ) - if hasattr(metrics, "output_tokens"): - span.set_attribute( - GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, - metrics.output_tokens, - ) - if hasattr(metrics, "total_tokens"): + except Exception as e: + span.set_status(Status(StatusCode.ERROR, str(e))) + span.record_exception(e) + span.end() + raise + else: + async def async_wrapper(): + span_name = f"{getattr(instance, 'name', 'unknown')}.agent" + + with self._tracer.start_as_current_span( + span_name, + kind=SpanKind.CLIENT, + ) as span: + try: + span.set_attribute(GenAIAttributes.GEN_AI_SYSTEM, "agno") span.set_attribute( - SpanAttributes.LLM_USAGE_TOTAL_TOKENS, metrics.total_tokens + SpanAttributes.TRACELOOP_SPAN_KIND, + TraceloopSpanKindValues.AGENT.value, ) - span.set_status(Status(StatusCode.OK)) + if hasattr(instance, "name"): + span.set_attribute(GenAIAttributes.GEN_AI_AGENT_NAME, instance.name) + + if hasattr(instance, "model") and instance.model: + model_name = getattr( + instance.model, "id", getattr(instance.model, "name", "unknown") + ) + span.set_attribute(GenAIAttributes.GEN_AI_REQUEST_MODEL, model_name) + + if args and should_send_prompts(): + input_message = str(args[0]) + span.set_attribute( + SpanAttributes.TRACELOOP_ENTITY_INPUT, input_message + ) + + import time + + start_time = time.time() + + result = await wrapped(*args, **kwargs) + + duration = time.time() - start_time + + if hasattr(result, "content") and should_send_prompts(): + span.set_attribute( + SpanAttributes.TRACELOOP_ENTITY_OUTPUT, str(result.content) + ) + + if hasattr(result, "run_id"): + span.set_attribute("agno.run.id", result.run_id) + + if hasattr(result, "metrics"): + metrics = result.metrics + if hasattr(metrics, "input_tokens"): + span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, + metrics.input_tokens, + ) + if hasattr(metrics, "output_tokens"): + span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, + metrics.output_tokens, + ) + if hasattr(metrics, "total_tokens"): + span.set_attribute( + SpanAttributes.LLM_USAGE_TOTAL_TOKENS, metrics.total_tokens + ) + + span.set_status(Status(StatusCode.OK)) + + self._duration_histogram.record( + duration, + attributes={ + GenAIAttributes.GEN_AI_SYSTEM: "agno", + SpanAttributes.TRACELOOP_SPAN_KIND: TraceloopSpanKindValues.AGENT.value, + }, + ) - self._duration_histogram.record( - duration, - attributes={ - GenAIAttributes.GEN_AI_SYSTEM: "agno", - SpanAttributes.TRACELOOP_SPAN_KIND: TraceloopSpanKindValues.AGENT.value, - }, - ) + return result - return result + except Exception as e: + span.set_status(Status(StatusCode.ERROR, str(e))) + span.record_exception(e) + raise - except Exception as e: - span.set_status(Status(StatusCode.ERROR, str(e))) - span.record_exception(e) - raise + return async_wrapper() class _TeamRunWrapper: diff --git a/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/streaming.py b/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/streaming.py new file mode 100644 index 0000000000..b00dcd85ef --- /dev/null +++ b/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/streaming.py @@ -0,0 +1,219 @@ +import logging +import time + +from opentelemetry.instrumentation.agno.utils import should_send_prompts +from opentelemetry.metrics import Histogram +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAIAttributes, +) +from opentelemetry.semconv_ai import SpanAttributes, TraceloopSpanKindValues +from opentelemetry.trace.status import Status, StatusCode +from wrapt import ObjectProxy + +logger = logging.getLogger(__name__) + + +class AgnoAsyncStream(ObjectProxy): + """Wrapper for Agno async streaming responses that handles instrumentation""" + + def __init__( + self, + span, + response, + instance, + start_time, + duration_histogram: Histogram = None, + token_histogram: Histogram = None, + ): + super().__init__(response) + + self._self_span = span + self._self_instance = instance + self._self_start_time = start_time + self._self_duration_histogram = duration_histogram + self._self_token_histogram = token_histogram + self._self_events = [] + self._self_final_result = None + self._self_instrumentation_completed = False + + def __aiter__(self): + return self + + async def __anext__(self): + try: + event = await self.__wrapped__.__anext__() + except StopAsyncIteration: + if not self._self_instrumentation_completed: + self._complete_instrumentation() + raise + except Exception as e: + if not self._self_instrumentation_completed: + if self._self_span and self._self_span.is_recording(): + self._self_span.set_status(Status(StatusCode.ERROR, str(e))) + self._self_span.record_exception(e) + self._self_span.end() + self._self_instrumentation_completed = True + raise + + self._self_events.append(event) + + if hasattr(event, "event") and event.event == "run_response": + self._self_final_result = event + + return event + + def _complete_instrumentation(self): + """Complete the instrumentation when stream is fully consumed""" + if self._self_instrumentation_completed: + return + + try: + duration = time.time() - self._self_start_time + + if self._self_final_result: + result = self._self_final_result + if hasattr(result, "content") and should_send_prompts(): + self._self_span.set_attribute( + SpanAttributes.TRACELOOP_ENTITY_OUTPUT, str(result.content) + ) + + if hasattr(result, "run_id"): + self._self_span.set_attribute("agno.run.id", result.run_id) + + if hasattr(result, "metrics"): + metrics = result.metrics + if hasattr(metrics, "input_tokens"): + self._self_span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, + metrics.input_tokens, + ) + if hasattr(metrics, "output_tokens"): + self._self_span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, + metrics.output_tokens, + ) + if hasattr(metrics, "total_tokens"): + self._self_span.set_attribute( + SpanAttributes.LLM_USAGE_TOTAL_TOKENS, metrics.total_tokens + ) + + self._self_span.set_status(Status(StatusCode.OK)) + + if self._self_duration_histogram: + self._self_duration_histogram.record( + duration, + attributes={ + GenAIAttributes.GEN_AI_SYSTEM: "agno", + SpanAttributes.TRACELOOP_SPAN_KIND: TraceloopSpanKindValues.AGENT.value, + }, + ) + + except Exception as e: + logger.warning("Failed to complete instrumentation: %s", str(e)) + finally: + if self._self_span.is_recording(): + self._self_span.end() + self._self_instrumentation_completed = True + + +class AgnoStream(ObjectProxy): + """Wrapper for Agno sync streaming responses that handles instrumentation""" + + def __init__( + self, + span, + response, + instance, + start_time, + duration_histogram: Histogram = None, + token_histogram: Histogram = None, + ): + super().__init__(response) + + self._self_span = span + self._self_instance = instance + self._self_start_time = start_time + self._self_duration_histogram = duration_histogram + self._self_token_histogram = token_histogram + self._self_events = [] + self._self_final_result = None + self._self_instrumentation_completed = False + + def __iter__(self): + return self + + def __next__(self): + try: + event = self.__wrapped__.__next__() + except StopIteration: + if not self._self_instrumentation_completed: + self._complete_instrumentation() + raise + except Exception as e: + if not self._self_instrumentation_completed: + if self._self_span and self._self_span.is_recording(): + self._self_span.set_status(Status(StatusCode.ERROR, str(e))) + self._self_span.record_exception(e) + self._self_span.end() + self._self_instrumentation_completed = True + raise + + self._self_events.append(event) + + if hasattr(event, "event") and event.event == "run_response": + self._self_final_result = event + + return event + + def _complete_instrumentation(self): + """Complete the instrumentation when stream is fully consumed""" + if self._self_instrumentation_completed: + return + + try: + duration = time.time() - self._self_start_time + + if self._self_final_result: + result = self._self_final_result + if hasattr(result, "content") and should_send_prompts(): + self._self_span.set_attribute( + SpanAttributes.TRACELOOP_ENTITY_OUTPUT, str(result.content) + ) + + if hasattr(result, "run_id"): + self._self_span.set_attribute("agno.run.id", result.run_id) + + if hasattr(result, "metrics"): + metrics = result.metrics + if hasattr(metrics, "input_tokens"): + self._self_span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, + metrics.input_tokens, + ) + if hasattr(metrics, "output_tokens"): + self._self_span.set_attribute( + GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, + metrics.output_tokens, + ) + if hasattr(metrics, "total_tokens"): + self._self_span.set_attribute( + SpanAttributes.LLM_USAGE_TOTAL_TOKENS, metrics.total_tokens + ) + + self._self_span.set_status(Status(StatusCode.OK)) + + if self._self_duration_histogram: + self._self_duration_histogram.record( + duration, + attributes={ + GenAIAttributes.GEN_AI_SYSTEM: "agno", + SpanAttributes.TRACELOOP_SPAN_KIND: TraceloopSpanKindValues.AGENT.value, + }, + ) + + except Exception as e: + logger.warning("Failed to complete instrumentation: %s", str(e)) + finally: + if self._self_span.is_recording(): + self._self_span.end() + self._self_instrumentation_completed = True diff --git a/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/utils.py b/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/utils.py index 71cb4f511d..3b12dadcf4 100644 --- a/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/utils.py +++ b/packages/opentelemetry-instrumentation-agno/opentelemetry/instrumentation/agno/utils.py @@ -1,5 +1,7 @@ import logging from typing import Any +from functools import wraps +import asyncio from opentelemetry.trace import Span logger = logging.getLogger(__name__) @@ -7,12 +9,22 @@ def dont_throw(func): """Decorator to prevent exceptions from being thrown.""" - def wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - except Exception as e: - logger.debug(f"Error in {func.__name__}: {e}") - return wrapper + if asyncio.iscoroutinefunction(func): + @wraps(func) + async def async_wrapper(*args, **kwargs): + try: + return await func(*args, **kwargs) + except Exception as e: + logger.debug(f"Error in {func.__name__}: {e}") + return async_wrapper + else: + @wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as e: + logger.debug(f"Error in {func.__name__}: {e}") + return wrapper def set_span_attribute(span: Span, name: str, value: Any) -> None: diff --git a/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_arun_streaming.yaml b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_arun_streaming.yaml new file mode 100644 index 0000000000..28b6293165 --- /dev/null +++ b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_arun_streaming.yaml @@ -0,0 +1,184 @@ +interactions: +- request: + body: '{"messages": [{"role": "developer", "content": "An async streaming test + agent"}, {"role": "user", "content": "What is 10 + 5?"}], "model": "gpt-4o-mini", + "stream": true, "stream_options": {"include_usage": true}}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '197' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.109.1 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.11.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"T3FmnOmia"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"10"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"yPabhjnXh"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + +"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"YgagWXPBu"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"7F9bGwVxLn"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"5"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"31XO6pfzvv"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + equals"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"bK5x"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"if1LCNLWSa"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"15"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"YTemoMQpm"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"tYV0az6arM"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"0Tn35"} + + + data: {"id":"chatcmpl-ChZGrWePAKduzUEk8i6v9utQc14XI","object":"chat.completion.chunk","created":1764499721,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[],"usage":{"prompt_tokens":24,"completion_tokens":8,"total_tokens":32,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"IngXyYERtzR"} + + + data: [DONE] + + + ' + headers: + CF-RAY: + - 9a69c60bb897935b-TLV + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Sun, 30 Nov 2025 10:48:41 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=lmJyJxowi7UCeCxiq5.eMfkvZKN72hM9pmfpka3qsO8-1764499721-1.0.1.1-cJzziBuNDFDzVCFbfOVlMIm6r0p3zK3QN50vqF9FiIo1XwctQeLhSVEbrqLjbEnbn3OzsSESXICcwyGcWdjVEbaYA9oke8OlS3USWr4aYio; + path=/; expires=Sun, 30-Nov-25 11:18:41 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=ii5nlTYgcQkf0j7QIT5pyTNx6dSiT.6sr.CVTensbFU-1764499721433-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - traceloop + openai-processing-ms: + - '152' + openai-project: + - proj_tzz1TbPPOXaf6j9tEkVUBIAa + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '1872' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999985' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_4c3b7fd0b96a4cd1a9ec031c9521253f + status: + code: 200 + message: OK +- request: + body: '{"session_id": "218bbc8f-4862-474f-97ea-a5e0fd7a75ab", "run_id": "327f651a-e2f4-467a-8106-48e3e1d5b8b7", + "data": {"agent_id": "asyncstreamagent", "db_type": null, "model_provider": + "OpenAI", "model_name": "OpenAIChat", "model_id": "gpt-4o-mini", "parser_model": + null, "output_model": null, "has_tools": true, "has_memory": false, "has_culture": + false, "has_reasoning": false, "has_knowledge": false, "has_input_schema": false, + "has_output_schema": false, "has_team": false}, "sdk_version": "2.2.13", "type": + "agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '478' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.2.13 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 327f651a-e2f4-467a-8106-48e3e1d5b8b7","status":"success"}' + headers: + Connection: + - keep-alive + Content-Length: + - '96' + Content-Type: + - application/json + Date: + - Sun, 30 Nov 2025 10:48:42 GMT + server: + - uvicorn + status: + code: 201 + message: Created +version: 1 diff --git a/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_arun_streaming_with_tools.yaml b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_arun_streaming_with_tools.yaml new file mode 100644 index 0000000000..3b873fb36b --- /dev/null +++ b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_arun_streaming_with_tools.yaml @@ -0,0 +1,326 @@ +interactions: +- request: + body: '{"messages": [{"role": "developer", "content": "A streaming agent with + tools"}, {"role": "user", "content": "What is 6 times 7?"}], "model": "gpt-4o-mini", + "stream": true, "stream_options": {"include_usage": true}, "tools": [{"type": + "function", "function": {"name": "multiply", "description": "Multiply two numbers.", + "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": + {"type": "number"}}, "required": ["a", "b"]}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '409' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.109.1 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.11.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_fvsP35x2Oga1cEKZEOPqYNeO","type":"function","function":{"name":"multiply","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"027O0Hw"} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ODp6oBI3Vkuq56"} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"a"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"7QZEpfKsRcs9Oj"} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"6"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZsJOt1y0FElDkx"} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"b"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"r2DypemXGAui7C"} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"7"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"1a6WhodWpeDLf8i"} + + + data: {"id":"chatcmpl-ChZGtWl2za1l5jTaEl4KhNkV3RdM0","object":"chat.completion.chunk","created":1764499723,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[],"usage":{"prompt_tokens":58,"completion_tokens":17,"total_tokens":75,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"YfyJcOz3jn"} + + + data: [DONE] + + + ' + headers: + CF-RAY: + - 9a69c6214ac4997f-TLV + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Sun, 30 Nov 2025 10:48:44 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=KdIqnM_NjlF_a_hAtr_VnbxBtM25OmOPaj9u7bqHUS4-1764499724-1.0.1.1-5kdn32rOYH0amaKqnRwLQGZHkedRSiREBAiLjFLl9IiQrCEvM0VNDfeP6nl2pkDX1It5pHoxCFiOudMy8fKLFaDn7KYYY_a7O4ADUQwT5aI; + path=/; expires=Sun, 30-Nov-25 11:18:44 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=ayM5sbPWKidG631jgonROcVKOmpiyZ13mX2bui1qUkE-1764499724004-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - traceloop + openai-processing-ms: + - '694' + openai-project: + - proj_tzz1TbPPOXaf6j9tEkVUBIAa + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '889' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999985' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_46bd5de8c07c4becb96557f555ec133d + status: + code: 200 + message: OK +- request: + body: '{"messages": [{"role": "developer", "content": "A streaming agent with + tools"}, {"role": "user", "content": "What is 6 times 7?"}, {"role": "assistant", + "tool_calls": [{"id": "call_fvsP35x2Oga1cEKZEOPqYNeO", "type": "function", "function": + {"name": "multiply", "arguments": "{\"a\":6,\"b\":7}"}}], "content": ""}, {"role": + "tool", "content": "42", "tool_call_id": "call_fvsP35x2Oga1cEKZEOPqYNeO"}], + "model": "gpt-4o-mini", "stream": true, "stream_options": {"include_usage": + true}, "tools": [{"type": "function", "function": {"name": "multiply", "description": + "Multiply two numbers.", "parameters": {"type": "object", "properties": {"a": + {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '656' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.109.1 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.11.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"rlrDvkoSF"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"6"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"zufiawYmUl"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + times"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AWd5L"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"O2QJv57PE8"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"7"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZWA0uR3j8m"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Eq6p3HYr"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"rjHD4JqjIJ"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"42"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"bX7kiBthP"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"JMunvWGQ4A"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"eDdrR"} + + + data: {"id":"chatcmpl-ChZGunQ0zcUQXzAGuRv0nT0f3APLS","object":"chat.completion.chunk","created":1764499724,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[],"usage":{"prompt_tokens":83,"completion_tokens":9,"total_tokens":92,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"Y46woMhBiYm"} + + + data: [DONE] + + + ' + headers: + CF-RAY: + - 9a69c62c3cc6997f-TLV + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Sun, 30 Nov 2025 10:48:45 GMT + Server: + - cloudflare + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - traceloop + openai-processing-ms: + - '653' + openai-project: + - proj_tzz1TbPPOXaf6j9tEkVUBIAa + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '767' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999985' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_7729e51298d349d0a76f25314c92a69f + status: + code: 200 + message: OK +- request: + body: '{"session_id": "68ca1e9e-b05e-4cce-a332-49b785ee30e5", "run_id": "70d8764b-1e37-493a-b822-f777434eb039", + "data": {"agent_id": "streamtoolagent", "db_type": null, "model_provider": "OpenAI", + "model_name": "OpenAIChat", "model_id": "gpt-4o-mini", "parser_model": null, + "output_model": null, "has_tools": true, "has_memory": false, "has_culture": + false, "has_reasoning": false, "has_knowledge": false, "has_input_schema": false, + "has_output_schema": false, "has_team": false}, "sdk_version": "2.2.13", "type": + "agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '477' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.2.13 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 70d8764b-1e37-493a-b822-f777434eb039","status":"success"}' + headers: + Connection: + - keep-alive + Content-Length: + - '96' + Content-Type: + - application/json + Date: + - Sun, 30 Nov 2025 10:48:46 GMT + server: + - uvicorn + status: + code: 201 + message: Created +version: 1 diff --git a/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_run_streaming.yaml b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_run_streaming.yaml new file mode 100644 index 0000000000..490b9c4149 --- /dev/null +++ b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_run_streaming.yaml @@ -0,0 +1,184 @@ +interactions: +- request: + body: '{"messages": [{"role": "developer", "content": "A streaming test agent"}, + {"role": "user", "content": "What is 10 + 5?"}], "model": "gpt-4o-mini", "stream": + true, "stream_options": {"include_usage": true}}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '190' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.11.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"LPXc94h4G"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"10"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6T3lgPpF9"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + +"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"2ueBQ6HRL"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Slu7xVhBqH"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"5"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ceZThojkDv"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + equals"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Z1wg"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"3SnjI07Zaw"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"15"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"utv78xVby"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"U6nZlKLMOG"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"J32gM"} + + + data: {"id":"chatcmpl-ChZNa5AVXUvGOZAleY7FgQlVr6bxn","object":"chat.completion.chunk","created":1764500138,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_50906f2aac","choices":[],"usage":{"prompt_tokens":23,"completion_tokens":8,"total_tokens":31,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"eUG8yTXdybZ"} + + + data: [DONE] + + + ' + headers: + CF-RAY: + - 9a69d04a5d28c222-TLV + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Sun, 30 Nov 2025 10:55:38 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=0A6vzsZqQJIcRNkAI9bEthhYCU5lSOir3tvToBJS_00-1764500138-1.0.1.1-ep_jbuMItvBDryGMoy.j620ijXQ5rKR3dfDzWrqFGldkY5nkJ.7a8hz.p4keQXwVHH6TVAQ90fNSpo4eVCm3vZtPNsVLi5WdioIuxNyx2i4; + path=/; expires=Sun, 30-Nov-25 11:25:38 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=WXukXV8S1Di4DqhkRFo1F.ySxtweSHR_vR1NL6lTgAs-1764500138962-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - traceloop + openai-processing-ms: + - '157' + openai-project: + - proj_tzz1TbPPOXaf6j9tEkVUBIAa + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '173' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999987' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_749ae20b6a8a467ca2ae1ad496a8e190 + status: + code: 200 + message: OK +- request: + body: '{"session_id": "e5ece810-530f-48fa-964a-72564179fa40", "run_id": "6ebff03b-9dd6-4eab-a06b-394809a84f27", + "data": {"agent_id": "streamagent", "db_type": null, "model_provider": "OpenAI", + "model_name": "OpenAIChat", "model_id": "gpt-4o-mini", "parser_model": null, + "output_model": null, "has_tools": true, "has_memory": false, "has_culture": + false, "has_reasoning": false, "has_knowledge": false, "has_input_schema": false, + "has_output_schema": false, "has_team": false}, "sdk_version": "2.2.13", "type": + "agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '473' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.2.13 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 6ebff03b-9dd6-4eab-a06b-394809a84f27","status":"success"}' + headers: + Connection: + - keep-alive + Content-Length: + - '96' + Content-Type: + - application/json + Date: + - Sun, 30 Nov 2025 10:55:39 GMT + server: + - uvicorn + status: + code: 201 + message: Created +version: 1 diff --git a/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_run_streaming_with_tools.yaml b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_run_streaming_with_tools.yaml new file mode 100644 index 0000000000..0b181428c0 --- /dev/null +++ b/packages/opentelemetry-instrumentation-agno/tests/cassettes/test_agent/test_agent_run_streaming_with_tools.yaml @@ -0,0 +1,326 @@ +interactions: +- request: + body: '{"messages": [{"role": "developer", "content": "A sync streaming agent + with tools"}, {"role": "user", "content": "What is 6 times 7?"}], "model": "gpt-4o-mini", + "stream": true, "stream_options": {"include_usage": true}, "tools": [{"type": + "function", "function": {"name": "multiply", "description": "Multiply two numbers.", + "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": + {"type": "number"}}, "required": ["a", "b"]}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '414' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.11.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_6KQlxELWhphiY7wr0DV9WW5S","type":"function","function":{"name":"multiply","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"2PjwdOi"} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"SwHPGrZh6yBiDL"} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"a"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"LanyQv9yM5rRyd"} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"6"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6WiOw37qWIcXbT"} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"b"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"3stn1EoqVULm1p"} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"7"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"Ho3TcVxJ5ZKW6gw"} + + + data: {"id":"chatcmpl-ChZNcadOV8XXL9i2Jh0PXsrur4L8k","object":"chat.completion.chunk","created":1764500140,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[],"usage":{"prompt_tokens":59,"completion_tokens":17,"total_tokens":76,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"ePhnJdxzH0"} + + + data: [DONE] + + + ' + headers: + CF-RAY: + - 9a69d0522b8bd31d-TLV + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Sun, 30 Nov 2025 10:55:41 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=fKq_KvjRNJ6ScCBDMjGG7CXVQolFjtlwJp9WwR9q2tg-1764500141-1.0.1.1-LZx4gtaC6s5f.YAGoI_98obNqEYc5ZxCx.E5azfBdiqz6tmZeXaU5rXcYjA44fAIeiqjrqn7bYdQMjCdCW6Xcf4ZKsGMT7ee9ytyJxaNlQE; + path=/; expires=Sun, 30-Nov-25 11:25:41 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=o8XuZB9.8.A1.5kafkPgKrJDedXgnhY3tIueIVYpdcE-1764500141773-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - traceloop + openai-processing-ms: + - '975' + openai-project: + - proj_tzz1TbPPOXaf6j9tEkVUBIAa + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '1141' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999985' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_41463ffe0f7b4d4ea56c529aac1fe427 + status: + code: 200 + message: OK +- request: + body: '{"messages": [{"role": "developer", "content": "A sync streaming agent + with tools"}, {"role": "user", "content": "What is 6 times 7?"}, {"role": "assistant", + "tool_calls": [{"id": "call_6KQlxELWhphiY7wr0DV9WW5S", "type": "function", "function": + {"name": "multiply", "arguments": "{\"a\":6,\"b\":7}"}}], "content": ""}, {"role": + "tool", "content": "42", "tool_call_id": "call_6KQlxELWhphiY7wr0DV9WW5S"}], + "model": "gpt-4o-mini", "stream": true, "stream_options": {"include_usage": + true}, "tools": [{"type": "function", "function": {"name": "multiply", "description": + "Multiply two numbers.", "parameters": {"type": "object", "properties": {"a": + {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '661' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.11.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"bjGBsIELG"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":"6"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ktWYEJioJW"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":" + times"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"7ffJp"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"M8zTj1sXhg"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":"7"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"3EnJ23LuRj"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":" + is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"YpCRpIsK"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":" + "},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"UoxQrOSyl9"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":"42"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"WkTaZmO1F"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZqOtAGQWae"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"rFtGr"} + + + data: {"id":"chatcmpl-ChZNeG5zNP0SKpnZBo2dn9nrj8vlh","object":"chat.completion.chunk","created":1764500142,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_b547601dbd","choices":[],"usage":{"prompt_tokens":84,"completion_tokens":9,"total_tokens":93,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"OJaMhe9ZjRM"} + + + data: [DONE] + + + ' + headers: + CF-RAY: + - 9a69d05f5fe1d31d-TLV + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Sun, 30 Nov 2025 10:55:42 GMT + Server: + - cloudflare + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - traceloop + openai-processing-ms: + - '303' + openai-project: + - proj_tzz1TbPPOXaf6j9tEkVUBIAa + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '317' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999980' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_30e15ae0d2d643a59a6673480c5a3a6a + status: + code: 200 + message: OK +- request: + body: '{"session_id": "034c8718-3f49-40b0-a1c3-2441d2117c25", "run_id": "bc2a14f6-7a67-4061-8a42-d832c758c720", + "data": {"agent_id": "syncstreamtoolagent", "db_type": null, "model_provider": + "OpenAI", "model_name": "OpenAIChat", "model_id": "gpt-4o-mini", "parser_model": + null, "output_model": null, "has_tools": true, "has_memory": false, "has_culture": + false, "has_reasoning": false, "has_knowledge": false, "has_input_schema": false, + "has_output_schema": false, "has_team": false}, "sdk_version": "2.2.13", "type": + "agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '481' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.2.13 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: bc2a14f6-7a67-4061-8a42-d832c758c720","status":"success"}' + headers: + Connection: + - keep-alive + Content-Length: + - '96' + Content-Type: + - application/json + Date: + - Sun, 30 Nov 2025 10:55:43 GMT + server: + - uvicorn + status: + code: 201 + message: Created +version: 1 diff --git a/packages/opentelemetry-instrumentation-agno/tests/test_agent.py b/packages/opentelemetry-instrumentation-agno/tests/test_agent.py index e794ba8625..52fdcc80fb 100644 --- a/packages/opentelemetry-instrumentation-agno/tests/test_agent.py +++ b/packages/opentelemetry-instrumentation-agno/tests/test_agent.py @@ -117,3 +117,131 @@ def test_agent_metrics(instrument, span_exporter, reader): assert len(metric.data.data_points) > 0 for dp in metric.data.data_points: assert dp.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" + + +@pytest.mark.vcr +def test_agent_run_streaming(instrument, span_exporter, reader): + """Test agent.run() with streaming enabled.""" + agent = Agent( + name="StreamAgent", + model=OpenAIChat(id="gpt-4o-mini"), + description="A streaming test agent", + ) + + events = [] + for event in agent.run("What is 10 + 5?", stream=True): + events.append(event) + + assert len(events) > 0 + + spans = span_exporter.get_finished_spans() + assert len(spans) >= 1 + + agent_span = spans[-1] + assert agent_span.name == "StreamAgent.agent" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_AGENT_NAME) == "StreamAgent" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_REQUEST_MODEL) == "gpt-4o-mini" + + prompt_content = agent_span.attributes.get(SpanAttributes.TRACELOOP_ENTITY_INPUT) + assert "10 + 5" in prompt_content + + +@pytest.mark.vcr +def test_agent_run_streaming_with_tools(instrument, span_exporter, reader): + """Test agent.run() with streaming and tool usage.""" + + def multiply(a: int, b: int) -> int: + """Multiply two numbers.""" + return a * b + + agent = Agent( + name="SyncStreamToolAgent", + model=OpenAIChat(id="gpt-4o-mini"), + tools=[multiply], + description="A sync streaming agent with tools", + ) + + events = [] + for event in agent.run("What is 6 times 7?", stream=True): + events.append(event) + + assert len(events) > 0 + + spans = span_exporter.get_finished_spans() + assert len(spans) >= 1 + + agent_span = [s for s in spans if "agent" in s.name][-1] + assert agent_span.name == "SyncStreamToolAgent.agent" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_AGENT_NAME) == "SyncStreamToolAgent" + + tool_spans = [s for s in spans if s.name == "multiply.tool"] + for tool_span in tool_spans: + assert tool_span.attributes.get(SpanAttributes.TRACELOOP_ENTITY_NAME) == "multiply" + assert tool_span.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" + + +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_agent_arun_streaming(instrument, span_exporter, reader): + """Test agent.arun() with streaming enabled.""" + agent = Agent( + name="AsyncStreamAgent", + model=OpenAIChat(id="gpt-4o-mini"), + description="An async streaming test agent", + ) + + events = [] + async for event in agent.arun("What is 10 + 5?", stream=True): + events.append(event) + + assert len(events) > 0 + + spans = span_exporter.get_finished_spans() + assert len(spans) >= 1 + + agent_span = spans[-1] + assert agent_span.name == "AsyncStreamAgent.agent" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_AGENT_NAME) == "AsyncStreamAgent" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_REQUEST_MODEL) == "gpt-4o-mini" + + prompt_content = agent_span.attributes.get(SpanAttributes.TRACELOOP_ENTITY_INPUT) + assert "10 + 5" in prompt_content + + +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_agent_arun_streaming_with_tools(instrument, span_exporter, reader): + """Test agent.arun() with streaming and tool usage.""" + + def multiply(a: int, b: int) -> int: + """Multiply two numbers.""" + return a * b + + agent = Agent( + name="StreamToolAgent", + model=OpenAIChat(id="gpt-4o-mini"), + tools=[multiply], + description="A streaming agent with tools", + ) + + events = [] + async for event in agent.arun("What is 6 times 7?", stream=True): + events.append(event) + + assert len(events) > 0 + + spans = span_exporter.get_finished_spans() + assert len(spans) >= 1 + + agent_span = [s for s in spans if "agent" in s.name][-1] + assert agent_span.name == "StreamToolAgent.agent" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" + assert agent_span.attributes.get(GenAIAttributes.GEN_AI_AGENT_NAME) == "StreamToolAgent" + + tool_spans = [s for s in spans if s.name == "multiply.tool"] + for tool_span in tool_spans: + assert tool_span.attributes.get(SpanAttributes.TRACELOOP_ENTITY_NAME) == "multiply" + assert tool_span.attributes.get(GenAIAttributes.GEN_AI_SYSTEM) == "agno" diff --git a/packages/sample-app/sample_app/agno_streaming_example.py b/packages/sample-app/sample_app/agno_streaming_example.py new file mode 100644 index 0000000000..c50c1bac6d --- /dev/null +++ b/packages/sample-app/sample_app/agno_streaming_example.py @@ -0,0 +1,44 @@ +import asyncio +import os +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from traceloop.sdk import Traceloop + +Traceloop.init(app_name="agno_streaming_example") + + +def get_weather(location: str) -> str: + """Get the weather for a location.""" + return f"The weather in {location} is sunny and 72 degrees." + + +async def test_streaming(): + agent = Agent( + name="WeatherAgent", + model=OpenAIChat( + id="gpt-4o-mini", + api_key=os.environ.get("OPENAI_API_KEY"), + ), + tools=[get_weather], + description="An agent that provides weather information", + ) + + print("Testing async streaming with Traceloop instrumentation...") + print("=" * 60) + + async for event in agent.arun( + "What's the weather like in San Francisco?", + stream=True + ): + if hasattr(event, "event"): + print(f"Event: {event.event}") + if hasattr(event, "content") and event.content: + print(f"Content: {event.content}") + print("-" * 40) + + print("=" * 60) + print("Streaming completed successfully!") + + +if __name__ == "__main__": + asyncio.run(test_streaming())