Skip to content

Commit

Permalink
api_v2
Browse files Browse the repository at this point in the history
  • Loading branch information
siyangqiu committed May 18, 2024
1 parent 38c666d commit 23eab50
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 84 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@
<a href="https://discord.gg/mKW3ZhN9p2">
<img src="https://img.shields.io/badge/chat-on%20Discord-blueviolet" alt="Discord community channel"/>
</a>
<a href="mailto:[email protected]">
<img src="https://img.shields.io/website?color=%23f26522&down_message=Y%20Combinator&label=Not%20Backed%20By&logo=ycombinator&style=flat-square&up_message=Y%20Combinator&url=https%3A%2F%2Fwww.ycombinator.com"/>
</a>
<a href="https://github.com/agentops-ai/agentops/issues">
<img src="https://img.shields.io/github/commit-activity/m/agentops-ai/agentops" alt="git commit activity"/>
</a>
Expand Down Expand Up @@ -122,6 +119,9 @@ pip install git+https://github.com/AgentOps-AI/crewAI.git@main

AgentOps works seamlessly with applications built using Langchain. To use the handler, install Langchain as an optional dependency:

<details>
<summary>Installation</summary>

```shell
pip install agentops[langchain]
```
Expand Down Expand Up @@ -151,13 +151,18 @@ agent = initialize_agent(tools,

Check out the [Langchain Examples Notebook](./examples/langchain_examples.ipynb) for more details including Async handlers.

### Cohere
</details>

### Cohere ⌨️

First class support for Cohere(>=5.4.0). This is a living integration, should you need any added functionality please message us on Discord!

- [AgentOps integration example](https://docs.agentops.ai/v1/integrations/cohere)
- [Official Cohere documentation](https://docs.cohere.com/reference/about)

<details>
<summary>Installation</summary>

```bash
pip install cohere
```
Expand Down Expand Up @@ -198,6 +203,8 @@ for event in stream:

agentops.end_session('Success')
```
</details>


### LlamaIndex 🦙

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
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
11 changes: 7 additions & 4 deletions agentops/meta_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __new__(cls, name, bases, dct):

return super().__new__(cls, name, bases, dct)

def send_exception_to_server(cls, exception, api_key):
def send_exception_to_server(cls, exception, api_key, session):
"""Class method to send exception to server."""
if api_key:
exception_type = type(exception).__name__
Expand All @@ -33,7 +33,10 @@ def send_exception_to_server(cls, exception, api_key):
"host_env": get_host_env()
}

HttpClient.post("https://api.agentops.ai/developer_errors",
if session:
developer_error["session_id"] = session.session_id

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

Expand All @@ -45,10 +48,10 @@ 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)
type(self).send_exception_to_server(e, self.config._api_key, self._session)
raise e

return wrapper
32 changes: 17 additions & 15 deletions agentops/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,10 +39,9 @@ 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>")
Expand All @@ -57,13 +56,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 +83,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 +96,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 +110,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
16 changes: 11 additions & 5 deletions tests/test_canary.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,35 @@
def mock_req():
with requests_mock.Mocker() as m:
url = 'https://api.agentops.ai'
m.post(url + '/events', text='ok')
m.post(url + '/sessions', json={'status': 'success', 'token_cost': 5})
m.post(url + '/v2/create_events', text='ok')
m.post(url + '/v2/create_session', json={'status': 'success',
'jwt': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'})
m.post(url + '/v2/update_session',
json={'status': 'success', 'token_cost': 5})
m.post(url + '/v2/developer_errors', text='ok')
yield m


class TestCanary:
def setup_method(self):
self.url = 'https://api.agentops.ai'
self.api_key = "random_api_key"
agentops.init(api_key=self.api_key, max_wait_time=5, auto_start_session=False)
agentops.init(api_key=self.api_key, max_wait_time=5,
auto_start_session=False)

def test_agent_ops_record(self, mock_req):
# Arrange
event_type = 'test_event_type'
agentops.start_session()

# Act
agentops.record(ActionEvent(event_type))
time.sleep(0.1)

# Assert
assert len(mock_req.request_history) == 2
request_json = mock_req.last_request.json()
assert mock_req.last_request.headers['X-Agentops-Auth'] == self.api_key
assert mock_req.last_request.headers['X-Agentops-Api-Key'] == self.api_key
assert request_json['events'][0]['event_type'] == event_type

agentops.end_session('Success')
Loading

0 comments on commit 23eab50

Please sign in to comment.