# OpenAI Toolchain

[![PyPI](https://img.shields.io/pypi/v/openai-toolchain.svg)](https://pypi.org/project/openai-toolchain/)
[![Python Version](https://img.shields.io/pypi/pyversions/openai-toolchain)](https://pypi.org/project/openai-toolchain/)
[![Tests](https://github.com/bemade/openai-toolchain/actions/workflows/test.yml/badge.svg)](https://github.com/bemade/openai-toolchain/actions/workflows/test.yml)
[![Documentation](https://github.com/bemade/openai-toolchain/actions/workflows/docs.yml/badge.svg)](https://github.com/bemade/openai-toolchain/actions/workflows/docs.yml)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A Python library for working with OpenAI's function calling API, making it
easier to create and manage tools that can be used with OpenAI's chat models.

## Features

- 🛠️ Simple function registration with `@tool` decorator
- 🤖 Automatic tool schema generation
- 🔄 Seamless integration with OpenAI's API
- ⚡ Support for both sync and async operations
- 📚 Clean and intuitive API

## Installation

```bash
pip install openai-toolchain
```

## Quick Start

1. **Define your tools** using the `@tool` decorator:

```python
from openai_toolchain import tool, OpenAIClient

@tool
def get_weather(location: str, unit: str = "celsius") -> str:
    """Get the current weather in a given location.

    Args:
        location: The city to get the weather for
        unit: The unit of temperature (celsius or fahrenheit)
    """
    return f"The weather in {location} is 22 {unit}"

@tool("get_forecast")
def get_forecast_function(location: str, days: int = 1) -> str:
    """Get a weather forecast for a location.

    Args:
        location: The city to get the forecast for
        days: Number of days to forecast (1-5)
    """
    return f"{days}-day forecast for {location}: Sunny"
```

2. **Use the tools with OpenAI**:

```python
# Initialize the client with your API key
client = OpenAIClient(api_key="your-api-key")

# Use the client
response = client.chat_with_tools(
    messages=[{"role": "user", "content": "What's the weather in Toronto?"}]
)
print(response)
```

## Documentation

For detailed documentation, including API reference and examples, please visit:

📚 [Documentation](https://bemade.github.io/openai-toolchain/)

Or run the documentation locally:

```bash
pip install -e ".[docs]"
mkdocs serve
```

## Contributing

Contributions are welcome! Please see our [Contributing Guide](https://bemade.github.io/openai-toolchain/CONTRIBUTING/)
for details.

### Pre-commit Hooks

This project uses pre-commit to ensure code quality and style consistency. To
set it up:

1. Install pre-commit:

   ```bash
   pip install pre-commit
   ```

2. Install the git hook scripts:

   ```bash
   pre-commit install
   ```

3. (Optional) Run against all files:
   ```bash
   pre-commit run --all-files
   ```

The hooks will now run automatically on every commit. To skip the pre-commit
checks, use:

```bash
git commit --no-verify -m "Your commit message"
```

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/Bemade/openai-toolchain/blob/main/LICENSE) file
for details.

# Initialize the client with your API key

client = OpenAIClient(api_key="your-api-key")

# Chat with automatic tool calling

response = client.chat_with_tools( messages=[{"role": "user", "content": "What's
the weather in Toronto?"}], tools=["get_weather"] # Optional: specify which
tools to use )

print(response)

````

## Features

### 1. Tool Registration

Use the `@tool` decorator to register functions as tools:

```python
from openai_toolchain import tool

@tool
def search_web(query: str) -> str:
    """Search the web for information."""
    return f"Search results for: {query}"
````

### 2. Chat with Automatic Tool Calling

The `chat_with_tools` method handles tool calls automatically:

```python
client = OpenAIClient(api_key="your-api-key")

response = client.chat_with_tools(
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Search for the latest Python news"}
    ],
    tools=["search_web"],
    model="gpt-4"  # Optional: specify a different model
)
```

### 3. Accessing Registered Tools

You can access registered tools directly:

```python
from openai_toolchain import tool_registry

# Get all registered tools
tools = tool_registry.get_openai_tools()

# Call a tool directly
result = tool_registry.call_tool("get_weather", {"location": "Paris", "unit": "fahrenheit"})
```

## API Reference

### `@tool` decorator

Register a function as a tool:

```python
from openai_toolchain import tool

# Basic usage
@tool
def my_function(param: str) -> str:
    """Function documentation."""
    return f"Result for {param}"

# With non-AI parameters
@tool(non_ai_params=["db_connection"])
def get_data(query: str, db_connection: Database) -> str:
    """Get data from the database.
    
    Args:
        query: The search query
        db_connection: Database connection (handled by the system, not AI)
    """
    return db_connection.execute(query)
```

#### Non-AI Parameters

You can mark certain parameters as non-AI parameters, which means they will be provided by the system rather than the AI. This is useful for passing in dependencies like database connections, configuration, or other runtime objects.

1. Specify non-AI parameters using the `non_ai_params` argument in the `@tool` decorator
2. These parameters will be excluded from the AI's schema
3. You must provide these parameters when calling `chat_with_tools` using the `tool_params` argument

Example usage with `chat_with_tools`:

```python
# Initialize client and dependencies
client = OpenAIClient(api_key="your-api-key")
db = DatabaseConnection()

# Call with non-AI parameters
response = client.chat_with_tools(
    messages=[{"role": "user", "content": "Get me user data for John"}],
    tools=["get_data"],
    tool_params={
        "get_data": {
            "db_connection": db
        }
    }
)
```

### `tool_registry`

The global registry instance with these methods:

- `register(func, **kwargs)`: Register a function as a tool
- `get_tool(name)`: Get a registered tool by name
- `call_tool(name, arguments)`: Call a registered tool by name with arguments
- `get_openai_tools()`: Get all tools in OpenAI format

## Development

```bash
# Install in development mode
pip install -e ".[dev]"

# Run tests
pytest
```

## License

MIT
