Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: switch to agentops api v2 #209

Merged
merged 16 commits into from
May 24, 2024
12 changes: 10 additions & 2 deletions agentops/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# agentops/__init__.py
import os
import logging
siyangqiu marked this conversation as resolved.
Show resolved Hide resolved
from typing import Optional, List, Union
from .client import Client
from .config import Configuration
from .event import Event, ActionEvent, LLMEvent, ToolEvent, ErrorEvent
from .enums import Models
from .decorators import record_function
from .agent import track_agent
from .log_config import set_logging_level_info, set_logging_level_critial
from .log_config import set_logging_level
try:
from .langchain_callback_handler import LangchainCallbackHandler, AsyncLangchainCallbackHandler
except ModuleNotFoundError:
Expand Down Expand Up @@ -46,7 +48,13 @@ def init(api_key: Optional[str] = None,
inherited_session_id (optional, str): Init Agentops with an existing Session
Attributes:
"""
set_logging_level_info()
if os.getenv('AGENTOPS_LOGGING_LEVEL') == 'DEBUG':
siyangqiu marked this conversation as resolved.
Show resolved Hide resolved
set_logging_level(logging.DEBUG)
elif os.getenv('AGENTOPS_LOGGING_LEVEL') == 'CRITICAL':
set_logging_level(logging.CRITICAL)
else:
set_logging_level(logging.INFO)

c = Client(api_key=api_key,
parent_key=parent_key,
endpoint=endpoint,
Expand Down
10 changes: 8 additions & 2 deletions agentops/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from uuid import uuid4
from typing import Optional, List, Union
import traceback
from .log_config import logger, set_logging_level_info
import logging
from .log_config import logger, set_logging_level
from decimal import Decimal
import inspect
import atexit
Expand Down Expand Up @@ -274,7 +275,12 @@ def start_session(self, tags: Optional[List[str]] = None, config: Optional[Confi
config: (Configuration, optional): Client configuration object
inherited_session_id (optional, str): assign session id to match existing Session
"""
set_logging_level_info()
if os.getenv('AGENTOPS_LOGGING_LEVEL') == 'DEBUG':
set_logging_level(logging.DEBUG)
elif os.getenv('AGENTOPS_LOGGING_LEVEL') == 'CRITICAL':
set_logging_level(logging.CRITICAL)
else:
set_logging_level(logging.INFO)

if self._session is not None:
return logger.warning("🖇 AgentOps: Cannot start session - session already started")
Expand Down
12 changes: 7 additions & 5 deletions agentops/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Configuration:
be read from the AGENTOPS_PARENT_KEY environment variable.
endpoint (str, optional): The endpoint for the AgentOps service. If none is provided, key will
be read from the AGENTOPS_API_ENDPOINT environment variable. Defaults to 'https://api.agentops.ai'.
max_wait_time (int, optional): The maximum time to wait in milliseconds before flushing the queue. Defaults to 30000.
max_wait_time (int, optional): The maximum time to wait in milliseconds before flushing the queue. Defaults to 5000.
max_queue_size (int, optional): The maximum size of the event queue. Defaults to 100.
"""

Expand All @@ -35,17 +35,19 @@ def __init__(self,
if not api_key:
api_key = environ.get('AGENTOPS_API_KEY', None)
if not api_key:
raise ConfigurationError("🖇 AgentOps: No API key provided - no data will be recorded.")
raise ConfigurationError(
"No API key provided - no data will be recorded.")

if not parent_key:
parent_key = environ.get('AGENTOPS_PARENT_KEY', None)

if not endpoint:
endpoint = environ.get('AGENTOPS_API_ENDPOINT', 'https://api.agentops.ai')
endpoint = environ.get(
'AGENTOPS_API_ENDPOINT', 'https://api.agentops.ai')

self._api_key: str = api_key
self._endpoint = endpoint
self._max_wait_time = max_wait_time or 30000
self._max_wait_time = max_wait_time or 5000
self._max_queue_size = max_queue_size or 100
self._parent_key: Optional[str] = parent_key

Expand Down Expand Up @@ -77,7 +79,7 @@ def endpoint(self) -> str:
Returns:
str: The endpoint for the AgentOps service.
"""
return self._endpoint
return self._endpoint # type: ignore

@endpoint.setter
def endpoint(self, value: str):
Expand Down
11 changes: 5 additions & 6 deletions agentops/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,13 @@ def get_agentops_version():
def debug_print_function_params(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
if os.getenv('DEBUG_MODE') == 'Y':
print("\n<AGENTOPS_DEBUG_OUTPUT>")
print(f"{func.__name__} called with arguments:")
logger.debug("\n<AGENTOPS_DEBUG_OUTPUT>")
logger.debug(f"{func.__name__} called with arguments:")

for key, value in kwargs.items():
print(f"{key}: {pformat(value)}")
for key, value in kwargs.items():
logger.debug(f"{key}: {pformat(value)}")

print("</AGENTOPS_DEBUG_OUTPUT>\n")
logger.debug("</AGENTOPS_DEBUG_OUTPUT>\n")

return func(self, *args, **kwargs)
return wrapper
Expand Down
9 changes: 6 additions & 3 deletions agentops/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,22 @@ def get_status(code: int) -> HttpStatus:
class HttpClient:

@staticmethod
def post(url: str, payload: bytes, api_key: Optional[str] = None, parent_key: Optional[str] = None,
header=None) -> Response:
def post(url: str, payload: bytes, api_key: Optional[str] = None, parent_key: Optional[str] = None,
jwt_token: Optional[str] = None, header=None) -> Response:
result = Response()
try:
# Create request session with retries configured
request_session = requests.Session()
request_session.mount(url, HTTPAdapter(max_retries=retry_config))

if api_key is not None:
JSON_HEADER["X-Agentops-Auth"] = api_key
JSON_HEADER["X-Agentops-Api-Key"] = api_key

if parent_key is not None:
JSON_HEADER["X-Agentops-Parent-Key"] = parent_key

if jwt_token is not None:
JSON_HEADER["Authorization"] = f"Bearer {jwt_token}"

res = request_session.post(url, data=payload,
headers=JSON_HEADER, timeout=20)
Expand Down
8 changes: 7 additions & 1 deletion agentops/log_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
logger = logging.getLogger("agentops")
logger.setLevel(logging.CRITICAL)

def set_logging_level(level):
logger.setLevel(level)

def set_logging_level_critial():
logger.setLevel(logging.CRITICAL)

def set_logging_level_info():
logger.setLevel(logging.INFO)
logger.setLevel(logging.INFO)

def set_logging_level_debug():
logger.setLevel(logging.DEBUG)
4 changes: 2 additions & 2 deletions agentops/meta_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def send_exception_to_server(cls, exception, api_key, session):
if session:
developer_error["session_id"] = session.session_id

HttpClient.post("https://api.agentops.ai/developer_errors",
HttpClient.post("https://api.agentops.ai/v2/developer_errors",
safe_serialize(developer_error).encode("utf-8"),
api_key=api_key)

Expand All @@ -48,7 +48,7 @@ def wrapper(self, *args, **kwargs):
try:
return method(self, *args, **kwargs)
except Exception as e:
logger.warning(f"🖇 AgentOps: Error: {e}")
logger.warning(f"Error: {e}")
config = getattr(self, 'config', None)
if config is not None:
type(self).send_exception_to_server(e, self.config._api_key, self._session)
Expand Down
43 changes: 22 additions & 21 deletions agentops/worker.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import json
from .log_config import logger
import threading
import time
from .http_client import HttpClient
from .config import Configuration
from .session import Session
from .helpers import safe_serialize, filter_unjsonable
from typing import Dict, Optional
import os


class Worker:
Expand All @@ -19,7 +19,7 @@ def __init__(self, config: Configuration) -> None:
self.thread.daemon = True
self.thread.start()
self._session: Optional[Session] = None
self._debug_mode = os.getenv('DEBUG_MODE') == 'Y'
self.jwt = None

def add_event(self, event: dict) -> None:
with self.lock:
Expand All @@ -39,16 +39,14 @@ def flush_queue(self) -> None:
}

serialized_payload = safe_serialize(payload).encode("utf-8")
HttpClient.post(f'{self.config.endpoint}/events',
HttpClient.post(f'{self.config.endpoint}/v2/create_events',
serialized_payload,
self.config.api_key,
self.config.parent_key)
jwt_token=self.jwt)

if self._debug_mode:
print("\n<AGENTOPS_DEBUG_OUTPUT>")
print(f"Worker request to {self.config.endpoint}/events")
print(serialized_payload)
print("</AGENTOPS_DEBUG_OUTPUT>\n")
logger.debug("\n<AGENTOPS_DEBUG_OUTPUT>")
logger.debug(f"Worker request to {self.config.endpoint}/events")
logger.debug(serialized_payload)
logger.debug("</AGENTOPS_DEBUG_OUTPUT>\n")

def start_session(self, session: Session) -> bool:
self._session = session
Expand All @@ -57,13 +55,19 @@ def start_session(self, session: Session) -> bool:
"session": session.__dict__
}
serialized_payload = json.dumps(filter_unjsonable(payload)).encode("utf-8")
res = HttpClient.post(f'{self.config.endpoint}/sessions',
res = HttpClient.post(f'{self.config.endpoint}/v2/create_session',
serialized_payload,
self.config.api_key,
self.config.parent_key)

logger.debug(res.body)

if res.code != 200:
return False

self.jwt = res.body.get('jwt', None)
if self.jwt is None:
return False

return True

Expand All @@ -78,12 +82,11 @@ def end_session(self, session: Session) -> str:
"session": session.__dict__
}

res = HttpClient.post(f'{self.config.endpoint}/sessions',
res = HttpClient.post(f'{self.config.endpoint}/v2/update_session',
json.dumps(filter_unjsonable(
payload)).encode("utf-8"),
self.config.api_key,
self.config.parent_key)

jwt_token=self.jwt)
logger.debug(res.body)
return res.body.get('token_cost', "unknown")

def update_session(self, session: Session) -> None:
Expand All @@ -92,11 +95,10 @@ def update_session(self, session: Session) -> None:
"session": session.__dict__
}

