Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.2.1"
rev: "v0.9.1"
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
exclude: ^examples/
- id: ruff-format
exclude: ^examples/
17 changes: 8 additions & 9 deletions agentops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@
# Client global instance; one per process runtime
_client = Client()


def record(event):
"""
Legacy function to record an event. This is kept for backward compatibility.

In the current version, this simply sets the end_timestamp on the event.

Args:
event: The event to record
"""
from agentops.helpers.time import get_ISO_time

# TODO: Manual timestamp assignment is a temporary fix; should use proper event lifecycle
if event and hasattr(event, 'end_timestamp'):
if event and hasattr(event, "end_timestamp"):
event.end_timestamp = get_ISO_time()

return event


Expand Down Expand Up @@ -141,6 +142,7 @@ def configure(**kwargs):

_client.configure(**kwargs)


# For backwards compatibility and testing


Expand All @@ -149,8 +151,7 @@ def get_client() -> Client:
return _client



from agentops.legacy import * # type: ignore
from agentops.legacy import * # noqa: E402, F403

__all__ = [
"init",
Expand All @@ -159,6 +160,4 @@ def get_client() -> Client:
"record",
"start_session",
"end_session",
"track_agent",
"track_tool",
]
3 changes: 1 addition & 2 deletions agentops/client/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
class TokenFetcher(Protocol):
"""Protocol for token fetching functions"""

def __call__(self, api_key: str) -> str:
...
def __call__(self, api_key: str) -> str: ...


class BaseApiClient:
Expand Down
2 changes: 1 addition & 1 deletion agentops/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def init(self, **kwargs):
session = start_session(tags=list(self.config.default_tags))
else:
session = start_session()

return session

def configure(self, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions agentops/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Config:
default_factory=lambda: get_env_int("AGENTOPS_MAX_WAIT_TIME", 5000),
metadata={"description": "Maximum time in milliseconds to wait for API responses"},
)

