Skip to content

Commit

Permalink
Merge branch 'main' into stop-instrumenting
Browse files Browse the repository at this point in the history
  • Loading branch information
bboynton97 committed May 3, 2024
2 parents f032b7a + 4f79bb0 commit 592aac5
Show file tree
Hide file tree
Showing 14 changed files with 65 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
python-version: [3.11]
python-version: [3.7,3.8,3.9,3.10,3.11,3.12]

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions agentops/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# agentops/__init__.py
from os import environ
from typing import Optional, List
from typing import Optional, List, Union

from .client import Client
from .config import Configuration
Expand Down Expand Up @@ -87,7 +87,7 @@ def start_session(tags: Optional[List[str]] = None, config: Optional[Configurati
return Client().start_session(tags, config, inherited_session_id)


def record(event: Event | ErrorEvent):
def record(event: Union[Event, ErrorEvent]):
"""
Record an event with the AgentOps service.
Expand Down
8 changes: 5 additions & 3 deletions agentops/agent.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from typing import Union

from .log_config import logger
from uuid import uuid4
from agentops import Client
from inspect import isclass, isfunction


def track_agent(name: str | None = None):
def track_agent(name: Union[str, None] = None):
def decorator(obj):
if name:
obj.agent_ops_agent_name = name
Expand All @@ -15,7 +17,7 @@ def decorator(obj):
def new_init(self, *args, **kwargs):
try:
original_init(self, *args, **kwargs)
self.agent_ops_agent_id = uuid4()
self.agent_ops_agent_id = str(uuid4())
Client().create_agent(self.agent_ops_agent_id, self.agent_ops_agent_name)
except AttributeError as e:
logger.warning("AgentOps failed to track an agent. This often happens if agentops.init() was not "
Expand All @@ -25,7 +27,7 @@ def new_init(self, *args, **kwargs):
obj.__init__ = new_init

elif isfunction(obj):
obj.agent_ops_agent_id = uuid4()
obj.agent_ops_agent_id = str(uuid4())
Client().create_agent(obj.agent_ops_agent_id, obj.agent_ops_agent_name)

else:
Expand Down
29 changes: 16 additions & 13 deletions agentops/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .worker import Worker
from .host_env import get_host_env
from uuid import uuid4
from typing import Optional, List
from typing import Optional, List, Union
import traceback
from .log_config import logger, set_logging_level_info
from decimal import Decimal
Expand Down Expand Up @@ -73,9 +73,9 @@ def __init__(self,
DeprecationWarning, stacklevel=2)
instrument_llm_calls = instrument_llm_calls or override

self._session = None
self._worker = None
self._tags_for_future_session = None
self._session: Optional[Session] = None
self._worker: Optional[Worker] = None
self._tags: Optional[List[str]] = tags

self._env_data_opt_out = os.getenv('AGENTOPS_ENV_DATA_OPT_OUT') and os.getenv('AGENTOPS_ENV_DATA_OPT_OUT').lower() == 'true'

Expand Down Expand Up @@ -113,7 +113,7 @@ def add_tags(self, tags: List[str]):
else:
self._session.tags = tags

if self._session is not None:
if self._session is not None and self._worker is not None:
self._worker.update_session(self._session)

def set_tags(self, tags: List[str]):
Expand All @@ -125,11 +125,11 @@ def set_tags(self, tags: List[str]):
"""
self._tags_for_future_session = tags

if self._session is not None:
if self._session is not None and self._worker is not None:
self._session.tags = tags
self._worker.update_session(self._session)

def record(self, event: Event | ErrorEvent):
def record(self, event: Union[Event, ErrorEvent]):
"""
Record an event with the AgentOps service.
Expand All @@ -138,7 +138,7 @@ def record(self, event: Event | ErrorEvent):
"""
if isinstance(event, Event) and not event.end_timestamp or event.init_timestamp == event.end_timestamp:
event.end_timestamp = get_ISO_time()
if self._session is not None and not self._session.has_ended:
if self._session is not None and not self._session.has_ended and self._worker is not None:
if isinstance(event, ErrorEvent):
if event.trigger_event:
event.trigger_event_id = event.trigger_event.id
Expand Down Expand Up @@ -247,8 +247,8 @@ def start_session(self, tags: Optional[List[str]] = None, config: Optional[Confi
self._session = None
return logger.warning("🖇 AgentOps: Cannot start session")

logger.info('View info on this session at https://app.agentops.ai/drilldown?session_id={}'
.format(self._session.session_id))
logger.info('View info on this session at https://app.agentops.ai/drilldown?session_id=%s',
self._session.session_id)

return self._session.session_id

Expand All @@ -269,10 +269,14 @@ def end_session(self,

if not any(end_state == state.value for state in EndState):
return logger.warning("🖇 AgentOps: Invalid end_state. Please use one of the EndState enums")

if self._worker is None or self._worker._session is None:
return logger.warning("🖇 AgentOps: Cannot end session - no current worker or session")

self._session.video = video
self._session.end_session(end_state, end_state_reason)
token_cost = self._worker.end_session(self._session)

if token_cost == 'unknown':
print('🖇 AgentOps: Could not determine cost of run.')
else:
Expand All @@ -287,7 +291,7 @@ def create_agent(self, agent_id: str, name: str):
self._worker.create_agent(agent_id, name)

def _handle_unclean_exits(self):
def cleanup(end_state: Optional[str] = 'Fail', end_state_reason: Optional[str] = None):
def cleanup(end_state: str = 'Fail', end_state_reason: Optional[str] = None):
# Only run cleanup function if session is created
if self._session is not None:
self.end_session(end_state=end_state,
Expand All @@ -302,8 +306,7 @@ def signal_handler(signum, frame):
frame: The current stack frame.
"""
signal_name = 'SIGINT' if signum == signal.SIGINT else 'SIGTERM'
logger.info(
f'🖇 AgentOps: {signal_name} detected. Ending session...')
logger.info('🖇 AgentOps: %s detected. Ending session...', signal_name)
self.end_session(end_state='Fail',
end_state_reason=f'Signal {signal_name} detected')
sys.exit(0)
Expand Down
18 changes: 9 additions & 9 deletions agentops/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""

from dataclasses import asdict, dataclass, field
from typing import List, Optional
from typing import Any, Dict, List, Optional, Sequence, Union
from .helpers import get_ISO_time, check_call_stack_for_agent_id
from .enums import EventType, Models
from uuid import UUID, uuid4
Expand Down Expand Up @@ -38,8 +38,8 @@ class Event:
"""

event_type: str # EventType.ENUM.value
params: Optional[dict] = None
returns: Optional[str] = None
params: Optional[Union[str, Dict[str, Any]]] = None
returns: Optional[Union[str, Dict[str, Any]]] = None
init_timestamp: Optional[str] = field(default_factory=get_ISO_time)
end_timestamp: str = field(default_factory=get_ISO_time)
agent_id: Optional[UUID] = field(default_factory=check_call_stack_for_agent_id)
Expand All @@ -60,7 +60,7 @@ class ActionEvent(Event):
event_type: str = EventType.ACTION.value
# TODO: Should not be optional, but non-default argument 'agent_id' follows default argument error
action_type: Optional[str] = None
logs: Optional[str] = None
logs: Optional[Union[str, Sequence[Any]]] = None
screenshot: Optional[str] = None

# May be needed if we keep Optional for agent_id
Expand All @@ -86,11 +86,11 @@ class LLMEvent(Event):

event_type: str = EventType.LLM.value
thread_id: Optional[UUID] = None
prompt: str | List = None
prompt: Optional[Union[str, List]] = None
prompt_tokens: Optional[int] = None
completion: str | object = None
completion: Union[str, object] = None
completion_tokens: Optional[int] = None
model: Optional[Models | str] = None
model: Optional[Union[Models, str]] = None


@dataclass
Expand All @@ -104,7 +104,7 @@ class ToolEvent(Event):
"""
event_type: str = EventType.TOOL.value
name: Optional[str] = None
logs: Optional[str | dict] = None
logs: Optional[Union[str, dict]] = None

# Does not inherit from Event because error will (optionally) be linked to an ActionEvent, LLMEvent, etc that will have the details

Expand All @@ -129,7 +129,7 @@ class ErrorEvent():
exception: Optional[BaseException] = None
error_type: Optional[str] = None
code: Optional[str] = None
details: Optional[str] = None
details: Optional[Union[str, Dict[str, str]]] = None
logs: Optional[str] = field(default_factory=traceback.format_exc)
timestamp: str = field(default_factory=get_ISO_time)

Expand Down
12 changes: 7 additions & 5 deletions agentops/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from pprint import pprint, pformat
from pprint import pformat
from functools import wraps
import time
from datetime import datetime
import json
import inspect
from typing import Union

from .log_config import logger
from uuid import UUID
import os
Expand Down Expand Up @@ -76,16 +78,16 @@ def remove_none_values(value):
return json.dumps(cleaned_obj, default=default)


def check_call_stack_for_agent_id() -> str | None:
def check_call_stack_for_agent_id() -> Union[UUID, None]:
for frame_info in inspect.stack():
# Look through the call stack for the class that called the LLM
local_vars = frame_info.frame.f_locals
for var in local_vars.values():
# We stop looking up the stack at main because after that we see global variables
if var == "__main__":
return
return None
if hasattr(var, 'agent_ops_agent_id') and getattr(var, 'agent_ops_agent_id'):
logger.debug('LLM call from agent named: ' + getattr(var, 'agent_ops_agent_name'))
logger.debug('LLM call from agent named: %s', getattr(var, 'agent_ops_agent_name'))
return getattr(var, 'agent_ops_agent_id')
return None

Expand All @@ -95,7 +97,7 @@ def get_agentops_version():
pkg_version = version("agentops")
return pkg_version
except Exception as e:
logger.warning(f"Error reading package version: {e}")
logger.warning('Error reading package version: %s', e)
return None


Expand Down
14 changes: 6 additions & 8 deletions agentops/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ class Response:
def __init__(self, status: HttpStatus = HttpStatus.UNKNOWN, body: Optional[dict] = None):
self.status: HttpStatus = status
self.code: int = status.value
self.body = body
if not self.body:
self.body = {}
self.body = body if body else {}

def parse(self, res: requests.models.Response):
res_body = res.json()
Expand Down Expand Up @@ -87,7 +85,7 @@ def post(url: str, payload: bytes, api_key: Optional[str] = None, parent_key: Op
except requests.exceptions.HTTPError as e:
try:
result.parse(e.response)
except:
except Exception:
result = Response()
result.code = e.response.status_code
result.status = Response.get_status(e.response.status_code)
Expand All @@ -96,12 +94,12 @@ def post(url: str, payload: bytes, api_key: Optional[str] = None, parent_key: Op
result.body = {'error': str(e)}

if result.code == 401:
logger.warning(
f'🖇 AgentOps: Could not post data - API server rejected your API key: {api_key}')
logger.warning('🖇 AgentOps: Could not post data - API server rejected your API key: %s',
api_key)
if result.code == 400:
logger.warning(f'🖇 AgentOps: Could not post data - {result.body}')
logger.warning('🖇 AgentOps: Could not post data - %s', result.body)
if result.code == 500:
logger.warning(
f'🖇 AgentOps: Could not post data - internal server error')
'🖇 AgentOps: Could not post data - internal server error')

return result
4 changes: 2 additions & 2 deletions agentops/langchain_callback_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, api_key: Optional[str] = None,
max_queue_size: Optional[int] = None,
tags: Optional[List[str]] = None):

client_params = {
client_params: Dict[str, Any] = {
'api_key': api_key,
'endpoint': endpoint,
'max_wait_time': max_wait_time,
Expand Down Expand Up @@ -331,7 +331,7 @@ def __init__(self, api_key: Optional[str] = None,
max_queue_size: Optional[int] = None,
tags: Optional[List[str]] = None):

client_params = {
client_params: Dict[str, Any] = {
'api_key': api_key,
'endpoint': endpoint,
'max_wait_time': max_wait_time,
Expand Down
3 changes: 3 additions & 0 deletions agentops/llm_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .event import LLMEvent, ErrorEvent
from .helpers import get_ISO_time, check_call_stack_for_agent_id
import inspect
from typing import Optional
import pprint

original_create = None
Expand All @@ -29,6 +30,8 @@ class LlmTracker:

def __init__(self, client):
self.client = client
self.completion = ""
self.llm_event: Optional[LLMEvent] = None

def _handle_response_v0_openai(self, response, kwargs, init_timestamp):
"""Handle responses for OpenAI versions <v1.0.0"""
Expand Down
10 changes: 5 additions & 5 deletions agentops/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ class Session:

def __init__(self, session_id: UUID, tags: Optional[List[str]] = None, host_env: Optional[dict] = None):
self.end_timestamp = None
self.end_state = None
self.end_state: Optional[str] = None
self.session_id = session_id
self.init_timestamp = get_ISO_time()
self.tags = tags
self.video = None
self.end_state_reason = None
self.video: Optional[str] = None
self.end_state_reason: Optional[str] = None
self.host_env = host_env

def set_session_video(self, video: str):
def set_session_video(self, video: str) -> None:
"""
Sets a url to the video recording of the session.
Expand All @@ -38,7 +38,7 @@ def set_session_video(self, video: str):
"""
self.video = video

def end_session(self, end_state: str = "Indeterminate", end_state_reason: Optional[str] = None):
def end_session(self, end_state: str = "Indeterminate", end_state_reason: Optional[str] = None) -> None:
"""
End the session with a specified state, rating, and reason.
Expand Down
Loading

0 comments on commit 592aac5

Please sign in to comment.