diff --git a/agentops/cli.py b/agentops/cli.py index 562c8053..29a81123 100644 --- a/agentops/cli.py +++ b/agentops/cli.py @@ -6,9 +6,7 @@ def main(): parser = argparse.ArgumentParser(description="AgentOps CLI") subparsers = parser.add_subparsers(dest="command") - timetravel_parser = subparsers.add_parser( - "timetravel", help="Time Travel Debugging commands", aliases=["tt"] - ) + timetravel_parser = subparsers.add_parser("timetravel", help="Time Travel Debugging commands", aliases=["tt"]) timetravel_parser.add_argument( "branch_name", type=str, diff --git a/agentops/client.py b/agentops/client.py index 80cb1cc5..86fe49b8 100644 --- a/agentops/client.py +++ b/agentops/client.py @@ -44,10 +44,7 @@ def __init__(self): api_key=os.environ.get("AGENTOPS_API_KEY"), parent_key=os.environ.get("AGENTOPS_PARENT_KEY"), endpoint=os.environ.get("AGENTOPS_API_ENDPOINT"), - env_data_opt_out=os.environ.get( - "AGENTOPS_ENV_DATA_OPT_OUT", "False" - ).lower() - == "true", + env_data_opt_out=os.environ.get("AGENTOPS_ENV_DATA_OPT_OUT", "False").lower() == "true", ) def configure( @@ -106,9 +103,7 @@ def initialize(self) -> Union[Session, None]: if session: for agent_args in self._pre_init_queue["agents"]: - session.create_agent( - name=agent_args["name"], agent_id=agent_args["agent_id"] - ) + session.create_agent(name=agent_args["name"], agent_id=agent_args["agent_id"]) self._pre_init_queue["agents"] = [] return session @@ -141,9 +136,7 @@ def add_tags(self, tags: List[str]) -> None: session = self._safe_get_session() if session is None: - return logger.warning( - "Could not add tags. Start a session by calling agentops.start_session()." - ) + return logger.warning("Could not add tags. Start a session by calling agentops.start_session().") session.add_tags(tags=tags) @@ -162,9 +155,7 @@ def set_tags(self, tags: List[str]) -> None: session = self._safe_get_session() if session is None: - return logger.warning( - "Could not set tags. Start a session by calling agentops.start_session()." - ) + return logger.warning("Could not set tags. Start a session by calling agentops.start_session().") else: session.set_tags(tags=tags) @@ -198,9 +189,7 @@ def record(self, event: Union[Event, ErrorEvent]) -> None: session = self._safe_get_session() if session is None: - return logger.error( - "Could not record event. Start a session by calling agentops.start_session()." - ) + return logger.error("Could not record event. Start a session by calling agentops.start_session().") session.record(event) def start_session( @@ -244,9 +233,7 @@ def start_session( if self._pre_init_queue["agents"] and len(self._pre_init_queue["agents"]) > 0: for agent_args in self._pre_init_queue["agents"]: - session.create_agent( - name=agent_args["name"], agent_id=agent_args["agent_id"] - ) + session.create_agent(name=agent_args["name"], agent_id=agent_args["agent_id"]) self._pre_init_queue["agents"] = [] self._sessions.append(session) @@ -277,9 +264,7 @@ def end_session( if is_auto_end and self._config.skip_auto_end_session: return - token_cost = session.end_session( - end_state=end_state, end_state_reason=end_state_reason, video=video - ) + token_cost = session.end_session(end_state=end_state, end_state_reason=end_state_reason, video=video) return token_cost @@ -299,9 +284,7 @@ def create_agent( # if no session passed, assume single session session = self._safe_get_session() if session is None: - self._pre_init_queue["agents"].append( - {"name": name, "agent_id": agent_id} - ) + self._pre_init_queue["agents"].append({"name": name, "agent_id": agent_id}) else: session.create_agent(name=name, agent_id=agent_id) @@ -326,9 +309,7 @@ def signal_handler(signum, frame): """ signal_name = "SIGINT" if signum == signal.SIGINT else "SIGTERM" logger.info("%s detected. Ending session...", signal_name) - self.end_session( - end_state="Fail", end_state_reason=f"Signal {signal_name} detected" - ) + self.end_session(end_state="Fail", end_state_reason=f"Signal {signal_name} detected") sys.exit(0) def handle_exception(exc_type, exc_value, exc_traceback): @@ -341,9 +322,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): exc_traceback (TracebackType): A traceback object encapsulating the call stack at the point where the exception originally occurred. """ - formatted_traceback = "".join( - traceback.format_exception(exc_type, exc_value, exc_traceback) - ) + formatted_traceback = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback)) for session in self._sessions: session.end_session( @@ -376,13 +355,7 @@ def add_pre_init_warning(self, message: str): # replaces the session currently stored with a specific session_id, with a new session def _update_session(self, session: Session): self._sessions[ - self._sessions.index( - [ - sess - for sess in self._sessions - if sess.session_id == session.session_id - ][0] - ) + self._sessions.index([sess for sess in self._sessions if sess.session_id == session.session_id][0]) ] = session def _safe_get_session(self) -> Optional[Session]: @@ -392,9 +365,7 @@ def _safe_get_session(self) -> Optional[Session]: return self._sessions[0] if len(self._sessions) > 1: - calling_function = inspect.stack()[ - 2 - ].function # Using index 2 because we have a wrapper at index 1 + calling_function = inspect.stack()[2].function # Using index 2 because we have a wrapper at index 1 return logger.warning( f"Multiple sessions detected. You must use session.{calling_function}(). More info: https://docs.agentops.ai/v1/concepts/core-concepts#session-management" ) diff --git a/agentops/decorators.py b/agentops/decorators.py index fa6a13fa..62266b9e 100644 --- a/agentops/decorators.py +++ b/agentops/decorators.py @@ -46,9 +46,7 @@ async def async_wrapper(*args, session: Optional[Session] = None, **kwargs): arg_names = list(func_args.keys()) # Get default values arg_values = { - name: func_args[name].default - for name in arg_names - if func_args[name].default is not inspect._empty + name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty } # Update with positional arguments arg_values.update(dict(zip(arg_names, args))) @@ -112,9 +110,7 @@ def sync_wrapper(*args, session: Optional[Session] = None, **kwargs): arg_names = list(func_args.keys()) # Get default values arg_values = { - name: func_args[name].default - for name in arg_names - if func_args[name].default is not inspect._empty + name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty } # Update with positional arguments arg_values.update(dict(zip(arg_names, args))) @@ -192,9 +188,7 @@ async def async_wrapper(*args, session: Optional[Session] = None, **kwargs): arg_names = list(func_args.keys()) # Get default values arg_values = { - name: func_args[name].default - for name in arg_names - if func_args[name].default is not inspect._empty + name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty } # Update with positional arguments arg_values.update(dict(zip(arg_names, args))) @@ -258,9 +252,7 @@ def sync_wrapper(*args, session: Optional[Session] = None, **kwargs): arg_names = list(func_args.keys()) # Get default values arg_values = { - name: func_args[name].default - for name in arg_names - if func_args[name].default is not inspect._empty + name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty } # Update with positional arguments arg_values.update(dict(zip(arg_names, args))) @@ -353,21 +345,15 @@ def new_init(self, *args, **kwargs): except AttributeError as ex: logger.debug(ex) - Client().add_pre_init_warning( - f"Failed to track an agent {name} with the @track_agent decorator." - ) - logger.warning( - "Failed to track an agent with the @track_agent decorator." - ) + Client().add_pre_init_warning(f"Failed to track an agent {name} with the @track_agent decorator.") + logger.warning("Failed to track an agent with the @track_agent decorator.") obj.__init__ = new_init elif inspect.isfunction(obj): obj.agentops_agent_id = str(uuid4()) obj.agentops_agent_name = name - Client().create_agent( - name=obj.agentops_agent_name, agent_id=obj.agentops_agent_id - ) + Client().create_agent(name=obj.agentops_agent_name, agent_id=obj.agentops_agent_id) else: raise Exception("Invalid input, 'obj' must be a class or a function") diff --git a/agentops/descriptor.py b/agentops/descriptor.py index d5b11415..020804cb 100644 --- a/agentops/descriptor.py +++ b/agentops/descriptor.py @@ -169,9 +169,7 @@ def stack_lookup() -> Union[UUID, None]: var_type = type(var) # Get all class attributes - class_attrs = { - name: getattr(var_type, name, None) for name in dir(var_type) - } + class_attrs = {name: getattr(var_type, name, None) for name in dir(var_type)} agent_id_desc = class_attrs.get("agentops_agent_id") diff --git a/agentops/helpers.py b/agentops/helpers.py index 03322186..ca0c4f0e 100644 --- a/agentops/helpers.py +++ b/agentops/helpers.py @@ -39,7 +39,9 @@ def filter_dict(obj): k: ( filter_dict(v) if isinstance(v, (dict, list)) or is_jsonable(v) - else str(v) if isinstance(v, UUID) else "" + else str(v) + if isinstance(v, UUID) + else "" ) for k, v in obj.items() } @@ -48,7 +50,9 @@ def filter_dict(obj): ( filter_dict(x) if isinstance(x, (dict, list)) or is_jsonable(x) - else str(x) if isinstance(x, UUID) else "" + else str(x) + if isinstance(x, UUID) + else "" ) for x in obj ] @@ -86,9 +90,7 @@ def remove_unwanted_items(value): """Recursively remove self key and None/... values from dictionaries so they aren't serialized""" if isinstance(value, dict): return { - k: remove_unwanted_items(v) - for k, v in value.items() - if v is not None and v is not ... and k != "self" + k: remove_unwanted_items(v) for k, v in value.items() if v is not None and v is not ... and k != "self" } elif isinstance(value, list): return [remove_unwanted_items(item) for item in value] diff --git a/agentops/host_env.py b/agentops/host_env.py index e466c86e..5307dec4 100644 --- a/agentops/host_env.py +++ b/agentops/host_env.py @@ -51,8 +51,7 @@ def get_installed_packages(): return { # TODO: add to opt out "Installed Packages": { - dist.metadata.get("Name"): dist.metadata.get("Version") - for dist in importlib.metadata.distributions() + dist.metadata.get("Name"): dist.metadata.get("Version") for dist in importlib.metadata.distributions() } } except: @@ -125,9 +124,7 @@ def get_disk_details(): } except OSError as inaccessible: # Skip inaccessible partitions, such as removable drives with no media - logger.debug( - "Mountpoint %s inaccessible: %s", partition.mountpoint, inaccessible - ) + logger.debug("Mountpoint %s inaccessible: %s", partition.mountpoint, inaccessible) return disk_info diff --git a/agentops/http_client.py b/agentops/http_client.py index 96e72e02..caa18b27 100644 --- a/agentops/http_client.py +++ b/agentops/http_client.py @@ -22,9 +22,7 @@ class HttpStatus(Enum): class Response: - def __init__( - self, status: HttpStatus = HttpStatus.UNKNOWN, body: Optional[dict] = None - ): + def __init__(self, status: HttpStatus = HttpStatus.UNKNOWN, body: Optional[dict] = None): self.status: HttpStatus = status self.code: int = status.value self.body = body if body else {} @@ -80,17 +78,13 @@ def post( if jwt is not None: JSON_HEADER["Authorization"] = f"Bearer {jwt}" - res = request_session.post( - url, data=payload, headers=JSON_HEADER, timeout=20 - ) + res = request_session.post(url, data=payload, headers=JSON_HEADER, timeout=20) result.parse(res) except requests.exceptions.Timeout: result.code = 408 result.status = HttpStatus.TIMEOUT - raise ApiServerException( - "Could not reach API server - connection timed out" - ) + raise ApiServerException("Could not reach API server - connection timed out") except requests.exceptions.HTTPError as e: try: result.parse(e.response) @@ -143,9 +137,7 @@ def get( except requests.exceptions.Timeout: result.code = 408 result.status = HttpStatus.TIMEOUT - raise ApiServerException( - "Could not reach API server - connection timed out" - ) + raise ApiServerException("Could not reach API server - connection timed out") except requests.exceptions.HTTPError as e: try: result.parse(e.response) diff --git a/agentops/llms/__init__.py b/agentops/llms/__init__.py index dd60c3b8..a5852d8c 100644 --- a/agentops/llms/__init__.py +++ b/agentops/llms/__init__.py @@ -66,17 +66,13 @@ def override_api(self): if api == "litellm": module_version = version(api) if module_version is None: - logger.warning( - "Cannot determine LiteLLM version. Only LiteLLM>=1.3.1 supported." - ) + logger.warning("Cannot determine LiteLLM version. Only LiteLLM>=1.3.1 supported.") if Version(module_version) >= parse("1.3.1"): provider = LiteLLMProvider(self.client) provider.override() else: - logger.warning( - f"Only LiteLLM>=1.3.1 supported. v{module_version} found." - ) + logger.warning(f"Only LiteLLM>=1.3.1 supported. v{module_version} found.") return # If using an abstraction like litellm, do not patch the underlying LLM APIs if api == "openai": @@ -96,17 +92,13 @@ def override_api(self): # Patch cohere v5.4.0+ methods module_version = version(api) if module_version is None: - logger.warning( - "Cannot determine Cohere version. Only Cohere>=5.4.0 supported." - ) + logger.warning("Cannot determine Cohere version. Only Cohere>=5.4.0 supported.") if Version(module_version) >= parse("5.4.0"): provider = CohereProvider(self.client) provider.override() else: - logger.warning( - f"Only Cohere>=5.4.0 supported. v{module_version} found." - ) + logger.warning(f"Only Cohere>=5.4.0 supported. v{module_version} found.") if api == "ollama": module_version = version(api) @@ -115,9 +107,7 @@ def override_api(self): provider = OllamaProvider(self.client) provider.override() else: - logger.warning( - f"Only Ollama>=0.0.1 supported. v{module_version} found." - ) + logger.warning(f"Only Ollama>=0.0.1 supported. v{module_version} found.") if api == "groq": module_version = version(api) @@ -126,25 +116,19 @@ def override_api(self): provider = GroqProvider(self.client) provider.override() else: - logger.warning( - f"Only Groq>=0.9.0 supported. v{module_version} found." - ) + logger.warning(f"Only Groq>=0.9.0 supported. v{module_version} found.") if api == "anthropic": module_version = version(api) if module_version is None: - logger.warning( - "Cannot determine Anthropic version. Only Anthropic>=0.32.0 supported." - ) + logger.warning("Cannot determine Anthropic version. Only Anthropic>=0.32.0 supported.") if Version(module_version) >= parse("0.32.0"): provider = AnthropicProvider(self.client) provider.override() else: - logger.warning( - f"Only Anthropic>=0.32.0 supported. v{module_version} found." - ) + logger.warning(f"Only Anthropic>=0.32.0 supported. v{module_version} found.") if api == "mistralai": module_version = version(api) @@ -153,25 +137,19 @@ def override_api(self): provider = MistralProvider(self.client) provider.override() else: - logger.warning( - f"Only MistralAI>=1.0.1 supported. v{module_version} found." - ) + logger.warning(f"Only MistralAI>=1.0.1 supported. v{module_version} found.") if api == "ai21": module_version = version(api) if module_version is None: - logger.warning( - "Cannot determine AI21 version. Only AI21>=2.0.0 supported." - ) + logger.warning("Cannot determine AI21 version. Only AI21>=2.0.0 supported.") if Version(module_version) >= parse("2.0.0"): provider = AI21Provider(self.client) provider.override() else: - logger.warning( - f"Only AI21>=2.0.0 supported. v{module_version} found." - ) + logger.warning(f"Only AI21>=2.0.0 supported. v{module_version} found.") def stop_instrumenting(self): OpenAiProvider(self.client).undo_override() diff --git a/agentops/llms/ai21.py b/agentops/llms/ai21.py index d3290edb..4e080a5b 100644 --- a/agentops/llms/ai21.py +++ b/agentops/llms/ai21.py @@ -23,9 +23,7 @@ def __init__(self, client): super().__init__(client) self._provider_name = "AI21" - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ): + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None): """Handle responses for AI21""" from ai21.stream.stream import Stream from ai21.stream.async_stream import AsyncStream @@ -50,9 +48,7 @@ def handle_stream_chunk(chunk: ChatCompletionChunk): accumulated_delta = llm_event.returns.choices[0].delta llm_event.agent_id = check_call_stack_for_agent_id() llm_event.model = kwargs["model"] - llm_event.prompt = [ - message.model_dump() for message in kwargs["messages"] - ] + llm_event.prompt = [message.model_dump() for message in kwargs["messages"]] # NOTE: We assume for completion only choices[0] is relevant choice = chunk.choices[0] @@ -79,9 +75,7 @@ def handle_stream_chunk(chunk: ChatCompletionChunk): self._safe_record(session, llm_event) except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) @@ -118,9 +112,7 @@ async def async_generator(): llm_event.returns = response llm_event.agent_id = check_call_stack_for_agent_id() llm_event.model = kwargs["model"] - llm_event.prompt = [ - message.model_dump() for message in kwargs["messages"] - ] + llm_event.prompt = [message.model_dump() for message in kwargs["messages"]] llm_event.prompt_tokens = response.usage.prompt_tokens llm_event.completion = response.choices[0].message.model_dump() llm_event.completion_tokens = response.usage.completion_tokens diff --git a/agentops/llms/anthropic.py b/agentops/llms/anthropic.py index cc6f55f8..0ec1c184 100644 --- a/agentops/llms/anthropic.py +++ b/agentops/llms/anthropic.py @@ -23,9 +23,7 @@ def __init__(self, client): self.tool_event = {} self.tool_id = "" - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ): + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None): """Handle responses for Anthropic""" import anthropic.resources.beta.messages.messages as beta_messages from anthropic import AsyncStream, Stream @@ -66,9 +64,7 @@ def handle_stream_chunk(chunk: Message): llm_event.completion["content"] += chunk.delta.text elif chunk.delta.type == "input_json_delta": - self.tool_event[self.tool_id].logs[ - "input" - ] += chunk.delta.partial_json + self.tool_event[self.tool_id].logs["input"] += chunk.delta.partial_json elif chunk.type == "content_block_stop": pass @@ -81,9 +77,7 @@ def handle_stream_chunk(chunk: Message): self._safe_record(session, llm_event) except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) @@ -143,7 +137,7 @@ async def async_generator(): The raw response has the following structure: { - 'id': str, # Message ID (e.g. 'msg_018Gk9N2pcWaYLS7mxXbPD5i') + 'id': str, # Message ID (e.g. 'msg_018Gk9N2pcWaYLS7mxXbPD5i') 'type': str, # Type of response (e.g. 'message') 'role': str, # Role of responder (e.g. 'assistant') 'model': str, # Model used (e.g. 'claude-3-5-sonnet-20241022') @@ -157,7 +151,7 @@ async def async_generator(): } Note: We import Anthropic types here since the package must be installed - for raw responses to be available; doing so in the global scope would + for raw responses to be available; doing so in the global scope would result in dependencies error since this provider is not lazily imported (tests fail) """ from anthropic import APIResponse @@ -173,11 +167,7 @@ async def async_generator(): llm_event.model = response_data["model"] llm_event.completion = { "role": response_data.get("role"), - "content": ( - response_data.get("content")[0].get("text") - if response_data.get("content") - else "" - ), + "content": response_data.get("content")[0].get("text") if response_data.get("content") else "", } if usage := response_data.get("usage"): llm_event.prompt_tokens = usage.get("input_tokens") @@ -229,9 +219,7 @@ def patched_function(*args, **kwargs): if "session" in kwargs.keys(): del kwargs["session"] - completion_override = fetch_completion_override_from_time_travel_cache( - kwargs - ) + completion_override = fetch_completion_override_from_time_travel_cache(kwargs) if completion_override: result_model = None pydantic_models = ( @@ -246,9 +234,7 @@ def patched_function(*args, **kwargs): for pydantic_model in pydantic_models: try: - result_model = pydantic_model.model_validate_json( - completion_override - ) + result_model = pydantic_model.model_validate_json(completion_override) break except Exception as e: pass @@ -260,18 +246,12 @@ def patched_function(*args, **kwargs): f"{pprint.pformat(completion_override)}" ) return None - return self.handle_response( - result_model, kwargs, init_timestamp, session=session - ) + return self.handle_response(result_model, kwargs, init_timestamp, session=session) # Call the original function with its original arguments - original_func = ( - self.original_create_beta if is_beta else self.original_create - ) + original_func = self.original_create_beta if is_beta else self.original_create result = original_func(*args, **kwargs) - return self.handle_response( - result, kwargs, init_timestamp, session=session - ) + return self.handle_response(result, kwargs, init_timestamp, session=session) return patched_function @@ -303,9 +283,7 @@ async def patched_function(*args, **kwargs): if "session" in kwargs.keys(): del kwargs["session"] - completion_override = fetch_completion_override_from_time_travel_cache( - kwargs - ) + completion_override = fetch_completion_override_from_time_travel_cache(kwargs) if completion_override: result_model = None pydantic_models = ( @@ -320,9 +298,7 @@ async def patched_function(*args, **kwargs): for pydantic_model in pydantic_models: try: - result_model = pydantic_model.model_validate_json( - completion_override - ) + result_model = pydantic_model.model_validate_json(completion_override) break except Exception as e: pass @@ -335,20 +311,12 @@ async def patched_function(*args, **kwargs): ) return None - return self.handle_response( - result_model, kwargs, init_timestamp, session=session - ) + return self.handle_response(result_model, kwargs, init_timestamp, session=session) # Call the original function with its original arguments - original_func = ( - self.original_create_async_beta - if is_beta - else self.original_create_async - ) + original_func = self.original_create_async_beta if is_beta else self.original_create_async result = await original_func(*args, **kwargs) - return self.handle_response( - result, kwargs, init_timestamp, session=session - ) + return self.handle_response(result, kwargs, init_timestamp, session=session) return patched_function diff --git a/agentops/llms/cohere.py b/agentops/llms/cohere.py index 16048f73..0756bc69 100644 --- a/agentops/llms/cohere.py +++ b/agentops/llms/cohere.py @@ -36,9 +36,7 @@ def undo_override(self): def __init__(self, client): super().__init__(client) - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ): + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None): """Handle responses for Cohere versions >v5.4.0""" from cohere.types.streamed_chat_response import ( StreamedChatResponse_CitationGeneration, @@ -101,9 +99,7 @@ def handle_stream_chunk(chunk, session: Optional[Session] = None): citation_dict = citation.dict() # Replace document_ids with the actual documents citation_dict["documents"] = [ - documents[doc_id] - for doc_id in citation_dict["document_ids"] - if doc_id in documents + documents[doc_id] for doc_id in citation_dict["document_ids"] if doc_id in documents ] del citation_dict["document_ids"] @@ -119,12 +115,10 @@ def handle_stream_chunk(chunk, session: Optional[Session] = None): pass elif isinstance(chunk, StreamedChatResponse_CitationGeneration): for citation in chunk.citations: - self.action_events[f"{citation.start}.{citation.end}"] = ( - ActionEvent( - action_type="citation", - init_timestamp=get_ISO_time(), - params=citation.text, - ) + self.action_events[f"{citation.start}.{citation.end}"] = ActionEvent( + action_type="citation", + init_timestamp=get_ISO_time(), + params=citation.text, ) elif isinstance(chunk, StreamedChatResponse_SearchQueriesGeneration): for query in chunk.search_queries: @@ -137,9 +131,7 @@ def handle_stream_chunk(chunk, session: Optional[Session] = None): pass except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) diff --git a/agentops/llms/groq.py b/agentops/llms/groq.py index ca869638..23d5b8db 100644 --- a/agentops/llms/groq.py +++ b/agentops/llms/groq.py @@ -29,9 +29,7 @@ def undo_override(self): completions.Completions.create = self.original_create completions.AsyncCompletions.create = self.original_create - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ): + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None): """Handle responses for OpenAI versions >v1.0.0""" from groq import AsyncStream, Stream from groq.resources.chat import AsyncCompletions @@ -81,9 +79,7 @@ def handle_stream_chunk(chunk: ChatCompletionChunk): self._safe_record(session, llm_event) except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) diff --git a/agentops/llms/instrumented_provider.py b/agentops/llms/instrumented_provider.py index f33f397a..f4dc78cf 100644 --- a/agentops/llms/instrumented_provider.py +++ b/agentops/llms/instrumented_provider.py @@ -14,9 +14,7 @@ def __init__(self, client): self.client = client @abstractmethod - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ) -> dict: + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None) -> dict: pass @abstractmethod diff --git a/agentops/llms/litellm.py b/agentops/llms/litellm.py index 30b4c25a..0f73c22b 100644 --- a/agentops/llms/litellm.py +++ b/agentops/llms/litellm.py @@ -40,9 +40,7 @@ def undo_override(self): completions.Completions.create = self.original_oai_create completions.AsyncCompletions.create = self.original_oai_create_async - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ) -> dict: + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None) -> dict: """Handle responses for OpenAI versions >v1.0.0""" from openai import AsyncStream, Stream from openai.resources import AsyncCompletions @@ -93,9 +91,7 @@ def handle_stream_chunk(chunk: ChatCompletionChunk): self._safe_record(session, llm_event) except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) @@ -186,14 +182,10 @@ def patched_function(*args, **kwargs): if "session" in kwargs.keys(): del kwargs["session"] - completion_override = fetch_completion_override_from_time_travel_cache( - kwargs - ) + completion_override = fetch_completion_override_from_time_travel_cache(kwargs) if completion_override: result_model = ChatCompletion.model_validate_json(completion_override) - return self.handle_response( - result_model, kwargs, init_timestamp, session=session - ) + return self.handle_response(result_model, kwargs, init_timestamp, session=session) # prompt_override = fetch_prompt_override_from_time_travel_cache(kwargs) # if prompt_override: @@ -222,14 +214,10 @@ async def patched_function(*args, **kwargs): if "session" in kwargs.keys(): del kwargs["session"] - completion_override = fetch_completion_override_from_time_travel_cache( - kwargs - ) + completion_override = fetch_completion_override_from_time_travel_cache(kwargs) if completion_override: result_model = ChatCompletion.model_validate_json(completion_override) - return self.handle_response( - result_model, kwargs, init_timestamp, session=session - ) + return self.handle_response(result_model, kwargs, init_timestamp, session=session) # prompt_override = fetch_prompt_override_from_time_travel_cache(kwargs) # if prompt_override: diff --git a/agentops/llms/mistral.py b/agentops/llms/mistral.py index be6933c9..770d4a81 100644 --- a/agentops/llms/mistral.py +++ b/agentops/llms/mistral.py @@ -20,9 +20,7 @@ def __init__(self, client): super().__init__(client) self._provider_name = "Mistral" - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ) -> dict: + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None) -> dict: """Handle responses for Mistral""" from mistralai import Chat from mistralai.types import UNSET, UNSET_SENTINEL @@ -72,9 +70,7 @@ def handle_stream_chunk(chunk: dict): self._safe_record(session, llm_event) except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) diff --git a/agentops/llms/ollama.py b/agentops/llms/ollama.py index e5779283..5c998897 100644 --- a/agentops/llms/ollama.py +++ b/agentops/llms/ollama.py @@ -16,9 +16,7 @@ class OllamaProvider(InstrumentedProvider): original_create = None original_create_async = None - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ) -> dict: + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None) -> dict: llm_event = LLMEvent(init_timestamp=init_timestamp, params=kwargs) def handle_stream_chunk(chunk: dict): @@ -84,9 +82,7 @@ def patched_function(*args, **kwargs): # Call the original function with its original arguments init_timestamp = get_ISO_time() result = original_func["ollama.chat"](*args, **kwargs) - return self.handle_response( - result, kwargs, init_timestamp, session=kwargs.get("session", None) - ) + return self.handle_response(result, kwargs, init_timestamp, session=kwargs.get("session", None)) # Override the original method with the patched one ollama.chat = patched_function diff --git a/agentops/llms/openai.py b/agentops/llms/openai.py index fd8e92b9..24756971 100644 --- a/agentops/llms/openai.py +++ b/agentops/llms/openai.py @@ -21,9 +21,7 @@ def __init__(self, client): super().__init__(client) self._provider_name = "OpenAI" - def handle_response( - self, response, kwargs, init_timestamp, session: Optional[Session] = None - ) -> dict: + def handle_response(self, response, kwargs, init_timestamp, session: Optional[Session] = None) -> dict: """Handle responses for OpenAI versions >v1.0.0""" from openai import AsyncStream, Stream from openai.resources import AsyncCompletions @@ -73,9 +71,7 @@ def handle_stream_chunk(chunk: ChatCompletionChunk): self._safe_record(session, llm_event) except Exception as e: - self._safe_record( - session, ErrorEvent(trigger_event=llm_event, exception=e) - ) + self._safe_record(session, ErrorEvent(trigger_event=llm_event, exception=e)) kwargs_str = pprint.pformat(kwargs) chunk = pprint.pformat(chunk) @@ -156,17 +152,13 @@ def patched_function(*args, **kwargs): if "session" in kwargs.keys(): del kwargs["session"] - completion_override = fetch_completion_override_from_time_travel_cache( - kwargs - ) + completion_override = fetch_completion_override_from_time_travel_cache(kwargs) if completion_override: result_model = None pydantic_models = (ChatCompletion, ChatCompletionChunk) for pydantic_model in pydantic_models: try: - result_model = pydantic_model.model_validate_json( - completion_override - ) + result_model = pydantic_model.model_validate_json(completion_override) break except Exception as e: pass @@ -178,9 +170,7 @@ def patched_function(*args, **kwargs): f"{pprint.pformat(completion_override)}" ) return None - return self.handle_response( - result_model, kwargs, init_timestamp, session=session - ) + return self.handle_response(result_model, kwargs, init_timestamp, session=session) # prompt_override = fetch_prompt_override_from_time_travel_cache(kwargs) # if prompt_override: @@ -207,17 +197,13 @@ async def patched_function(*args, **kwargs): if "session" in kwargs.keys(): del kwargs["session"] - completion_override = fetch_completion_override_from_time_travel_cache( - kwargs - ) + completion_override = fetch_completion_override_from_time_travel_cache(kwargs) if completion_override: result_model = None pydantic_models = (ChatCompletion, ChatCompletionChunk) for pydantic_model in pydantic_models: try: - result_model = pydantic_model.model_validate_json( - completion_override - ) + result_model = pydantic_model.model_validate_json(completion_override) break except Exception as e: pass @@ -229,9 +215,7 @@ async def patched_function(*args, **kwargs): f"{pprint.pformat(completion_override)}" ) return None - return self.handle_response( - result_model, kwargs, init_timestamp, session=session - ) + return self.handle_response(result_model, kwargs, init_timestamp, session=session) # prompt_override = fetch_prompt_override_from_time_travel_cache(kwargs) # if prompt_override: diff --git a/agentops/meta_client.py b/agentops/meta_client.py index 6e222771..6cc7ed2e 100644 --- a/agentops/meta_client.py +++ b/agentops/meta_client.py @@ -14,9 +14,7 @@ class MetaClient(type): def __new__(cls, name, bases, dct): # Wrap each method with the handle_exceptions decorator for method_name, method in dct.items(): - if ( - callable(method) and not method_name.startswith("__") - ) or method_name == "__init__": + if (callable(method) and not method_name.startswith("__")) or method_name == "__init__": dct[method_name] = handle_exceptions(method) return super().__new__(cls, name, bases, dct) diff --git a/agentops/partners/autogen_logger.py b/agentops/partners/autogen_logger.py index 4080d792..77aca142 100644 --- a/agentops/partners/autogen_logger.py +++ b/agentops/partners/autogen_logger.py @@ -72,13 +72,9 @@ def log_chat_completion( def log_new_agent(self, agent: ConversableAgent, init_args: Dict[str, Any]) -> None: """Calls agentops.create_agent""" ao_agent_id = agentops.create_agent(agent.name, str(uuid4())) - self.agent_store.append( - {"agentops_id": ao_agent_id, "autogen_id": str(id(agent))} - ) + self.agent_store.append({"agentops_id": ao_agent_id, "autogen_id": str(id(agent))}) - def log_event( - self, source: Union[str, Agent], name: str, **kwargs: Dict[str, Any] - ) -> None: + def log_event(self, source: Union[str, Agent], name: str, **kwargs: Dict[str, Any]) -> None: """Records an ActionEvent to AgentOps session""" event = ActionEvent(action_type=name) agentops_id = self._get_agentops_id_from_agent(str(id(source))) @@ -86,9 +82,7 @@ def log_event( event.params = kwargs agentops.record(event) - def log_function_use( - self, source: Union[str, Agent], function: F, args: Dict[str, Any], returns: any - ): + def log_function_use(self, source: Union[str, Agent], function: F, args: Dict[str, Any], returns: any): """Records a ToolEvent to AgentOps session""" event = ToolEvent() agentops_id = self._get_agentops_id_from_agent(str(id(source))) diff --git a/agentops/partners/langchain_callback_handler.py b/agentops/partners/langchain_callback_handler.py index ec2016aa..7d3ded9b 100644 --- a/agentops/partners/langchain_callback_handler.py +++ b/agentops/partners/langchain_callback_handler.py @@ -131,12 +131,8 @@ def on_llm_end( llm_event.end_timestamp = get_ISO_time() llm_event.completion = response.generations[0][0].text if response.llm_output is not None: - llm_event.prompt_tokens = response.llm_output["token_usage"][ - "prompt_tokens" - ] - llm_event.completion_tokens = response.llm_output["token_usage"][ - "completion_tokens" - ] + llm_event.prompt_tokens = response.llm_output["token_usage"]["prompt_tokens"] + llm_event.completion_tokens = response.llm_output["token_usage"]["completion_tokens"] if len(response.generations) == 0: # TODO: more descriptive error @@ -297,9 +293,7 @@ def on_retriever_end( **kwargs: Any, ) -> None: action_event: ActionEvent = self.events.retriever[str(run_id)] - action_event.logs = ( - documents # TODO: Adding this. Might want to add elsewhere e.g. params - ) + action_event.logs = documents # TODO: Adding this. Might want to add elsewhere e.g. params action_event.end_timestamp = get_ISO_time() self.ao_client.record(action_event) @@ -326,9 +320,7 @@ def on_agent_action( parent_run_id: Optional[UUID] = None, **kwargs: Any, ) -> Any: - self.agent_actions[run_id].append( - ActionEvent(params={"action": action, **kwargs}, action_type="agent") - ) + self.agent_actions[run_id].append(ActionEvent(params={"action": action, **kwargs}, action_type="agent")) @debug_print_function_params def on_agent_finish( @@ -367,9 +359,7 @@ def on_retry( @property def session_id(self): - raise DeprecationWarning( - "session_id is deprecated in favor of current_session_ids" - ) + raise DeprecationWarning("session_id is deprecated in favor of current_session_ids") @property def current_session_ids(self): @@ -395,9 +385,7 @@ def __init__( "tags": tags, } - self.ao_client = AOClient( - **{k: v for k, v in client_params.items() if v is not None}, override=False - ) + self.ao_client = AOClient(**{k: v for k, v in client_params.items() if v is not None}, override=False) self.events = Events() self.agent_actions: Dict[UUID, List[ActionEvent]] = defaultdict(list) @@ -481,12 +469,8 @@ async def on_llm_end( llm_event.end_timestamp = get_ISO_time() llm_event.completion = response.generations[0][0].text if response.llm_output is not None: - llm_event.prompt_tokens = response.llm_output["token_usage"][ - "prompt_tokens" - ] - llm_event.completion_tokens = response.llm_output["token_usage"][ - "completion_tokens" - ] + llm_event.prompt_tokens = response.llm_output["token_usage"]["prompt_tokens"] + llm_event.completion_tokens = response.llm_output["token_usage"]["completion_tokens"] if len(response.generations) == 0: # TODO: more descriptive error @@ -644,9 +628,7 @@ async def on_retriever_end( **kwargs: Any, ) -> None: action_event: ActionEvent = self.events.retriever[str(run_id)] - action_event.logs = ( - documents # TODO: Adding this. Might want to add elsewhere e.g. params - ) + action_event.logs = documents # TODO: Adding this. Might want to add elsewhere e.g. params action_event.end_timestamp = get_ISO_time() self.ao_client.record(action_event) @@ -673,9 +655,7 @@ async def on_agent_action( parent_run_id: Optional[UUID] = None, **kwargs: Any, ) -> Any: - self.agent_actions[run_id].append( - ActionEvent(params={"action": action, **kwargs}, action_type="agent") - ) + self.agent_actions[run_id].append(ActionEvent(params={"action": action, **kwargs}, action_type="agent")) @debug_print_function_params async def on_agent_finish( diff --git a/agentops/session.py b/agentops/session.py index 330409dc..a12a1cf8 100644 --- a/agentops/session.py +++ b/agentops/session.py @@ -90,9 +90,7 @@ def end_session( return if not any(end_state == state.value for state in EndState): - return logger.warning( - "Invalid end_state. Please use one of the EndState enums" - ) + return logger.warning("Invalid end_state. Please use one of the EndState enums") self.end_timestamp = get_ISO_time() self.end_state = end_state @@ -115,9 +113,7 @@ def end_session( formatted_cost = ( "{:.2f}".format(token_cost_d) if token_cost_d == 0 - else "{:.6f}".format( - token_cost_d.quantize(Decimal("0.000001"), rounding=ROUND_HALF_UP) - ) + else "{:.6f}".format(token_cost_d.quantize(Decimal("0.000001"), rounding=ROUND_HALF_UP)) ) analytic_stats = self.get_analytics(response) @@ -193,8 +189,7 @@ def record(self, event: Union[Event, ErrorEvent]): if event.trigger_event: if ( not event.trigger_event.end_timestamp - or event.trigger_event.init_timestamp - == event.trigger_event.end_timestamp + or event.trigger_event.init_timestamp == event.trigger_event.end_timestamp ): event.trigger_event.end_timestamp = get_ISO_time() @@ -309,9 +304,7 @@ def _flush_queue(self) -> None: return logger.error(f"Could not post events - {e}") logger.debug("\n") - logger.debug( - f"Session request to {self.config.endpoint}/v2/create_events" - ) + logger.debug(f"Session request to {self.config.endpoint}/v2/create_events") logger.debug(serialized_payload) logger.debug("\n") diff --git a/agentops/time_travel.py b/agentops/time_travel.py index 6c1c7588..55ad6662 100644 --- a/agentops/time_travel.py +++ b/agentops/time_travel.py @@ -20,9 +20,7 @@ def __init__(self): try: with open(cache_path, "r") as file: time_travel_cache_json = json.load(file) - self._completion_overrides = time_travel_cache_json.get( - "completion_overrides" - ) + self._completion_overrides = time_travel_cache_json.get("completion_overrides") except FileNotFoundError: return @@ -99,20 +97,14 @@ def find_cache_hit(prompt_messages, completion_overrides): continue if all( - isinstance(a, dict) - and isinstance(b, dict) - and a.get("content") == b.get("content") + isinstance(a, dict) and isinstance(b, dict) and a.get("content") == b.get("content") for a, b in zip(prompt_messages, cached_messages) ): return value except (SyntaxError, ValueError, TypeError) as e: - print( - f"{ttd_prepend_string} Error - Error processing completion_overrides item: {e}" - ) + print(f"{ttd_prepend_string} Error - Error processing completion_overrides item: {e}") except Exception as e: - print( - f"{ttd_prepend_string} Error - Unexpected error in find_cache_hit: {e}" - ) + print(f"{ttd_prepend_string} Error - Unexpected error in find_cache_hit: {e}") return None @@ -143,9 +135,7 @@ def set_time_travel_active_state(is_active: bool): try: yaml.dump(config, config_file) except: - print( - f"{ttd_prepend_string} Error - Unable to write to {config_path}. Time Travel not activated" - ) + print(f"{ttd_prepend_string} Error - Unable to write to {config_path}. Time Travel not activated") return if is_active: diff --git a/examples/anthropic_examples/agentops-anthropic-understanding-tools.ipynb b/examples/anthropic_examples/agentops-anthropic-understanding-tools.ipynb index c5dfdf02..9cc95c57 100644 --- a/examples/anthropic_examples/agentops-anthropic-understanding-tools.ipynb +++ b/examples/anthropic_examples/agentops-anthropic-understanding-tools.ipynb @@ -40,9 +40,9 @@ "from anthropic import Anthropic, AsyncAnthropic\n", "import agentops\n", "import os\n", - "import random # We don't need this for agentops, we use this to generate a message later\n", - "import time # We don't need this for agentops either, we use this when simulating an API later\n", - "import re # Regex for formatting\n", + "import random #We don't need this for agentops, we use this to generate a message later\n", + "import time #We don't need this for agentops either, we use this when simulating an API later\n", + "import re #Regex for formatting\n", "from dotenv import load_dotenv" ] }, @@ -124,32 +124,32 @@ "locations = [\n", " {\n", " \"Name\": \"Super Gore Nest\",\n", - " \"Description\": \"A grotesque mass of demonic growth and organic structures infesting the ruins of an urban area on Earth. The Super Gore Nest serves as a massive, pulsating hive for Hell’s forces, complete with rivers of blood, twisted tendrils, and a dark, organic design that shows how deeply Hell has taken root in the city.\",\n", + " \"Description\": \"A grotesque mass of demonic growth and organic structures infesting the ruins of an urban area on Earth. The Super Gore Nest serves as a massive, pulsating hive for Hell’s forces, complete with rivers of blood, twisted tendrils, and a dark, organic design that shows how deeply Hell has taken root in the city.\"\n", " },\n", " {\n", " \"Name\": \"Exultia\",\n", - " \"Description\": \"An ancient, mystical world that holds the ruins of the Night Sentinels' kingdom, with gothic structures and arcane symbols throughout. This realm is filled with epic landscapes, medieval architecture, and hints of the powerful civilization that once defended against Hell’s forces.\",\n", + " \"Description\": \"An ancient, mystical world that holds the ruins of the Night Sentinels' kingdom, with gothic structures and arcane symbols throughout. This realm is filled with epic landscapes, medieval architecture, and hints of the powerful civilization that once defended against Hell’s forces.\"\n", " },\n", " {\n", " \"Name\": \"Cultist Base\",\n", - " \"Description\": \"A grim fortress hidden within the icy mountains, where a fanatical cult worships demons. Filled with chilling sacrificial chambers, traps, and rituals, the Cultist Base is a hostile stronghold where Doom Slayer must confront the cult leaders aiding Hell's invasion of Earth.\",\n", + " \"Description\": \"A grim fortress hidden within the icy mountains, where a fanatical cult worships demons. Filled with chilling sacrificial chambers, traps, and rituals, the Cultist Base is a hostile stronghold where Doom Slayer must confront the cult leaders aiding Hell's invasion of Earth.\"\n", " },\n", " {\n", " \"Name\": \"Taras Nabad\",\n", - " \"Description\": \"A war-ravaged city on the homeworld of the Night Sentinels, showcasing grandiose, ancient architecture in the midst of destruction. Taras Nabad's sprawling structures and historical significance reveal glimpses into the Doom Slayer’s past and the once-thriving Sentinel civilization.\",\n", + " \"Description\": \"A war-ravaged city on the homeworld of the Night Sentinels, showcasing grandiose, ancient architecture in the midst of destruction. Taras Nabad's sprawling structures and historical significance reveal glimpses into the Doom Slayer’s past and the once-thriving Sentinel civilization.\"\n", " },\n", " {\n", " \"Name\": \"Nekravol\",\n", - " \"Description\": \"A hellish, industrial fortress where souls are processed into Argent energy. With conveyor belts moving the damned and a skyline dominated by fire and darkness, Nekravol is a nightmarish facility that powers Hell's armies and embodies the horrific machinery of Hell's cruelty.\",\n", + " \"Description\": \"A hellish, industrial fortress where souls are processed into Argent energy. With conveyor belts moving the damned and a skyline dominated by fire and darkness, Nekravol is a nightmarish facility that powers Hell's armies and embodies the horrific machinery of Hell's cruelty.\"\n", " },\n", " {\n", " \"Name\": \"Urdak\",\n", - " \"Description\": \"A surreal, high-tech realm that serves as the home of the angelic Maykrs. Urdak’s sleek, pristine architecture and ethereal ambiance sharply contrast with Hell’s brutal landscapes, yet this realm holds its own dark secrets and a critical role in Hell's invasion of Earth.\",\n", + " \"Description\": \"A surreal, high-tech realm that serves as the home of the angelic Maykrs. Urdak’s sleek, pristine architecture and ethereal ambiance sharply contrast with Hell’s brutal landscapes, yet this realm holds its own dark secrets and a critical role in Hell's invasion of Earth.\"\n", " },\n", " {\n", " \"Name\": \"UAC Base\",\n", - " \"Description\": \"A futuristic military base on Earth controlled by the Union Aerospace Corporation (UAC), filled with high-tech weaponry and security systems. The UAC Base serves as a human foothold in the fight against Hell, though some within its ranks may have darker intentions.\",\n", - " },\n", + " \"Description\": \"A futuristic military base on Earth controlled by the Union Aerospace Corporation (UAC), filled with high-tech weaponry and security systems. The UAC Base serves as a human foothold in the fight against Hell, though some within its ranks may have darker intentions.\"\n", + " }\n", "]\n", "\n", "combat_casualties = [\"Nonexistent\", \"Low\", \"Medium\", \"High\", \"Extinction\"]\n", @@ -160,8 +160,8 @@ " \"Disrupt Hell's control over the area by eliminating critical targets.\",\n", " \"Enter a critical demonic stronghold to disrupt enemy operations.\",\n", " \"Locate and destroy the central power source to weaken enemy forces.\",\n", - " \"Collect essential resources before the area becomes unstable.\",\n", - "]" + " \"Collect essential resources before the area becomes unstable.\"\n", + "]\n" ] }, { @@ -180,7 +180,7 @@ "def generate_missions():\n", " selectedmissions = []\n", " loop = 0\n", - "\n", + " \n", " while loop < 3:\n", " location = random.choice(locations)\n", " casualties = random.choice(combat_casualties)\n", @@ -188,17 +188,15 @@ " final = (\n", " f'LocationName: {location[\"Name\"]}, '\n", " f'LocationInfo: {location[\"Description\"]}, '\n", - " f\"HumanCombatCasualties: {casualties}, \"\n", - " f\"Mission: {mission}\"\n", + " f'HumanCombatCasualties: {casualties}, '\n", + " f'Mission: {mission}'\n", " )\n", - "\n", + " \n", " selectedmissions.append(final)\n", " loop += 1\n", - "\n", + " \n", " # Combine all mission strings into a single string with a separator (e.g., newline or comma)\n", - " missions_string = \"\\n\".join(\n", - " missions\n", - " ) # Or \", \".join(missions) for a comma-separated string\n", + " missions_string = \"\\n\".join(missions) # Or \", \".join(missions) for a comma-separated string\n", " print(missions_string)\n", " return missions_string" ] @@ -261,9 +259,9 @@ "initial_messages = [\n", " {\n", " \"role\": \"user\",\n", - " \"content\": \"You are VEGA, the assistant to the DOOMGUY. Get three missions from the ship's API and tell me which mission is most to least important for quellng the forces of hell. \",\n", + " \"content\": \"You are VEGA, the assistant to the DOOMGUY. Get three missions from the ship's API and tell me which mission is most to least important for quellng the forces of hell. \"\n", " }\n", - "]" + "]\n" ] }, { @@ -290,14 +288,17 @@ "response = client.messages.create(\n", " max_tokens=5000,\n", " model=\"claude-3-5-sonnet-20240620\",\n", - " tools=[\n", - " {\n", - " \"name\": \"generate_missions\",\n", - " \"description\": \"Retrieve three missions for the DoomSlayer\",\n", - " \"input_schema\": {\"type\": \"object\", \"properties\": {}, \"required\": []},\n", - " }\n", - " ],\n", - " messages=initial_messages,\n", + " tools=[{\n", + " \"name\": \"generate_missions\",\n", + " \"description\": \"Retrieve three missions for the DoomSlayer\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " },\n", + " \"required\": []\n", + " },\n", + " }],\n", + " messages=initial_messages\n", ")\n", "\n", "print(response.content)" @@ -374,7 +375,8 @@ "# Check if the tool name is \"generate_missions\"\n", "if tool_name == \"generate_missions\":\n", " # Call the function with the tool creator as an argument\n", - " gen_mission_result = generate_missions()" + " gen_mission_result = generate_missions()\n", + " " ] }, { @@ -390,14 +392,15 @@ "metadata": {}, "outputs": [], "source": [ - "initial_messages.append({\"role\": \"assistant\", \"content\": gen_mission_result})\n", + "initial_messages.append({\n", + " \"role\": \"assistant\",\n", + " \"content\": gen_mission_result\n", + "})\n", "\n", - "initial_messages.append(\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": \"Based on these, which location should take priority and why?\",\n", - " }\n", - ")" + "initial_messages.append({\n", + " \"role\": \"user\",\n", + " \"content\": \"Based on these, which location should take priority and why?\"\n", + "})" ] }, { @@ -424,14 +427,17 @@ "response = client.messages.create(\n", " max_tokens=5000,\n", " model=\"claude-3-5-sonnet-20240620\",\n", - " tools=[\n", - " {\n", - " \"name\": \"generate_missions\",\n", - " \"description\": \"Retrieve three missions for the DoomSlayer\",\n", - " \"input_schema\": {\"type\": \"object\", \"properties\": {}, \"required\": []},\n", - " }\n", - " ],\n", - " messages=initial_messages,\n", + " tools=[{\n", + " \"name\": \"generate_missions\",\n", + " \"description\": \"Retrieve three missions for the DoomSlayer\",\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " },\n", + " \"required\": []\n", + " },\n", + " }],\n", + " messages=initial_messages\n", ")\n", "\n", "print(response)" @@ -480,45 +486,46 @@ "demons = [\n", " {\n", " \"Name\": \"Imp\",\n", - " \"Description\": \"A fast, agile demon that hurls fireballs and uses its claws to tear apart its prey. Imps are commonly found in Hell’s army, notorious for their quickness and ability to climb walls, making them dangerous adversaries in any environment.\",\n", + " \"Description\": \"A fast, agile demon that hurls fireballs and uses its claws to tear apart its prey. Imps are commonly found in Hell’s army, notorious for their quickness and ability to climb walls, making them dangerous adversaries in any environment.\"\n", " },\n", " {\n", " \"Name\": \"Cacodemon\",\n", - " \"Description\": \"A floating, spherical demon with a large mouth full of teeth and an ability to launch explosive projectiles. Cacodemons are often encountered in open areas, where their aerial agility and relentless attacks pose a constant threat.\",\n", + " \"Description\": \"A floating, spherical demon with a large mouth full of teeth and an ability to launch explosive projectiles. Cacodemons are often encountered in open areas, where their aerial agility and relentless attacks pose a constant threat.\"\n", " },\n", " {\n", " \"Name\": \"Hell Knight\",\n", - " \"Description\": \"A towering, brutish demon with immense strength and durability. The Hell Knight is capable of charging at the Doom Slayer and delivering devastating melee attacks. Its tough hide makes it resistant to most forms of damage.\",\n", + " \"Description\": \"A towering, brutish demon with immense strength and durability. The Hell Knight is capable of charging at the Doom Slayer and delivering devastating melee attacks. Its tough hide makes it resistant to most forms of damage.\"\n", " },\n", " {\n", " \"Name\": \"Mancubus\",\n", - " \"Description\": \"A grotesque, overweight demon that releases powerful fireballs from its massive arm cannons. Mancubus demons are slow-moving but dangerous due to their firepower and the ability to overwhelm enemies with their fiery onslaughts.\",\n", - " },\n", + " \"Description\": \"A grotesque, overweight demon that releases powerful fireballs from its massive arm cannons. Mancubus demons are slow-moving but dangerous due to their firepower and the ability to overwhelm enemies with their fiery onslaughts.\"\n", + " }\n", "]\n", "\n", "\n", + "\n", "weapons = [\n", " {\n", " \"Name\": \"Super Shotgun\",\n", - " \"Description\": \"A powerful, double-barreled shotgun that delivers devastating close-range damage. Known for its sheer stopping power, the Super Shotgun can tear through enemies with ease, especially when equipped with the Meat Hook attachment, allowing for rapid mobility and devastating hits.\",\n", + " \"Description\": \"A powerful, double-barreled shotgun that delivers devastating close-range damage. Known for its sheer stopping power, the Super Shotgun can tear through enemies with ease, especially when equipped with the Meat Hook attachment, allowing for rapid mobility and devastating hits.\"\n", " },\n", " {\n", " \"Name\": \"Rocket Launcher\",\n", - " \"Description\": \"A high-powered weapon that fires explosive rockets capable of dealing massive area damage. The Rocket Launcher is invaluable for taking down groups of enemies or dealing significant damage to larger demons, especially when upgraded with the Lock-On Burst mod.\",\n", + " \"Description\": \"A high-powered weapon that fires explosive rockets capable of dealing massive area damage. The Rocket Launcher is invaluable for taking down groups of enemies or dealing significant damage to larger demons, especially when upgraded with the Lock-On Burst mod.\"\n", " },\n", " {\n", " \"Name\": \"Chaingun\",\n", - " \"Description\": \"A rapid-fire weapon that can unleash a torrent of bullets at a high rate of speed. The Chaingun is perfect for mowing down enemies and can be equipped with the Heat Blast mod, allowing for explosive energy rounds that can clear multiple enemies at once.\",\n", + " \"Description\": \"A rapid-fire weapon that can unleash a torrent of bullets at a high rate of speed. The Chaingun is perfect for mowing down enemies and can be equipped with the Heat Blast mod, allowing for explosive energy rounds that can clear multiple enemies at once.\"\n", " },\n", " {\n", " \"Name\": \"BFG 9000\",\n", - " \"Description\": \"One of the most iconic weapons in the *Doom* franchise, the BFG 9000 fires a massive energy beam that obliterates anything in its path. With its massive damage potential, the BFG 9000 is a game-changer, especially in dealing with large groups of enemies or the toughest foes.\",\n", + " \"Description\": \"One of the most iconic weapons in the *Doom* franchise, the BFG 9000 fires a massive energy beam that obliterates anything in its path. With its massive damage potential, the BFG 9000 is a game-changer, especially in dealing with large groups of enemies or the toughest foes.\"\n", " },\n", " {\n", " \"Name\": \"Ice Bomb\",\n", - " \"Description\": \"A special grenade that freezes enemies in a wide area, giving the Doom Slayer a chance to deal with multiple foes at once. The Ice Bomb is effective for crowd control, allowing for easy Glory Kills or creating distance from overwhelming enemies.\",\n", - " },\n", - "]" + " \"Description\": \"A special grenade that freezes enemies in a wide area, giving the Doom Slayer a chance to deal with multiple foes at once. The Ice Bomb is effective for crowd control, allowing for easy Glory Kills or creating distance from overwhelming enemies.\"\n", + " }\n", + "]\n" ] }, { @@ -534,12 +541,10 @@ "metadata": {}, "outputs": [], "source": [ - "initial_messages.append(\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": \"The first priority mission was selected. At the same time, scan for enemies and check inventory to determine the best combat strategy. You should use both tools at once.\",\n", - " }\n", - ")" + "initial_messages.append({\n", + " \"role\": \"user\",\n", + " \"content\": \"The first priority mission was selected. At the same time, scan for enemies and check inventory to determine the best combat strategy. You should use both tools at once.\"\n", + "})\n" ] }, { @@ -558,17 +563,17 @@ "def enemyscan(amount):\n", " enemiesonscene = []\n", " loop = 0\n", - "\n", + " \n", " while loop < amount + 1:\n", " scannedenemy = random.choice(demons)\n", - "\n", + " \n", " # Append just the name of the demon to the list\n", " enemiesonscene.append(scannedenemy[\"Name\"])\n", " enemiesonscene.append(scannedenemy[\"Description\"])\n", " loop += 1\n", - "\n", + " \n", " # Combine all mission strings into a single string with a separator (e.g., newline or comma)\n", - " enemies_string = \"\\n\".join(enemiesonscene)\n", + " enemies_string = \"\\n\".join(enemiesonscene) \n", " print(enemies_string)\n", " return enemies_string" ] @@ -627,17 +632,17 @@ "def inventoryscan():\n", " weapons_at_hand = []\n", " loop = 0\n", - "\n", + " \n", " while loop < 5:\n", " weapon = random.choice(weapons)\n", - "\n", + " \n", " # Append just the name of the demon to the list\n", " weapons_at_hand.append(weapon[\"Name\"])\n", " weapons_at_hand.append(weapon[\"Description\"])\n", " loop += 1\n", - "\n", + " \n", " # Combine all mission strings into a single string with a separator (e.g., newline or comma)\n", - " weapons_string = \"\\n\".join(weapons_at_hand)\n", + " weapons_string = \"\\n\".join(weapons_at_hand) \n", " print(weapons_string)\n", " return weapons_string" ] @@ -711,22 +716,26 @@ " \"properties\": {\n", " \"amount\": {\n", " \"type\": \"integer\",\n", - " \"description\": \"Number of enemies to scan.\",\n", + " \"description\": \"Number of enemies to scan.\"\n", " }\n", " },\n", - " \"required\": [\"amount\"],\n", + " \"required\": [\"amount\"]\n", " },\n", " },\n", " {\n", " \"name\": \"inventoryscan_tool\",\n", " \"description\": \"Retrieve a list of weapons the Doom Slayer has at hand.\",\n", - " \"input_schema\": {\"type\": \"object\", \"properties\": {}, \"required\": []},\n", - " },\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {},\n", + " \"required\": []\n", + " },\n", + " }\n", " ],\n", - " messages=initial_messages,\n", + " messages=initial_messages\n", ")\n", "\n", - "print(response)" + "print(response)\n" ] }, { @@ -760,7 +769,10 @@ "metadata": {}, "outputs": [], "source": [ - "initial_messages.append({\"role\": \"assistant\", \"content\": f\"{str(response)}\"})" + "initial_messages.append({\n", + " \"role\": \"assistant\",\n", + " \"content\": f\"{str(response)}\" \n", + "})" ] }, { @@ -831,37 +843,33 @@ "enemy_scan_res = \"\"\n", "\n", "\n", - "response_str = str(response)\n", - "tool_use_count = response_str.count(\n", - " \"ToolUseBlock\"\n", - ") # We know the ToolUseBlock will appear once for each tool request so we check how many time it appears\n", + "response_str = str(response) \n", + "tool_use_count = response_str.count(\"ToolUseBlock\") #We know the ToolUseBlock will appear once for each tool request so we check how many time it appears\n", "\n", "\n", "# You can use print(tool_use_count)to validate the ToolBlocks here if you wish\n", "\n", - "loop = 0\n", + "loop = 0 \n", "\n", - "# We do this instead of a (foreach) because we need to skip the first block! This contains the message from the AI, not the tool! This way allows us to reference the item we want as easily as possible without complex logic needed!\n", + "#We do this instead of a (foreach) because we need to skip the first block! This contains the message from the AI, not the tool! This way allows us to reference the item we want as easily as possible without complex logic needed!\n", "\n", - "while loop < tool_use_count: # We will get the tools now\n", - " tool_use_block = response.content[\n", - " loop + 1\n", - " ] # We start at 1 since 0 holds the AI mesage\n", + "while loop < tool_use_count: #We will get the tools now\n", + " tool_use_block = response.content[loop + 1] #We start at 1 since 0 holds the AI mesage\n", " tool_name = tool_use_block.name\n", " tool_input = tool_use_block.input\n", - "\n", + " \n", " if tool_name == \"inventoryscan_tool\":\n", " # Call the inventoryscan function for inventoryscan_tool\n", " inv_scan_res = inventoryscan()\n", " elif tool_name == \"enemyscan_tool\":\n", " # Get the amount for enemyscan_tool\n", - " amount = tool_input[\"amount\"]\n", + " amount = tool_input['amount']\n", " # Call the enemyscan function with the amount\n", " enemy_scan_res = enemyscan(amount)\n", - "\n", - " loop = loop + 1\n", - "print(inv_scan_res)\n", - "print(enemy_scan_res)" + " \n", + " loop = loop + 1 \n", + "print (inv_scan_res)\n", + "print (enemy_scan_res)" ] }, { @@ -877,20 +885,16 @@ "metadata": {}, "outputs": [], "source": [ - "initial_messages.append(\n", - " {\n", - " \"role\": \"assistant\",\n", - " \"content\": f\"Weapons Inventory Scan Result: {inv_scan_res}\\nEnemy Scans Result: {enemy_scan_res}\",\n", - " }\n", - ")\n", + "initial_messages.append({\n", + " \"role\": \"assistant\",\n", + " \"content\": f\"Weapons Inventory Scan Result: {inv_scan_res}\\nEnemy Scans Result: {enemy_scan_res}\" \n", + "})\n", "\n", "\n", - "initial_messages.append(\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": \"What is the combat plan for killing these demons? Based on the last message, tell me which demons to kill first, in which order and using which weapons as well as any sweakpoints.\",\n", - " }\n", - ")" + "initial_messages.append({\n", + " \"role\": \"user\",\n", + " \"content\": \"What is the combat plan for killing these demons? Based on the last message, tell me which demons to kill first, in which order and using which weapons as well as any sweakpoints.\"\n", + "})" ] }, { @@ -944,23 +948,27 @@ " \"properties\": {\n", " \"amount\": {\n", " \"type\": \"integer\",\n", - " \"description\": \"Number of enemies to scan.\",\n", + " \"description\": \"Number of enemies to scan.\"\n", " }\n", " },\n", - " \"required\": [\"amount\"],\n", + " \"required\": [\"amount\"]\n", " },\n", " },\n", " {\n", " \"name\": \"inventoryscan_tool\",\n", " \"description\": \"Retrieve a list of weapons the Doom Slayer has at hand.\",\n", - " \"input_schema\": {\"type\": \"object\", \"properties\": {}, \"required\": []},\n", - " },\n", + " \"input_schema\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {},\n", + " \"required\": []\n", + " },\n", + " }\n", " ],\n", - " messages=initial_messages,\n", + " messages=initial_messages\n", ")\n", "\n", "message = response.content[0].text\n", - "print(message)" + "print(message)\n" ] }, { diff --git a/examples/autogen_examples/MathAgent.ipynb b/examples/autogen_examples/MathAgent.ipynb index f33adeca..13bf58a8 100644 --- a/examples/autogen_examples/MathAgent.ipynb +++ b/examples/autogen_examples/MathAgent.ipynb @@ -51,7 +51,7 @@ "source": [ "from autogen import ConversableAgent\n", "from typing import Annotated, Literal\n", - "from autogen import register_function\n", + "from autogen import ConversableAgent, register_function\n", "import agentops\n", "import os\n", "from dotenv import load_dotenv\n", diff --git a/examples/demos/agentchat_agentops.ipynb b/examples/demos/agentchat_agentops.ipynb index 669db241..2aa7a84e 100644 --- a/examples/demos/agentchat_agentops.ipynb +++ b/examples/demos/agentchat_agentops.ipynb @@ -82,7 +82,7 @@ "source": [ "from autogen import ConversableAgent, UserProxyAgent\n", "from typing import Annotated, Literal\n", - "from autogen import register_function\n", + "from autogen import ConversableAgent, register_function\n", "import agentops\n", "import os\n", "from os import environ\n", diff --git a/examples/recording-events.ipynb b/examples/recording-events.ipynb index b26a7858..f1c9e47c 100644 --- a/examples/recording-events.ipynb +++ b/examples/recording-events.ipynb @@ -224,7 +224,7 @@ }, "outputs": [], "source": [ - "from agentops import ErrorEvent\n", + "from agentops import ToolEvent, record, ErrorEvent\n", "\n", "\n", "def scrape_website(url: str):\n", diff --git a/tests/core_manual_tests/multi_session_llm.py b/tests/core_manual_tests/multi_session_llm.py index 25a652ae..bd55477c 100644 --- a/tests/core_manual_tests/multi_session_llm.py +++ b/tests/core_manual_tests/multi_session_llm.py @@ -25,9 +25,7 @@ session_1.record(ActionEvent(action_type="test event")) # option 2: add session as a keyword argument -response2 = openai.chat.completions.create( - model="gpt-3.5-turbo", messages=messages, temperature=0.5, session=session_2 -) +response2 = openai.chat.completions.create(model="gpt-3.5-turbo", messages=messages, temperature=0.5, session=session_2) session_1.end_session(end_state="Success") session_2.end_session(end_state="Success") diff --git a/tests/core_manual_tests/providers/litellm_canary.py b/tests/core_manual_tests/providers/litellm_canary.py index ecd2186a..0b9cf98c 100644 --- a/tests/core_manual_tests/providers/litellm_canary.py +++ b/tests/core_manual_tests/providers/litellm_canary.py @@ -7,9 +7,7 @@ load_dotenv() agentops.init(default_tags=["litellm-provider-test"]) -response = litellm.completion( - model="gpt-3.5-turbo", messages=[{"content": "Hello, how are you?", "role": "user"}] -) +response = litellm.completion(model="gpt-3.5-turbo", messages=[{"content": "Hello, how are you?", "role": "user"}]) stream_response = litellm.completion( model="gpt-3.5-turbo", diff --git a/tests/langchain_handlers/_test_langchain_handler.py b/tests/langchain_handlers/_test_langchain_handler.py index 97cc2205..8f468c54 100644 --- a/tests/langchain_handlers/_test_langchain_handler.py +++ b/tests/langchain_handlers/_test_langchain_handler.py @@ -14,13 +14,9 @@ AGENTOPS_API_KEY = os.environ.get("AGENTOPS_API_KEY") OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") -agentops_handler = AgentOpsLangchainCallbackHandler( - api_key=AGENTOPS_API_KEY, tags=["Langchain Example"] -) +agentops_handler = AgentOpsLangchainCallbackHandler(api_key=AGENTOPS_API_KEY, tags=["Langchain Example"]) -llm = ChatOpenAI( - openai_api_key=OPENAI_API_KEY, callbacks=[agentops_handler], model="gpt-3.5-turbo" -) +llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, callbacks=[agentops_handler], model="gpt-3.5-turbo") @tool @@ -43,9 +39,7 @@ def find_movie(genre) -> str: llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True, - callbacks=[ - agentops_handler - ], # You must pass in a callback handler to record your agent + callbacks=[agentops_handler], # You must pass in a callback handler to record your agent handle_parsing_errors=True, ) @@ -56,22 +50,16 @@ def find_movie(genre) -> str: ######## # Async -agentops_handler = AgentOpsAsyncLangchainCallbackHandler( - api_key=AGENTOPS_API_KEY, tags=["Async Example"] -) +agentops_handler = AgentOpsAsyncLangchainCallbackHandler(api_key=AGENTOPS_API_KEY, tags=["Async Example"]) -llm = ChatOpenAI( - openai_api_key=OPENAI_API_KEY, callbacks=[agentops_handler], model="gpt-3.5-turbo" -) +llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, callbacks=[agentops_handler], model="gpt-3.5-turbo") agent = initialize_agent( tools, llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True, - callbacks=[ - agentops_handler - ], # You must pass in a callback handler to record your agent + callbacks=[agentops_handler], # You must pass in a callback handler to record your agent handle_parsing_errors=True, ) diff --git a/tests/openai_handlers/_test_gpt_vision.py b/tests/openai_handlers/_test_gpt_vision.py index 29a04e7e..3aae1231 100644 --- a/tests/openai_handlers/_test_gpt_vision.py +++ b/tests/openai_handlers/_test_gpt_vision.py @@ -45,9 +45,7 @@ def encode_image(image_path): # Path to your image -image_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "logo_for_vision_test.png" -) +image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logo_for_vision_test.png") # Getting the base64 string base64_image = encode_image(image_path) @@ -71,9 +69,7 @@ def encode_image(image_path): "max_tokens": 300, } -response = requests.post( - "https://api.openai.com/v1/chat/completions", headers=headers, json=payload -) +response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload) print(response.json()) diff --git a/tests/openai_handlers/_test_handler_openai_v0.py b/tests/openai_handlers/_test_handler_openai_v0.py index 601be8da..3dc4d5e0 100644 --- a/tests/openai_handlers/_test_handler_openai_v0.py +++ b/tests/openai_handlers/_test_handler_openai_v0.py @@ -7,9 +7,7 @@ print("Running OpenAI