export_flush_interval: int = field(
default_factory=lambda: get_env_int("AGENTOPS_EXPORT_FLUSH_INTERVAL", 1000),
metadata={"description": "Time interval in milliseconds between automatic exports of telemetry data"},
Expand Down Expand Up @@ -154,7 +154,7 @@ def configure(

if max_wait_time is not None:
self.max_wait_time = max_wait_time

if export_flush_interval is not None:
self.export_flush_interval = export_flush_interval

Expand Down
1 change: 1 addition & 0 deletions agentops/helpers/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
def is_coroutine_or_generator(fn: Any) -> bool:
"""Check if a function is asynchronous (coroutine or async generator)"""
import inspect

return inspect.iscoroutinefunction(fn) or inspect.isasyncgenfunction(fn)
81 changes: 44 additions & 37 deletions agentops/legacy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Session:
"""
This class provides compatibility with CrewAI >= 0.105.0, which uses an event-based
integration pattern where it calls methods directly on the Session object:

- create_agent(): Called when a CrewAI agent is created
- record(): Called when a CrewAI tool is used
- end_session(): Called when a CrewAI run completes
Expand All @@ -42,7 +42,7 @@ def __del__(self):
def create_agent(self, name: Optional[str] = None, agent_id: Optional[str] = None, **kwargs):
"""
Method to create an agent for CrewAI >= 0.105.0 compatibility.

CrewAI >= 0.105.0 calls this with:
- name=agent.role
- agent_id=str(agent.id)
Expand All @@ -52,19 +52,19 @@ def create_agent(self, name: Optional[str] = None, agent_id: Optional[str] = Non
def record(self, event=None):
"""
Method to record events for CrewAI >= 0.105.0 compatibility.

CrewAI >= 0.105.0 calls this with a tool event when a tool is used.
"""
pass

def end_session(self, **kwargs):
"""
Method to end the session for CrewAI >= 0.105.0 compatibility.

CrewAI >= 0.105.0 calls this with:
- end_state="Success"
- end_state_reason="Finished Execution"

forces a flush to ensure the span is exported immediately.
"""
_set_span_attributes(self.span, kwargs)
Expand All @@ -75,7 +75,7 @@ def end_session(self, **kwargs):
def _create_session_span(tags: Union[Dict[str, Any], List[str], None] = None) -> tuple:
"""
Helper function to create a session span with tags.

This is an internal function used by start_session() to create the
from the SDK to create a span with kind=SpanKind.SESSION.

Expand Down Expand Up @@ -106,12 +106,12 @@ def start_session(
This function creates and starts a new session span, which can be used to group
related operations together. The session will remain active until end_session
is called either with the Session object or with kwargs.

Usage patterns:
1. Standard pattern: session = start_session(); end_session(session)
2. CrewAI < 0.105.0: start_session(); end_session(end_state="Success", ...)
3. CrewAI >= 0.105.0: session = start_session(); session.end_session(end_state="Success", ...)

This function stores the session in a global variable to support the CrewAI
< 0.105.0 pattern where end_session is called without the session object.

Expand All @@ -127,11 +127,12 @@ def start_session(
AgentOpsClientNotInitializedException: If the client is not initialized
"""
global _current_session

if not TracingCore.get_instance().initialized:
from agentops import Client

Client().init()

span, context, token = _create_session_span(tags)
session = Session(span, token)
_current_session = session
Expand All @@ -141,14 +142,14 @@ def start_session(
def _set_span_attributes(span: Any, attributes: Dict[str, Any]) -> None:
"""
Helper to set attributes on a span.

Args:
span: The span to set attributes on
attributes: The attributes to set as a dictionary
"""
if not attributes or not hasattr(span, "set_attribute"):
return

for key, value in attributes.items():
span.set_attribute(f"agentops.status.{key}", str(value))

Expand All @@ -159,11 +160,12 @@ def _flush_span_processors() -> None:
"""
try:
from opentelemetry.trace import get_tracer_provider

tracer_provider = get_tracer_provider()
tracer_provider.force_flush() # type: ignore
except Exception as e:
logger.warning(f"Failed to force flush span processor: {e}")


def end_session(session_or_status: Any = None, **kwargs) -> None:
"""
Expand All @@ -181,24 +183,25 @@ def end_session(session_or_status: Any = None, **kwargs) -> None:
Args:
session_or_status: The session object returned by start_session,
or a string representing the status (for backwards compatibility)
**kwargs: Additional arguments for CrewAI < 0.105.0 compatibility.
**kwargs: Additional arguments for CrewAI < 0.105.0 compatibility.
CrewAI < 0.105.0 passes these named arguments:
- end_state="Success"
- end_state_reason="Finished Execution"
- is_auto_end=True

When called this way, the function will use the most recently
created session via start_session().
"""
from agentops.sdk.decorators.utility import _finalize_span

from agentops.sdk.core import TracingCore

if not TracingCore.get_instance().initialized:
logger.debug("Ignoring end_session call - TracingCore not initialized")
return

# In some old implementations, and in crew < 0.10.5 `end_session` will be
# called with a single string as a positional argument like: "Success"
# In some old implementations, and in crew < 0.10.5 `end_session` will be
# called with a single string as a positional argument like: "Success"

# Handle the CrewAI < 0.105.0 integration pattern where end_session is called
# with only named parameters. In this pattern, CrewAI does not keep a reference
Expand All @@ -211,18 +214,18 @@ def end_session(session_or_status: Any = None, **kwargs) -> None:
# )
if session_or_status is None and kwargs:
global _current_session

if _current_session is not None:
_set_span_attributes(_current_session.span, kwargs)
_finalize_span(_current_session.span, _current_session.token)
_flush_span_processors()
_current_session = None
return

# Handle the standard pattern and CrewAI >= 0.105.0 pattern where a Session object is passed.
# In both cases, we call _finalize_span with the span and token from the Session.
# This is the most direct and precise way to end a specific session.
if hasattr(session_or_status, 'span') and hasattr(session_or_status, 'token'):
if hasattr(session_or_status, "span") and hasattr(session_or_status, "token"):
_set_span_attributes(session_or_status.span, kwargs)
_finalize_span(session_or_status.span, session_or_status.token)
_flush_span_processors()
Expand All @@ -231,8 +234,8 @@ def end_session(session_or_status: Any = None, **kwargs) -> None:
def end_all_sessions():
"""
@deprecated
We don't automatically track more than one session, so just end the session
that we are tracking.
We don't automatically track more than one session, so just end the session
that we are tracking.
"""
end_session()

Expand All @@ -249,35 +252,35 @@ def ErrorEvent(*args, **kwargs):
"""
@deprecated
Use tracing instead.

For backward compatibility with tests, this returns a minimal object with the
required attributes.
"""
from agentops.helpers.time import get_ISO_time

class LegacyErrorEvent:
def __init__(self):
self.init_timestamp = get_ISO_time()
self.end_timestamp = None

return LegacyErrorEvent()


def ActionEvent(*args, **kwargs):
"""
@deprecated
Use tracing instead.

For backward compatibility with tests, this returns a minimal object with the
required attributes.
"""
from agentops.helpers.time import get_ISO_time

class LegacyActionEvent:
def __init__(self):
self.init_timestamp = get_ISO_time()
self.end_timestamp = None

return LegacyActionEvent()


Expand All @@ -294,28 +297,32 @@ def track_agent(*args, **kwargs):
@deprecated
Decorator for marking agents in legacy projects.
"""

def noop(f):
return f

return noop


def track_tool(*args, **kwargs):
"""
@deprecated
Decorator for marking tools and legacy projects.
Decorator for marking tools and legacy projects.
"""

def noop(f):
return f

return noop


__all__ = [
"start_session",
"end_session",
"ToolEvent",
"ErrorEvent",
"ActionEvent",
"track_agent",
"start_session",
"end_session",
"ToolEvent",
"ErrorEvent",
"ActionEvent",
"track_agent",
"track_tool",
"end_all_sessions"
"end_all_sessions",
]
2 changes: 2 additions & 0 deletions agentops/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

# Import core components
from agentops.sdk.core import TracingCore

# Import decorators
from agentops.sdk.decorators import agent, operation, session, task, workflow

# from agentops.sdk.traced import TracedObject # Merged into TracedObject
from agentops.sdk.types import TracingConfig

Expand Down
Loading
Loading