HttpClient.post(f'{self.config.endpoint}/sessions',
res = HttpClient.post(f'{self.config.endpoint}/v2/update_session',
json.dumps(filter_unjsonable(
payload)).encode("utf-8"),
self.config.api_key,
self.config.parent_key)
jwt_token=self.jwt)

def create_agent(self, agent_id, name):
payload = {
Expand All @@ -107,10 +109,9 @@ def create_agent(self, agent_id, name):

serialized_payload = \
safe_serialize(payload).encode("utf-8")
HttpClient.post(f'{self.config.endpoint}/agents',
HttpClient.post(f'{self.config.endpoint}/v2/create_agent',
serialized_payload,
self.config.api_key,
self.config.parent_key)
jwt_token=self.jwt)

def run(self) -> None:
while not self.stop_flag.is_set():
Expand Down
45 changes: 23 additions & 22 deletions examples/recording-events.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
},
"source": [
"# Recording Events\n",
"AgentOps has a number of different [Event Types](https://docs.agentops.ai/v1/details/events)"
"AgentOps has a number of different [Event Types](https://docs.agentops.ai/v1/details/events)\n",
"\n",
"AgentOps automatically instruments your LLM Calls from OpenAI, LiteLLM, and Cohere. Just make sure their SDKs are imported before initializing AgentOps like we see below"
]
},
{
Expand All @@ -22,11 +24,25 @@
"outputs": [],
"source": [
"import agentops\n",
"import openai\n",
"\n",
"# Create new session\n",
"agentops.init()\n",
"\n",
"# Optionally, we can add tags to the session\n",
"# agentops.init(tags=['Hello Tracker'])"
"# agentops.init(tags=['Hello Tracker'])\n",
"\n",
"message = {\"role\": \"user\", \"content\": \"Hello\"},\n",
"response = openai.chat.completions.create(\n",
" model='gpt-3.5-turbo', messages=message, temperature=0.5)"
]
},
{
"cell_type": "markdown",
"id": "95a6047d",
"metadata": {},
"source": [
"Click the AgentOps link above to see your session!"
]
},
{
Expand All @@ -36,7 +52,9 @@
"collapsed": false
},
"source": [
"The easiest way to record actions is through the use of AgentOp's decorators"
"## Action Event\n",
"\n",
"AgentOps allows you to record other actions. The easiest way to record actions is through the use of AgentOp's decorators"
]
},
{
Expand Down Expand Up @@ -64,7 +82,7 @@
"collapsed": false
},
"source": [
"We can also manually craft an event exactly the way we want"
"We can also manually craft an event exactly the way we want by creating and recording an `ActionEvent`"
]
},
{
Expand All @@ -78,24 +96,7 @@
"source": [
"from agentops import ActionEvent\n",
"\n",
"message = {\"role\": \"user\", \"content\": \"Hello\"},\n",
"response = openai.chat.completions.create(\n",
" model='gpt-3.5-turbo', messages=message, temperature=0.5)\n",
"\n",
"if \"hello\" in str(response.choices[0].message.content).lower():\n",
" agentops.record(ActionEvent(action_type=\"Agent says hello\", params=str(message), returns=str(response.choices[0].message.content) ))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e10a89e06fe6b2be",
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"agentops.end_session('Success')"
"agentops.record(ActionEvent(action_type=\"Agent says hello\", params={\"message\": \"Hi\"}, returns=\"Hi Back!\" ))"
]
},
{
Expand Down
Loading
Loading