From 6059741226e832df7e884ff39b238fe8351b5e43 Mon Sep 17 00:00:00 2001 From: Howard Gil Date: Mon, 6 May 2024 17:36:02 -0700 Subject: [PATCH] Fix crash endtimestamp (#189) * Setting session to none if server does not return 200 for /sessions * Fixed crash on end_timestamp does not exist * notebook update * bump version number * end_timestamp optional * Making code more explicit --------- Co-authored-by: Shawn Qiu --- agentops/client.py | 37 +++++---- agentops/event.py | 9 +- examples/recording-events.ipynb | 143 +++++++++++++++++--------------- 3 files changed, 101 insertions(+), 88 deletions(-) diff --git a/agentops/client.py b/agentops/client.py index 7dc21559..8707aba4 100644 --- a/agentops/client.py +++ b/agentops/client.py @@ -77,7 +77,8 @@ def __init__(self, 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' + self._env_data_opt_out = os.getenv('AGENTOPS_ENV_DATA_OPT_OUT') and os.getenv( + 'AGENTOPS_ENV_DATA_OPT_OUT').lower() == 'true' try: self.config = Configuration(api_key=api_key, @@ -136,19 +137,24 @@ def record(self, event: Union[Event, ErrorEvent]): Args: event (Event): The event to record. """ - 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 and self._worker is not None: - if isinstance(event, ErrorEvent): - if event.trigger_event: - event.trigger_event_id = event.trigger_event.id - event.trigger_event_type = event.trigger_event.event_type - self._worker.add_event(event.trigger_event.__dict__) - event.trigger_event = None # removes trigger_event from serialization - self._worker.add_event(event.__dict__) - else: - logger.warning( - "🖇 AgentOps: Cannot record event - no current session") + if self._session is None or self._session.has_ended: + logger.warning("🖇 AgentOps: Cannot record event - no current session") + return + + if isinstance(event, Event): + if not event.end_timestamp or event.init_timestamp == event.end_timestamp: + event.end_timestamp = get_ISO_time() + elif isinstance(event, ErrorEvent): + if event.trigger_event: + if not event.trigger_event.end_timestamp or event.trigger_event.init_timestamp == event.trigger_event.end_timestamp: + event.trigger_event.end_timestamp = get_ISO_time() + + event.trigger_event_id = event.trigger_event.id + event.trigger_event_type = event.trigger_event.event_type + self._worker.add_event(event.trigger_event.__dict__) + event.trigger_event = None # removes trigger_event from serialization + + self._worker.add_event(event.__dict__) def _record_event_sync(self, func, event_name, *args, **kwargs): init_time = get_ISO_time() @@ -240,7 +246,8 @@ def start_session(self, tags: Optional[List[str]] = None, config: Optional[Confi if not config and not self.config: return logger.warning("🖇 AgentOps: Cannot start session - missing configuration") - self._session = Session(inherited_session_id or uuid4(), tags or self._tags_for_future_session, host_env=get_host_env(self._env_data_opt_out)) + self._session = Session(inherited_session_id or uuid4(), + tags or self._tags_for_future_session, host_env=get_host_env(self._env_data_opt_out)) self._worker = Worker(config or self.config) start_session_result = self._worker.start_session(self._session) if not start_session_result: diff --git a/agentops/event.py b/agentops/event.py index 5773bf17..b7efbc75 100644 --- a/agentops/event.py +++ b/agentops/event.py @@ -38,13 +38,12 @@ class Event: """ event_type: str # EventType.ENUM.value - 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) + params: Optional[dict] = None + returns: Optional[str] = None + init_timestamp: str = field(default_factory=get_ISO_time) + end_timestamp: Optional[str] = None agent_id: Optional[UUID] = field(default_factory=check_call_stack_for_agent_id) id: UUID = field(default_factory=uuid4) - # TODO: has_been_recorded: bool = False @dataclass diff --git a/examples/recording-events.ipynb b/examples/recording-events.ipynb index 0f224f8a..9c766b58 100644 --- a/examples/recording-events.ipynb +++ b/examples/recording-events.ipynb @@ -2,44 +2,50 @@ "cells": [ { "cell_type": "markdown", - "source": [ - "# Recording Events\n", - "AgentOps has a number of different [Event Types](https://docs.agentops.ai/v1/details/events)" - ], + "id": "dc8cfd2cfa8a594b", "metadata": { "collapsed": false }, - "id": "dc8cfd2cfa8a594b" + "source": [ + "# Recording Events\n", + "AgentOps has a number of different [Event Types](https://docs.agentops.ai/v1/details/events)" + ] }, { "cell_type": "code", + "execution_count": null, + "id": "168ecd05cc123de0", + "metadata": { + "collapsed": false, + "is_executing": true + }, "outputs": [], "source": [ + "import agentops\n", "# Create new session\n", - "agentops.start_session()\n", + "agentops.init()\n", "\n", "# Optionally, we can add tags to the session\n", - "# agentops.start_session(['Hello Tracker'])" - ], - "metadata": { - "collapsed": false, - "is_executing": true - }, - "id": "168ecd05cc123de0", - "execution_count": null + "# agentops.init(tags=['Hello Tracker'])" + ] }, { "cell_type": "markdown", - "source": [ - "The easiest way to record actions is through the use of AgentOp's decorators" - ], + "id": "c6d06ee8c66dad17", "metadata": { "collapsed": false }, - "id": "c6d06ee8c66dad17" + "source": [ + "The easiest way to record actions is through the use of AgentOp's decorators" + ] }, { "cell_type": "code", + "execution_count": null, + "id": "b460318317adc624", + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "from agentops import record_function\n", @@ -49,25 +55,25 @@ " return x + y\n", "\n", "add(2,4)" - ], - "metadata": { - "collapsed": false - }, - "id": "b460318317adc624", - "execution_count": 0 + ] }, { "cell_type": "markdown", - "source": [ - "We can also manually craft an event exactly the way we want" - ], + "id": "9068a4cdd328f652", "metadata": { "collapsed": false }, - "id": "9068a4cdd328f652" + "source": [ + "We can also manually craft an event exactly the way we want" + ] }, { "cell_type": "code", + "execution_count": null, + "id": "b62ad88921ff26f2", + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "from agentops import ActionEvent\n", @@ -78,38 +84,38 @@ "\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) ))" - ], - "metadata": { - "collapsed": false - }, - "id": "b62ad88921ff26f2", - "execution_count": 0 + ] }, { "cell_type": "code", - "outputs": [], - "source": [ - "agentops.end_session('Success')" - ], + "execution_count": null, + "id": "e10a89e06fe6b2be", "metadata": { "collapsed": false }, - "id": "e10a89e06fe6b2be", - "execution_count": null + "outputs": [], + "source": [ + "agentops.end_session('Success')" + ] }, { "cell_type": "markdown", - "source": [ - "## Tool Event\n", - "Agents use tools. These tools are useful to track with information such as name, end status, runtime, etc. To record tool usage, you can create and record a `ToolEvent` similar to above." - ], + "id": "f7c947d815f581e7", "metadata": { "collapsed": false }, - "id": "f7c947d815f581e7" + "source": [ + "## Tool Event\n", + "Agents use tools. These tools are useful to track with information such as name, end status, runtime, etc. To record tool usage, you can create and record a `ToolEvent` similar to above." + ] }, { "cell_type": "code", + "execution_count": null, + "id": "5d387a071a1c70cf", + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "from agentops import ToolEvent, record\n", @@ -118,41 +124,42 @@ " result = integration.scrape_website(data) # perform tool logic\n", " tool_event.returns = result\n", " record(tool_event)" - ], - "metadata": { - "collapsed": false - }, - "id": "5d387a071a1c70cf" + ] }, { "cell_type": "markdown", - "source": [ - "## Error Events\n", - "Error events can be used alone or in reference to another event. Lets add a catch block to the code above" - ], + "id": "968d1503dd0aae9a", "metadata": { "collapsed": false }, - "id": "968d1503dd0aae9a" + "source": [ + "## Error Events\n", + "Error events can be used alone or in reference to another event. Lets add a catch block to the code above" + ] }, { "cell_type": "code", + "execution_count": null, + "id": "eb23c1325298e22f", + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "from agentops import ToolEvent, record, ErrorEvent\n", + "\n", "def scrape_website(url: str):\n", " tool_event = ToolEvent(name='scrape_website', params={'url':url}) # the start timestamp is set when the obj is created\n", + "\n", " try:\n", - " result = integration.scrape_website(data) # perform tool logic\n", - " tool_event.returns = result\n", - " except Error as e:\n", - " record(ErrorEvent(message=e, trigger_event=tool_event))\n", - " record(tool_event)" - ], - "metadata": { - "collapsed": false - }, - "id": "eb23c1325298e22f" + " 1 / 0 # Ooops! Something went wrong\n", + " except Exception as e:\n", + " record(ErrorEvent(exception=e, trigger_event=tool_event))\n", + "\n", + "scrape_website('https://app.agentops.ai') \n", + "\n", + "agentops.end_session('Success')" + ] } ], "metadata": { @@ -164,14 +171,14 @@ "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" + "pygments_lexer": "ipython3", + "version": "3.12.3" } }, "nbformat": 4,