diff --git a/docs/swarms/utils/phoenix_tracer.md b/docs/swarms/utils/phoenix_tracer.md new file mode 100644 index 00000000..97ed422a --- /dev/null +++ b/docs/swarms/utils/phoenix_tracer.md @@ -0,0 +1,128 @@ +# Phoenix Trace Decorator Documentation + +## Introduction + +Welcome to the documentation for the `phoenix_trace_decorator` module. This module provides a convenient decorator for tracing Python functions and capturing exceptions using Phoenix, a versatile tracing and monitoring tool. Phoenix allows you to gain insights into the execution of your code, capture errors, and monitor performance. + +## Table of Contents + +1. [Installation](#installation) +2. [Getting Started](#getting-started) +3. [Decorator Usage](#decorator-usage) +4. [Examples](#examples) +5. [Best Practices](#best-practices) +6. [References](#references) + +## 1. Installation + +Before using the `phoenix_trace_decorator`, you need to install the Swarms library. You can install Phoenix using pip: + +```bash +pip install swarms +``` + +## 2. Getting Started + +Phoenix is a powerful tracing and monitoring tool, and the `phoenix_trace_decorator` simplifies the process of tracing functions and capturing exceptions within your Python code. To begin, ensure that Phoenix is installed, and then import the `phoenix_trace_decorator` module into your Python script. + +```python +from swarms import phoenix_trace_decorator +``` + +## 3. Decorator Usage + +The `phoenix_trace_decorator` module provides a decorator, `phoenix_trace_decorator`, which can be applied to functions you want to trace. The decorator takes a single argument, a docstring that describes the purpose of the function being traced. + +Here is the basic structure of using the decorator: + +```python +@phoenix_trace_decorator("Description of the function") +def my_function(param1, param2): + # Function implementation + pass +``` + +## 4. Examples + +Let's explore some practical examples of using the `phoenix_trace_decorator` in your code. + +### Example 1: Basic Tracing + +In this example, we'll trace a simple function and print a message. + +```python +@phoenix_trace_decorator("Tracing a basic function") +def hello_world(): + print("Hello, World!") + +# Call the decorated function +hello_world() +``` + +### Example 2: Tracing a Function with Parameters + +You can use the decorator with functions that have parameters. + +```python +@phoenix_trace_decorator("Tracing a function with parameters") +def add_numbers(a, b): + result = a + b + print(f"Result: {result}") + +# Call the decorated function with parameters +add_numbers(2, 3) +``` + +### Example 3: Tracing Nested Calls + +The decorator can also trace nested function calls. + +```python +@phoenix_trace_decorator("Outer function") +def outer_function(): + print("Outer function") + + @phoenix_trace_decorator("Inner function") + def inner_function(): + print("Inner function") + + inner_function() + +# Call the decorated functions +outer_function() +``` + +### Example 4: Exception Handling + +Phoenix can capture exceptions and provide detailed information about them. + +```python +@phoenix_trace_decorator("Function with exception handling") +def divide(a, b): + try: + result = a / b + except ZeroDivisionError as e: + raise ValueError("Division by zero") from e + +# Call the decorated function with an exception +try: + divide(5, 0) +except ValueError as e: + print(f"Error: {e}") +``` + +## 5. Best Practices + +When using the `phoenix_trace_decorator`, consider the following best practices: + +- Use meaningful docstrings to describe the purpose of the traced functions. +- Keep your tracing focused on critical parts of your code. +- Make sure Phoenix is properly configured and running before using the decorator. + +## 6. References + +For more information on Phoenix and advanced usage, please refer to the [Phoenix Documentation](https://phoenix-docs.readthedocs.io/en/latest/). + +--- + +By following this documentation, you can effectively use the `phoenix_trace_decorator` to trace your Python functions, capture exceptions, and gain insights into the execution of your code. This tool is valuable for debugging, performance optimization, and monitoring the health of your applications. \ No newline at end of file diff --git a/swarms/utils/phoenix_handler.py b/swarms/utils/phoenix_handler.py new file mode 100644 index 00000000..a7820be8 --- /dev/null +++ b/swarms/utils/phoenix_handler.py @@ -0,0 +1,62 @@ +import subprocess +import sys +import traceback +import functools + +try: + import phoenix as px +except Exception as error: + print(f"Error importing phoenix: {error}") + print("Please install phoenix: pip install phoenix") + subprocess.run( + [sys.executable, "-m", "pip", "install", "arize-mlflow"] + ) + + +def phoenix_trace_decorator(doc_string): + """Phoenix trace decorator. + + + Args: + doc_string (_type_): doc string for the function + + + Example: + >>> @phoenix_trace_decorator( + >>> "This is a doc string" + >>> ) + >>> def test_function(): + >>> print("Hello world") + >>> + >>> test_function() + + + # Example of using the decorator + @phoenix_trace_decorator("This function does XYZ and is traced by Phoenix.") + def my_function(param1, param2): + # Function implementation + pass + """ + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + # Start phoenix session for tracing + session = px.active_session() or px.launch_app() + + try: + # Attempt to execute the function + result = func(*args, **kwargs) + return result + except Exception as error: + error_info = traceback.format_exc() + session.trace_exception( + exception=error, error_info=error_info + ) + raise + + # Atteach docs to wrapper func + wrapper.__doc__ = doc_string + return wrapper + + return decorator diff --git a/tests/utils/test_phoenix_handler.py b/tests/utils/test_phoenix_handler.py new file mode 100644 index 00000000..3b6915b9 --- /dev/null +++ b/tests/utils/test_phoenix_handler.py @@ -0,0 +1,152 @@ +# Import necessary modules and functions for testing +import functools +import subprocess +import sys +import traceback + +import pytest + +# Try importing phoenix and handle exceptions +try: + import phoenix as px +except Exception as error: + print(f"Error importing phoenix: {error}") + print("Please install phoenix: pip install phoenix") + subprocess.run( + [sys.executable, "-m", "pip", "install", "arize-mlflow"] + ) + +# Import the code to be tested +from swarms.utils.phoenix_handler import phoenix_trace_decorator + + +# Define a fixture for Phoenix session +@pytest.fixture(scope="function") +def phoenix_session(): + session = px.active_session() or px.launch_app() + yield session + session.stop() + + +# Define test cases for the phoenix_trace_decorator function +def test_phoenix_trace_decorator_documentation(): + """Test if phoenix_trace_decorator has a docstring.""" + assert phoenix_trace_decorator.__doc__ is not None + + +def test_phoenix_trace_decorator_functionality( + capsys, phoenix_session +): + """Test the functionality of phoenix_trace_decorator.""" + + # Define a function to be decorated + @phoenix_trace_decorator("This is a test function.") + def test_function(): + print("Hello, Phoenix!") + + # Execute the decorated function + test_function() + + # Capture the printed output + captured = capsys.readouterr() + assert captured.out == "Hello, Phoenix!\n" + + +def test_phoenix_trace_decorator_exception_handling(phoenix_session): + """Test if phoenix_trace_decorator handles exceptions correctly.""" + + # Define a function that raises an exception + @phoenix_trace_decorator("This function raises an exception.") + def exception_function(): + raise ValueError("An error occurred.") + + # Execute the decorated function + with pytest.raises(ValueError): + exception_function() + + # Check if the exception was traced by Phoenix + traces = phoenix_session.get_traces() + assert len(traces) == 1 + assert traces[0].get("error") is not None + assert traces[0].get("error_info") is not None + + +# Define test cases for phoenix_trace_decorator +def test_phoenix_trace_decorator_docstring(): + """Test if phoenix_trace_decorator's inner function has a docstring.""" + + @phoenix_trace_decorator("This is a test function.") + def test_function(): + """Test function docstring.""" + pass + + assert test_function.__doc__ is not None + + +def test_phoenix_trace_decorator_functionality_with_params( + capsys, phoenix_session +): + """Test the functionality of phoenix_trace_decorator with parameters.""" + + # Define a function with parameters to be decorated + @phoenix_trace_decorator("This function takes parameters.") + def param_function(a, b): + result = a + b + print(f"Result: {result}") + + # Execute the decorated function with parameters + param_function(2, 3) + + # Capture the printed output + captured = capsys.readouterr() + assert captured.out == "Result: 5\n" + + +def test_phoenix_trace_decorator_nested_calls( + capsys, phoenix_session +): + """Test nested calls of phoenix_trace_decorator.""" + + # Define a nested function with decorators + @phoenix_trace_decorator("Outer function") + def outer_function(): + print("Outer function") + + @phoenix_trace_decorator("Inner function") + def inner_function(): + print("Inner function") + + inner_function() + + # Execute the decorated functions + outer_function() + + # Capture the printed output + captured = capsys.readouterr() + assert "Outer function" in captured.out + assert "Inner function" in captured.out + + +def test_phoenix_trace_decorator_nested_exception_handling( + phoenix_session, +): + """Test exception handling with nested phoenix_trace_decorators.""" + + # Define a function with nested decorators and an exception + @phoenix_trace_decorator("Outer function") + def outer_function(): + @phoenix_trace_decorator("Inner function") + def inner_function(): + raise ValueError("Inner error") + + inner_function() + + # Execute the decorated functions + with pytest.raises(ValueError): + outer_function() + + # Check if both exceptions were traced by Phoenix + traces = phoenix_session.get_traces() + assert len(traces) == 2 + assert "Outer function" in traces[0].get("error_info") + assert "Inner function" in traces[1].get("error_info")