Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f5c1458
fix session decorator resource management
dot-agi May 28, 2025
5fa1cb8
fix api consistency issues in `decorators/__init__.py`
dot-agi May 28, 2025
9d46fba
deprecation warning when using session decorator
dot-agi May 28, 2025
811bffa
make `int` as `Required`
dot-agi May 28, 2025
3b9585d
use method to format trace id
dot-agi May 28, 2025
9f4d307
improved error handling in span processor flushing
dot-agi May 28, 2025
4148fe5
prevent resource leak in `LiveSpanProcessor`
dot-agi May 28, 2025
339b3d1
better method to create span
dot-agi May 28, 2025
bf850af
Revert "fix api consistency issues in `decorators/__init__.py`"
dot-agi May 28, 2025
c9a0532
Revert "better method to create span"
dot-agi May 28, 2025
d7f20df
add some tests
dot-agi May 28, 2025
d4fd257
remove `Annotated`
dot-agi May 28, 2025
f2dcc41
bad GitHub copilot
dot-agi May 28, 2025
3cc4bf8
add compatibility for both Python <3.11 and >=3.11
dot-agi May 28, 2025
9881aa5
Merge branch 'main' into fix/stable-sdk-fixes
Dwij1704 May 29, 2025
de37a13
use `logger` instead of `warnings`
dot-agi May 29, 2025
4829179
purge `LiveSpanProcessor` from codebase
dot-agi May 29, 2025
c184be2
add `dev-llm` to make @Dwij1704 happy :)
dot-agi May 29, 2025
9ecfeb9
Revert "add `dev-llm` to make @Dwij1704 happy :)"
dot-agi May 29, 2025
96629d9
Merge branch 'main' into fix/stable-sdk-fixes
dot-agi Jun 2, 2025
58bbb7a
Merge branch 'main' into fix/stable-sdk-fixes
dot-agi Jun 2, 2025
21bf90d
import `functools`
dot-agi Jun 2, 2025
7d90392
Merge branch 'main' into fix/stable-sdk-fixes
dot-agi Jun 2, 2025
dc35730
@bboynton97 use the new tracer variable pls #1030
dot-agi Jun 4, 2025
c7cd7e3
Merge branch 'main' into fix/stable-sdk-fixes
dot-agi Jun 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions agentops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,20 +208,20 @@ def start_trace(
return tracing_core.start_trace(trace_name=trace_name, tags=tags)


def end_trace(trace_context: Optional[TraceContext] = None, end_state: str = "Success") -> None:
def end_trace(tracer: Optional[TraceContext] = None, end_state: str = "Success") -> None:
"""
Ends a trace (its root span) and finalizes it.
If no trace_context is provided, ends all active session spans.
If no tracer is provided, ends all active session spans.

Args:
trace_context: The TraceContext object returned by start_trace. If None, ends all active traces.
tracer: The TraceContext object returned by start_trace. If None, ends all active traces.
end_state: The final state of the trace (e.g., "Success", "Failure", "Error").
"""
tracing_core = TracingCore.get_instance()
if not tracing_core.initialized:
logger.warning("AgentOps SDK not initialized. Cannot end trace.")
return
tracing_core.end_trace(trace_context=trace_context, end_state=end_state)
tracing_core.end_trace(tracer=tracer, end_state=end_state)


__all__ = [
Expand Down
44 changes: 22 additions & 22 deletions agentops/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from agentops.legacy import Session

# Global variables to hold the client's auto-started trace and its legacy session wrapper
_client_init_trace_context: Optional[TraceContext] = None
_client_init_tracer: Optional[TraceContext] = None
_client_legacy_session_for_init_trace: Optional[Session] = None

# Single atexit handler registered flag
Expand All @@ -20,18 +20,18 @@

def _end_init_trace_atexit():
"""Global atexit handler to end the client's auto-initialized trace during shutdown."""
global _client_init_trace_context, _client_legacy_session_for_init_trace
if _client_init_trace_context is not None:
global _client_init_tracer, _client_legacy_session_for_init_trace
if _client_init_tracer is not None:

Check warning on line 24 in agentops/client/client.py

View check run for this annotation

Codecov / codecov/patch

agentops/client/client.py#L24

Added line #L24 was not covered by tests
logger.debug("Auto-ending client's init trace during shutdown.")
try:
# Use TracingCore to end the trace directly
tracing_core = TracingCore.get_instance()
if tracing_core.initialized and _client_init_trace_context.span.is_recording():
tracing_core.end_trace(_client_init_trace_context, end_state="Shutdown")
if tracing_core.initialized and _client_init_tracer.span.is_recording():
tracing_core.end_trace(_client_init_tracer, end_state="Shutdown")

Check warning on line 30 in agentops/client/client.py

View check run for this annotation

Codecov / codecov/patch

agentops/client/client.py#L29-L30

Added lines #L29 - L30 were not covered by tests
except Exception as e:
logger.warning(f"Error ending client's init trace during shutdown: {e}")
finally:
_client_init_trace_context = None
_client_init_tracer = None

Check warning on line 34 in agentops/client/client.py

View check run for this annotation

Codecov / codecov/patch

agentops/client/client.py#L34

Added line #L34 was not covered by tests
_client_legacy_session_for_init_trace = None # Clear its legacy wrapper too


Expand All @@ -40,7 +40,7 @@

config: Config
_initialized: bool
_init_trace_context: Optional[TraceContext] = None # Stores the context of the auto-started trace
_init_tracer: Optional[TraceContext] = None # Stores the context of the auto-started trace
_legacy_session_for_init_trace: Optional[
Session
] = None # Stores the legacy Session wrapper for the auto-started trace
Expand All @@ -53,7 +53,7 @@
if cls.__instance is None:
cls.__instance = super(Client, cls).__new__(cls)
# Initialize instance variables that should only be set once per instance
cls.__instance._init_trace_context = None
cls.__instance._init_tracer = None
cls.__instance._legacy_session_for_init_trace = None
return cls.__instance

Expand All @@ -66,7 +66,7 @@
): # Ensure init logic runs only once per actual initialization intent
self.config = Config() # Initialize config here for the instance
self._initialized = False
# self._init_trace_context = None # Already done in __new__
# self._init_tracer = None # Already done in __new__
# self._legacy_session_for_init_trace = None # Already done in __new__

def init(self, **kwargs: Any) -> None: # Return type updated to None
Expand All @@ -81,10 +81,10 @@
logger.warning("AgentOps Client being re-initialized with a different API key. This is unusual.")
# Reset initialization status to allow re-init with new key/config
self._initialized = False
if self._init_trace_context and self._init_trace_context.span.is_recording():
if self._init_tracer and self._init_tracer.span.is_recording():

Check warning on line 84 in agentops/client/client.py

View check run for this annotation

Codecov / codecov/patch

agentops/client/client.py#L84

Added line #L84 was not covered by tests
logger.warning("Ending previously auto-started trace due to re-initialization.")
TracingCore.get_instance().end_trace(self._init_trace_context, "Reinitialized")
self._init_trace_context = None
TracingCore.get_instance().end_trace(self._init_tracer, "Reinitialized")
self._init_tracer = None

Check warning on line 87 in agentops/client/client.py

View check run for this annotation

Codecov / codecov/patch

agentops/client/client.py#L86-L87

Added lines #L86 - L87 were not covered by tests
self._legacy_session_for_init_trace = None

if self.initialized:
Expand Down Expand Up @@ -133,31 +133,31 @@

# Auto-start trace if configured
if self.config.auto_start_session:
if self._init_trace_context is None or not self._init_trace_context.span.is_recording():
if self._init_tracer is None or not self._init_tracer.span.is_recording():
logger.debug("Auto-starting init trace.")
trace_name = self.config.trace_name or "default"
self._init_trace_context = tracing_core.start_trace(
self._init_tracer = tracing_core.start_trace(
trace_name=trace_name,
tags=list(self.config.default_tags) if self.config.default_tags else None,
is_init_trace=True,
)
if self._init_trace_context:
self._legacy_session_for_init_trace = Session(self._init_trace_context)
if self._init_tracer:
self._legacy_session_for_init_trace = Session(self._init_tracer)

# For backward compatibility, also update the global references in legacy and client modules
# These globals are what old code might have been using via agentops.legacy.get_session() or similar indirect access.
global _client_init_trace_context, _client_legacy_session_for_init_trace
_client_init_trace_context = self._init_trace_context
global _client_init_tracer, _client_legacy_session_for_init_trace
_client_init_tracer = self._init_tracer
_client_legacy_session_for_init_trace = self._legacy_session_for_init_trace

# Update legacy module's _current_session and _current_trace_context
# Update legacy module's _current_session and _current_tracer
# This is tricky; direct access to another module's globals is not ideal.
# Prefer explicit calls if possible, but for maximum BC:
try:
import agentops.legacy

agentops.legacy._current_session = self._legacy_session_for_init_trace
agentops.legacy._current_trace_context = self._init_trace_context
agentops.legacy._current_tracer = self._init_tracer
except ImportError:
pass # Should not happen

Expand Down Expand Up @@ -196,7 +196,7 @@
# Remove the old __instance = None at the end of the class definition if it's a repeat
# __instance = None # This was a class variable, should be defined once

# Make _init_trace_context and _legacy_session_for_init_trace accessible
# Make _init_tracer and _legacy_session_for_init_trace accessible
# to the atexit handler if it becomes a static/class method or needs access
# For now, the atexit handler is global and uses global vars copied from these.

Expand All @@ -210,4 +210,4 @@
# For now, _client_legacy_session_for_init_trace is the primary global for the auto-init trace's legacy Session.

# Remove the old global _active_session defined at the top of this file if it's no longer the primary mechanism.
# The new globals _client_init_trace_context and _client_legacy_session_for_init_trace handle the auto-init trace.
# The new globals _client_init_tracer and _client_legacy_session_for_init_trace handle the auto-init trace.
78 changes: 36 additions & 42 deletions agentops/legacy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from agentops.sdk.core import TracingCore, TraceContext

_current_session: Optional["Session"] = None
_current_trace_context: Optional[TraceContext] = None
_current_tracer: Optional[TraceContext] = None


class Session:
Expand All @@ -28,22 +28,22 @@
- end_session(): Called when a CrewAI run completes
"""

def __init__(self, trace_context: Optional[TraceContext]):
self.trace_context = trace_context
def __init__(self, tracer: Optional[TraceContext]):
self.tracer = tracer

@property
def span(self) -> Optional[Any]:
return self.trace_context.span if self.trace_context else None
return self.tracer.span if self.tracer else None

@property
def token(self) -> Optional[Any]:
return self.trace_context.token if self.trace_context else None
return self.tracer.token if self.tracer else None

def __del__(self):
if self.trace_context and self.trace_context.span and self.trace_context.span.is_recording():
if not self.trace_context.is_init_trace:
if self.tracer and self.tracer.span and self.tracer.span.is_recording():
if not self.tracer.is_init_trace:
logger.warning(
f"Legacy Session (trace ID: {self.trace_context.span.get_span_context().span_id}) \
f"Legacy Session (trace ID: {self.tracer.span.get_span_context().span_id}) \
was garbage collected but its trace might still be recording. Ensure legacy sessions are ended with end_session()."
)

Expand All @@ -67,7 +67,7 @@
@deprecated Use agentops.start_trace() instead.
Starts a legacy AgentOps session. Calls TracingCore.start_trace internally.
"""
global _current_session, _current_trace_context
global _current_session, _current_tracer
tracing_core = TracingCore.get_instance()

if not tracing_core.initialized:
Expand All @@ -79,33 +79,33 @@
logger.warning("AgentOps client init failed during legacy start_session. Creating dummy session.")
dummy_session = Session(None)
_current_session = dummy_session
_current_trace_context = None
_current_tracer = None

Check warning on line 82 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L82

Added line #L82 was not covered by tests
return dummy_session
except Exception as e:
logger.warning(f"AgentOps client init failed: {str(e)}. Creating dummy session.")
dummy_session = Session(None)
_current_session = dummy_session
_current_trace_context = None
_current_tracer = None

Check warning on line 88 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L88

Added line #L88 was not covered by tests
return dummy_session

trace_context = tracing_core.start_trace(trace_name="session", tags=tags)
if trace_context is None:
tracer = tracing_core.start_trace(trace_name="session", tags=tags)
if tracer is None:
logger.error("Failed to start trace via TracingCore. Returning dummy session.")
dummy_session = Session(None)
_current_session = dummy_session
_current_trace_context = None
_current_tracer = None

Check warning on line 96 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L96

Added line #L96 was not covered by tests
return dummy_session

session_obj = Session(trace_context)
session_obj = Session(tracer)
_current_session = session_obj
_current_trace_context = trace_context
_current_tracer = tracer

try:
import agentops.client.client

agentops.client.client._active_session = session_obj # type: ignore
if hasattr(agentops.client.client, "_active_trace_context"):
agentops.client.client._active_trace_context = trace_context # type: ignore
if hasattr(agentops.client.client, "_active_tracer"):
agentops.client.client._active_tracer = tracer # type: ignore

Check warning on line 108 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L108

Added line #L108 was not covered by tests
except (ImportError, AttributeError):
pass
return session_obj
Expand All @@ -128,61 +128,55 @@
Ends a legacy AgentOps session. Calls TracingCore.end_trace internally.
Supports multiple calling patterns for backward compatibility.
"""
global _current_session, _current_trace_context
global _current_session, _current_tracer
tracing_core = TracingCore.get_instance()

if not tracing_core.initialized:
logger.debug("Ignoring end_session: TracingCore not initialized.")
return

target_trace_context: Optional[TraceContext] = None
target_tracer: Optional[TraceContext] = None
end_state_from_args = "Success"
extra_attributes = kwargs.copy()

if isinstance(session_or_status, Session):
target_trace_context = session_or_status.trace_context
target_tracer = session_or_status.tracer
if "end_state" in extra_attributes:
end_state_from_args = str(extra_attributes.pop("end_state"))
elif isinstance(session_or_status, str):
end_state_from_args = session_or_status
target_trace_context = _current_trace_context
target_tracer = _current_tracer
if "end_state" in extra_attributes:
end_state_from_args = str(extra_attributes.pop("end_state"))
elif session_or_status is None and kwargs:
target_trace_context = _current_trace_context
target_tracer = _current_tracer
if "end_state" in extra_attributes:
end_state_from_args = str(extra_attributes.pop("end_state"))
else:
target_trace_context = _current_trace_context
target_tracer = _current_tracer

Check warning on line 156 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L156

Added line #L156 was not covered by tests
if "end_state" in extra_attributes:
end_state_from_args = str(extra_attributes.pop("end_state"))

if not target_trace_context:
if not target_tracer:
logger.warning("end_session called but no active trace context found.")
return

if target_trace_context.span and extra_attributes:
_set_span_attributes(target_trace_context.span, extra_attributes)
if target_tracer.span and extra_attributes:
_set_span_attributes(target_tracer.span, extra_attributes)

tracing_core.end_trace(target_trace_context, end_state=end_state_from_args)
tracing_core.end_trace(target_tracer, end_state=end_state_from_args)

if target_trace_context is _current_trace_context:
if target_tracer is _current_tracer:
_current_session = None
_current_trace_context = None
_current_tracer = None

try:
import agentops.client.client

if (
hasattr(agentops.client.client, "_active_trace_context")
and agentops.client.client._active_trace_context is target_trace_context
): # type: ignore
agentops.client.client._active_trace_context = None # type: ignore
if hasattr(agentops.client.client, "_active_tracer") and agentops.client.client._active_tracer is target_tracer: # type: ignore
agentops.client.client._active_tracer = None # type: ignore

Check warning on line 177 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L177

Added line #L177 was not covered by tests
agentops.client.client._active_session = None # type: ignore
elif (
hasattr(agentops.client.client, "_init_trace_context")
and agentops.client.client._init_trace_context is target_trace_context
): # type: ignore
elif hasattr(agentops.client.client, "_init_tracer") and agentops.client.client._init_tracer is target_tracer: # type: ignore
logger.debug("Legacy end_session called on client's auto-init trace. This is unusual.")
except (ImportError, AttributeError):
pass
Expand All @@ -198,12 +192,12 @@
return

# Use the new end_trace functionality to end all active traces
tracing_core.end_trace(trace_context=None, end_state="Success")
tracing_core.end_trace(tracer=None, end_state="Success")

Check warning on line 195 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L195

Added line #L195 was not covered by tests

# Clear legacy global state
global _current_session, _current_trace_context
global _current_session, _current_tracer
_current_session = None
_current_trace_context = None
_current_tracer = None

Check warning on line 200 in agentops/legacy/__init__.py

View check run for this annotation

Codecov / codecov/patch

agentops/legacy/__init__.py#L200

Added line #L200 was not covered by tests


def ToolEvent(*args: Any, **kwargs: Any) -> None:
Expand Down
17 changes: 17 additions & 0 deletions agentops/sdk/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,20 @@ def camel_to_snake(text: str) -> str:

text = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", text)
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", text).lower()


def format_trace_id(trace_id: int) -> str:
"""
Format trace ID consistently as hex string with error handling.

Args:
trace_id: The trace ID integer to format

Returns:
Formatted trace ID as hex string
"""
try:
return f"{trace_id:x}"
except (TypeError, ValueError):
# Handle case where trace_id is not a valid integer
return str(trace_id)
Loading
Loading