diff --git a/src/pipecat/utils/tracing/class_decorators.py b/src/pipecat/utils/tracing/class_decorators.py index d84605839..98da7211e 100644 --- a/src/pipecat/utils/tracing/class_decorators.py +++ b/src/pipecat/utils/tracing/class_decorators.py @@ -18,10 +18,10 @@ import functools import inspect from typing import Callable, Optional, TypeVar -from pipecat.utils.tracing.setup import OPENTELEMETRY_AVAILABLE +from pipecat.utils.tracing.setup import is_tracing_available # Import OpenTelemetry if available -if OPENTELEMETRY_AVAILABLE: +if is_tracing_available(): import opentelemetry.trace from opentelemetry import metrics, trace @@ -59,7 +59,7 @@ class Traceable: """ super().__init__(**kwargs) - if not OPENTELEMETRY_AVAILABLE: + if not is_tracing_available(): self._tracer = self._meter = self._parent_span_id = self._span = None return @@ -177,7 +177,7 @@ def traced( RuntimeError: If used in a class not inheriting from Traceable. ValueError: If used on a non-async function. """ - if not OPENTELEMETRY_AVAILABLE: + if not is_tracing_available(): # Just return the original function or a simple decorator def decorator(f): return f @@ -204,7 +204,7 @@ def traceable(cls: C) -> C: Returns: A new class with tracing capabilities. """ - if not OPENTELEMETRY_AVAILABLE: + if not is_tracing_available(): return cls @functools.wraps(cls, updated=()) diff --git a/src/pipecat/utils/tracing/conversation_context_provider.py b/src/pipecat/utils/tracing/conversation_context_provider.py index d4a7e8205..611e59650 100644 --- a/src/pipecat/utils/tracing/conversation_context_provider.py +++ b/src/pipecat/utils/tracing/conversation_context_provider.py @@ -5,7 +5,12 @@ # import uuid -from typing import Optional +from typing import TYPE_CHECKING, Optional + +# Import types for type checking only +if TYPE_CHECKING: + from opentelemetry.context import Context + from opentelemetry.trace import SpanContext from pipecat.utils.tracing.setup import is_tracing_available @@ -22,7 +27,7 @@ class ConversationContextProvider: """ _instance = None - _current_conversation_context: Optional[Context] = None + _current_conversation_context: Optional["Context"] = None _conversation_id: Optional[str] = None @classmethod @@ -33,7 +38,7 @@ class ConversationContextProvider: return cls._instance def set_current_conversation_context( - self, span_context: Optional[SpanContext], conversation_id: Optional[str] = None + self, span_context: Optional["SpanContext"], conversation_id: Optional[str] = None ): """Set the current conversation context. @@ -53,7 +58,7 @@ class ConversationContextProvider: else: self._current_conversation_context = None - def get_current_conversation_context(self) -> Optional[Context]: + def get_current_conversation_context(self) -> Optional["Context"]: """Get the OpenTelemetry context for the current conversation. Returns: @@ -79,7 +84,7 @@ class ConversationContextProvider: # Create a simple helper function to get the current conversation context -def get_current_conversation_context() -> Optional[Context]: +def get_current_conversation_context() -> Optional["Context"]: """Get the OpenTelemetry context for the current conversation. Returns: diff --git a/src/pipecat/utils/tracing/service_attributes.py b/src/pipecat/utils/tracing/service_attributes.py index cebabc4bf..22ffd2bec 100644 --- a/src/pipecat/utils/tracing/service_attributes.py +++ b/src/pipecat/utils/tracing/service_attributes.py @@ -6,13 +6,20 @@ """Functions for adding attributes to OpenTelemetry spans.""" -from typing import Any, Dict, Optional +from typing import TYPE_CHECKING, Any, Dict, Optional -from opentelemetry.trace import Span +# Import for type checking only +if TYPE_CHECKING: + from opentelemetry.trace import Span + +from pipecat.utils.tracing.setup import is_tracing_available + +if is_tracing_available(): + from opentelemetry.trace import Span def add_tts_span_attributes( - span: Span, + span: "Span", service_name: str, model: str, voice_id: str, @@ -66,7 +73,7 @@ def add_tts_span_attributes( def add_stt_span_attributes( - span: Span, + span: "Span", service_name: str, model: str, transcript: Optional[str] = None, @@ -122,7 +129,7 @@ def add_stt_span_attributes( def add_llm_span_attributes( - span: Span, + span: "Span", service_name: str, model: str, stream: bool = True, diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 7c9912c65..e684ccb14 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -16,19 +16,25 @@ import functools import inspect import json import logging -from typing import Callable, Optional, TypeVar +from typing import TYPE_CHECKING, Callable, Optional, TypeVar -from opentelemetry import context as context_api -from opentelemetry import trace +# Type imports for type checking only +if TYPE_CHECKING: + from opentelemetry import context as context_api + from opentelemetry import trace from pipecat.utils.tracing.service_attributes import ( add_llm_span_attributes, add_stt_span_attributes, add_tts_span_attributes, ) -from pipecat.utils.tracing.setup import OPENTELEMETRY_AVAILABLE, is_tracing_available +from pipecat.utils.tracing.setup import is_tracing_available from pipecat.utils.tracing.turn_context_provider import get_current_turn_context +if is_tracing_available(): + from opentelemetry import context as context_api + from opentelemetry import trace + T = TypeVar("T") R = TypeVar("R") @@ -114,7 +120,7 @@ def traced_tts(func: Optional[Callable] = None, *, name: Optional[str] = None) - Returns: Wrapped method with TTS-specific tracing. """ - if not OPENTELEMETRY_AVAILABLE: + if not is_tracing_available(): return _noop_decorator if func is None else _noop_decorator(func) def decorator(f): @@ -220,7 +226,7 @@ def traced_stt(func: Optional[Callable] = None, *, name: Optional[str] = None) - Returns: Wrapped method with STT-specific tracing. """ - if not OPENTELEMETRY_AVAILABLE: + if not is_tracing_available(): return _noop_decorator if func is None else _noop_decorator(func) def decorator(f): @@ -296,7 +302,7 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - Returns: Wrapped method with LLM-specific tracing. """ - if not OPENTELEMETRY_AVAILABLE: + if not is_tracing_available(): return _noop_decorator if func is None else _noop_decorator(func) def decorator(f): diff --git a/src/pipecat/utils/tracing/turn_context_provider.py b/src/pipecat/utils/tracing/turn_context_provider.py index 868e2cf4a..7af3d694a 100644 --- a/src/pipecat/utils/tracing/turn_context_provider.py +++ b/src/pipecat/utils/tracing/turn_context_provider.py @@ -4,7 +4,12 @@ # SPDX-License-Identifier: BSD 2-Clause License # -from typing import Optional +from typing import TYPE_CHECKING, Optional + +# Import types for type checking only +if TYPE_CHECKING: + from opentelemetry.context import Context + from opentelemetry.trace import SpanContext from pipecat.utils.tracing.setup import is_tracing_available @@ -21,7 +26,7 @@ class TurnContextProvider: """ _instance = None - _current_turn_context: Optional[Context] = None + _current_turn_context: Optional["Context"] = None @classmethod def get_instance(cls): @@ -30,7 +35,7 @@ class TurnContextProvider: cls._instance = TurnContextProvider() return cls._instance - def set_current_turn_context(self, span_context: Optional[SpanContext]): + def set_current_turn_context(self, span_context: Optional["SpanContext"]): """Set the current turn context. Args: @@ -46,7 +51,7 @@ class TurnContextProvider: else: self._current_turn_context = None - def get_current_turn_context(self) -> Optional[Context]: + def get_current_turn_context(self) -> Optional["Context"]: """Get the OpenTelemetry context for the current turn. Returns: @@ -56,7 +61,7 @@ class TurnContextProvider: # Create a simple helper function to get the current turn context -def get_current_turn_context() -> Optional[Context]: +def get_current_turn_context() -> Optional["Context"]: """Get the OpenTelemetry context for the current turn. Returns: diff --git a/src/pipecat/utils/tracing/turn_trace_observer.py b/src/pipecat/utils/tracing/turn_trace_observer.py index dedf87696..9fb8a5a7f 100644 --- a/src/pipecat/utils/tracing/turn_trace_observer.py +++ b/src/pipecat/utils/tracing/turn_trace_observer.py @@ -4,7 +4,7 @@ # SPDX-License-Identifier: BSD 2-Clause License # -from typing import Dict, Optional +from typing import TYPE_CHECKING, Dict, Optional from loguru import logger @@ -14,6 +14,10 @@ from pipecat.utils.tracing.conversation_context_provider import ConversationCont from pipecat.utils.tracing.setup import is_tracing_available from pipecat.utils.tracing.turn_context_provider import TurnContextProvider +# Import types for type checking only +if TYPE_CHECKING: + from opentelemetry.trace import Span, SpanContext + if is_tracing_available(): from opentelemetry import trace from opentelemetry.trace import Span, SpanContext @@ -33,13 +37,13 @@ class TurnTraceObserver(BaseObserver): def __init__(self, turn_tracker: TurnTrackingObserver, conversation_id: Optional[str] = None): super().__init__() self._turn_tracker = turn_tracker - self._current_span: Optional[Span] = None + self._current_span: Optional["Span"] = None self._current_turn_number: int = 0 - self._trace_context_map: Dict[int, SpanContext] = {} + self._trace_context_map: Dict[int, "SpanContext"] = {} self._tracer = trace.get_tracer("pipecat.turn") if is_tracing_available() else None # Conversation tracking properties - self._conversation_span: Optional[Span] = None + self._conversation_span: Optional["Span"] = None self._conversation_id = conversation_id if turn_tracker: @@ -180,7 +184,7 @@ class TurnTraceObserver(BaseObserver): logger.debug(f"Ended tracing for Turn {turn_number}") - def get_current_turn_context(self) -> Optional[SpanContext]: + def get_current_turn_context(self) -> Optional["SpanContext"]: """Get the span context for the current turn. This can be used by services to create child spans. @@ -190,7 +194,7 @@ class TurnTraceObserver(BaseObserver): return self._current_span.get_span_context() - def get_turn_context(self, turn_number: int) -> Optional[SpanContext]: + def get_turn_context(self, turn_number: int) -> Optional["SpanContext"]: """Get the span context for a specific turn. This can be used by services to create child spans. diff --git a/test-requirements.txt b/test-requirements.txt index 1abcabfcf..fec8adf52 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1 @@ --e ".[anthropic,aws,google,langchain,tracing]" +-e ".[anthropic,aws,google,langchain]"