zipcoil


Namezipcoil JSON
Version 0.2.1 PyPI version JSON
download
home_pageNone
SummaryZipcoil simplifies OpenAI tool usage
upload_time2025-08-21 11:49:19
maintainerNone
docs_urlNone
authorVlad Iliescu
requires_python>=3.11
licenseNone
keywords openai tools ai
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Zipcoil

**Zipcoil** is a Python library that simplifies OpenAI tool usage, helping developers build simple AI agents with ease. It provides a clean, decorator-based approach to define tools and an `Agent` class that handles the OpenAI tool-calling loop automatically.

## Why Zipcoil?

Building AI agents that can use tools typically involves:
- Converting Python functions to OpenAI's JSON schema format 😕
- Handling the complex tool-calling conversation flow 🙁
- Managing multiple iterations of tool calls and responses ☹️
- Dealing with error handling and edge cases 😣

Zipcoil eliminates this boilerplate by providing:
- A **simple `@tool` decorator** to help convert Python functions into OpenAI tools
- **Automatic schema generation** from type hints and docstrings
- **Built-in agent loop** that handles tool calling iterations
- **Type safety** with comprehensive type hints including Optional, Union, Enum, and more
- **Error handling** for malformed tool calls and execution errors
- **Minimal dependencies**, built on top of the official OpenAI library
- Works with both `OpenAI` and `AzureOpenAI` clients, in both `sync` and `async` modes

## Installation

Zipcoil requires Python 3.11 or higher.

```bash
pip install zipcoil
```

## Quick Start

Here's a simple example of creating an AI agent with tools:

```python
import os
from enum import Enum

from openai import AzureOpenAI, OpenAI

from zipcoil import Agent, tool

# Initialize OpenAI client
# client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# or the Azure OpenAI client
client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    azure_endpoint=os.getenv("AZURE_OPENAI_API_BASE"),
    api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
)


# Define tools using the @tool decorator
@tool
def get_weather(city: str, unit: str = "celsius") -> str:
    """Get the current weather for a city.

    Args:
        city: The name of the city
        unit: Temperature unit (celsius or fahrenheit)
    """
    # Your weather API call here
    return f"The weather in {city} is 22°{unit[0].upper()}"


class MathOp(Enum):
    ADD = 1
    SUBTRACT = 2
    MULTIPLY = 3
    DIVIDE = 4


@tool
def calculate(x: float, y: float, operation: MathOp) -> float:
    """Perform a mathematical calculation.

    Args:
        x: First number
        y: Second number
        operation: Operation to perform (add, subtract, multiply, divide)
    """
    # normalise int -> MathOp
    if isinstance(operation, int):
        try:
            operation = MathOp(operation)
        except ValueError as exc:
            raise ValueError(f"Unsupported operation value: {operation}") from exc

    operations = {
        MathOp.ADD: x + y,
        MathOp.SUBTRACT: x - y,
        MathOp.MULTIPLY: x * y,
        MathOp.DIVIDE: x / y if y != 0 else float("inf"),
    }
    return operations.get(operation, 0)


# Create an agent with tools
agent = Agent(model="gpt-4o", client=client, tools=[get_weather, calculate])

# Run a conversation
messages = [{"role": "user", "content": "What's the weather in Paris? Also calculate 15 * 23."}]

result = agent.run(messages)
print(result.choices[0].message.content)

```

## Advanced Usage

### Complex Type Support

Zipcoil supports various Python types including enums, optionals, and unions:

```python
from enum import Enum
from typing import Optional, List, Dict

class Priority(Enum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

@tool
def create_task(
    title: str,
    description: Optional[str],
    priority: Priority,
    tags: List[str],
    metadata: Dict[str, str]
) -> str:
    """Create a new task.

    Args:
        title: Task title
        description: Optional task description
        priority: Task priority level
        tags: List of tags for the task
        metadata: Additional metadata as key-value pairs
    """
    return f"Created task '{title}' with priority {priority.name}"
```

### Error Handling

Zipcoil automatically handles tool execution errors:

```python
@tool
def divide_numbers(a: float, b: float) -> float:
    """Divide two numbers.

    Args:
        a: Numerator
        b: Denominator
    """
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

# The agent will catch the error and include it in the conversation
```

### Custom Agent Configuration

You can pass additional parameters to the underlying OpenAI API:

```python
result = agent.run(
    messages=messages,
    temperature=0.7,
    max_completion_tokens=1000,
    max_iterations=5  # Limit tool calling iterations
)
```


## Type Support

Zipcoil automatically converts Python types to OpenAI's JSON schema:

| Python Type | JSON Schema Type | Notes |
|-------------|------------------|--------|
| `str` | `string` | |
| `int` | `integer` | |
| `float` | `number` | |
| `bool` | `boolean` | |
| `list` | `array` | |
| `dict` | `object` | |
| `Optional[T]` | `[T, "null"]` | Union with null |
| `Union[T, U]` | Mixed type | For Optional types |
| `Enum` | `enum` | Extracts enum values |

## API Reference

### `@tool` Decorator

Converts a Python function into an OpenAI tool. The function should:
- Have type hints for all parameters
- Have a docstring with Google-style Args section

Example:

```python
@tool
def get_weather(city: str, unit: str = "celsius") -> str:
    """Get the current weather for a city.

    Args:
        city: The name of the city
        unit: Temperature unit (celsius or fahrenheit)
    """
    return f"The weather in {city} is 22°{unit[0].upper()}"
```

### `Agent` and `AsyncAgent` Classes

```python
Agent(
    model: Uniont[str, ChatModel],
    client: OpenAI,
    tools: Iterable[ToolProtocol]
)

AsyncAgent(
    model: Union[str, ChatModel],
    client: AsyncOpenAI,
    tools: Iterable[Union[ToolProtocol, AsyncToolProtocol]]
)
```

The main abstraction of the agentic event loop. It will take in a model name (more on this below), an OpenAI or AzureOpenAI client, and a list of tools decorated with the `@tool` decorator.

While `Agent` will accept only sync tools and will reject async ones, `AsyncAgent` will accept both sync and async tools and will `await` async tools. Use `@tool` for both sync and async functions.

Note: As opposed to standard OpenAI usage, Zipcoil associates the model with an agent to avoid having to specify it every time you call `run`.

#### `Agent.run()`

Runs the agentic loop, calling all tools as needed and iterating until the underlying model doesn't need to call any tools anymore, until it's ready to return a ChatCompletion.

**Parameters:**
- `max_iterations`: Maximum number of tool calling iterations (default: 10)
- All other parameters are passed through to OpenAI's chat completion API
- Returns the standard OpenAI [ChatCompletion](https://platform.openai.com/docs/api-reference/chat/object) object

## Error Handling

Zipcoil handles several types of errors gracefully:

1. **Tool execution errors**: Caught and passed back to the model as error messages
2. **JSON parsing errors**: Invalid tool arguments are reported to the model
3. **Missing tools**: Requests for non-existent tools return error messages
4. **Iteration limits**: Prevents infinite loops with configurable max iterations

## Contributing

Contributions are welcome! Please see our development setup:

```bash
# Install development dependencies
uv sync --group dev

# Run tests
uv run pytest

# Format code
uv run ruff format src/ tests/
```

## Requirements

- Python 3.11+
- OpenAI Python library (≥1.0.0)
- docstring-parser (≥0.16)

## License

This project is open-source, licensed under the GNU Lesser General Public License v3.0 - see the [LICENSE](LICENSE) file for details.

---

**Zipcoil** - Making AI tool usage as simple as decorating a function.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "zipcoil",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "openai, tools, ai",
    "author": "Vlad Iliescu",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/79/ef/97b552ee96af233b8c6abcc6d6b72bd8ad168e31b795940b60b6e95a6827/zipcoil-0.2.1.tar.gz",
    "platform": null,
    "description": "# Zipcoil\n\n**Zipcoil** is a Python library that simplifies OpenAI tool usage, helping developers build simple AI agents with ease. It provides a clean, decorator-based approach to define tools and an `Agent` class that handles the OpenAI tool-calling loop automatically.\n\n## Why Zipcoil?\n\nBuilding AI agents that can use tools typically involves:\n- Converting Python functions to OpenAI's JSON schema format \ud83d\ude15\n- Handling the complex tool-calling conversation flow \ud83d\ude41\n- Managing multiple iterations of tool calls and responses \u2639\ufe0f\n- Dealing with error handling and edge cases \ud83d\ude23\n\nZipcoil eliminates this boilerplate by providing:\n- A **simple `@tool` decorator** to help convert Python functions into OpenAI tools\n- **Automatic schema generation** from type hints and docstrings\n- **Built-in agent loop** that handles tool calling iterations\n- **Type safety** with comprehensive type hints including Optional, Union, Enum, and more\n- **Error handling** for malformed tool calls and execution errors\n- **Minimal dependencies**, built on top of the official OpenAI library\n- Works with both `OpenAI` and `AzureOpenAI` clients, in both `sync` and `async` modes\n\n## Installation\n\nZipcoil requires Python 3.11 or higher.\n\n```bash\npip install zipcoil\n```\n\n## Quick Start\n\nHere's a simple example of creating an AI agent with tools:\n\n```python\nimport os\nfrom enum import Enum\n\nfrom openai import AzureOpenAI, OpenAI\n\nfrom zipcoil import Agent, tool\n\n# Initialize OpenAI client\n# client = OpenAI(api_key=os.getenv(\"OPENAI_API_KEY\"))\n# or the Azure OpenAI client\nclient = AzureOpenAI(\n    api_key=os.getenv(\"AZURE_OPENAI_API_KEY\"),\n    azure_endpoint=os.getenv(\"AZURE_OPENAI_API_BASE\"),\n    api_version=os.getenv(\"AZURE_OPENAI_API_VERSION\"),\n)\n\n\n# Define tools using the @tool decorator\n@tool\ndef get_weather(city: str, unit: str = \"celsius\") -> str:\n    \"\"\"Get the current weather for a city.\n\n    Args:\n        city: The name of the city\n        unit: Temperature unit (celsius or fahrenheit)\n    \"\"\"\n    # Your weather API call here\n    return f\"The weather in {city} is 22\u00b0{unit[0].upper()}\"\n\n\nclass MathOp(Enum):\n    ADD = 1\n    SUBTRACT = 2\n    MULTIPLY = 3\n    DIVIDE = 4\n\n\n@tool\ndef calculate(x: float, y: float, operation: MathOp) -> float:\n    \"\"\"Perform a mathematical calculation.\n\n    Args:\n        x: First number\n        y: Second number\n        operation: Operation to perform (add, subtract, multiply, divide)\n    \"\"\"\n    # normalise int -> MathOp\n    if isinstance(operation, int):\n        try:\n            operation = MathOp(operation)\n        except ValueError as exc:\n            raise ValueError(f\"Unsupported operation value: {operation}\") from exc\n\n    operations = {\n        MathOp.ADD: x + y,\n        MathOp.SUBTRACT: x - y,\n        MathOp.MULTIPLY: x * y,\n        MathOp.DIVIDE: x / y if y != 0 else float(\"inf\"),\n    }\n    return operations.get(operation, 0)\n\n\n# Create an agent with tools\nagent = Agent(model=\"gpt-4o\", client=client, tools=[get_weather, calculate])\n\n# Run a conversation\nmessages = [{\"role\": \"user\", \"content\": \"What's the weather in Paris? Also calculate 15 * 23.\"}]\n\nresult = agent.run(messages)\nprint(result.choices[0].message.content)\n\n```\n\n## Advanced Usage\n\n### Complex Type Support\n\nZipcoil supports various Python types including enums, optionals, and unions:\n\n```python\nfrom enum import Enum\nfrom typing import Optional, List, Dict\n\nclass Priority(Enum):\n    LOW = 1\n    MEDIUM = 2\n    HIGH = 3\n\n@tool\ndef create_task(\n    title: str,\n    description: Optional[str],\n    priority: Priority,\n    tags: List[str],\n    metadata: Dict[str, str]\n) -> str:\n    \"\"\"Create a new task.\n\n    Args:\n        title: Task title\n        description: Optional task description\n        priority: Task priority level\n        tags: List of tags for the task\n        metadata: Additional metadata as key-value pairs\n    \"\"\"\n    return f\"Created task '{title}' with priority {priority.name}\"\n```\n\n### Error Handling\n\nZipcoil automatically handles tool execution errors:\n\n```python\n@tool\ndef divide_numbers(a: float, b: float) -> float:\n    \"\"\"Divide two numbers.\n\n    Args:\n        a: Numerator\n        b: Denominator\n    \"\"\"\n    if b == 0:\n        raise ValueError(\"Cannot divide by zero\")\n    return a / b\n\n# The agent will catch the error and include it in the conversation\n```\n\n### Custom Agent Configuration\n\nYou can pass additional parameters to the underlying OpenAI API:\n\n```python\nresult = agent.run(\n    messages=messages,\n    temperature=0.7,\n    max_completion_tokens=1000,\n    max_iterations=5  # Limit tool calling iterations\n)\n```\n\n\n## Type Support\n\nZipcoil automatically converts Python types to OpenAI's JSON schema:\n\n| Python Type | JSON Schema Type | Notes |\n|-------------|------------------|--------|\n| `str` | `string` | |\n| `int` | `integer` | |\n| `float` | `number` | |\n| `bool` | `boolean` | |\n| `list` | `array` | |\n| `dict` | `object` | |\n| `Optional[T]` | `[T, \"null\"]` | Union with null |\n| `Union[T, U]` | Mixed type | For Optional types |\n| `Enum` | `enum` | Extracts enum values |\n\n## API Reference\n\n### `@tool` Decorator\n\nConverts a Python function into an OpenAI tool. The function should:\n- Have type hints for all parameters\n- Have a docstring with Google-style Args section\n\nExample:\n\n```python\n@tool\ndef get_weather(city: str, unit: str = \"celsius\") -> str:\n    \"\"\"Get the current weather for a city.\n\n    Args:\n        city: The name of the city\n        unit: Temperature unit (celsius or fahrenheit)\n    \"\"\"\n    return f\"The weather in {city} is 22\u00b0{unit[0].upper()}\"\n```\n\n### `Agent` and `AsyncAgent` Classes\n\n```python\nAgent(\n    model: Uniont[str, ChatModel],\n    client: OpenAI,\n    tools: Iterable[ToolProtocol]\n)\n\nAsyncAgent(\n    model: Union[str, ChatModel],\n    client: AsyncOpenAI,\n    tools: Iterable[Union[ToolProtocol, AsyncToolProtocol]]\n)\n```\n\nThe main abstraction of the agentic event loop. It will take in a model name (more on this below), an OpenAI or AzureOpenAI client, and a list of tools decorated with the `@tool` decorator.\n\nWhile `Agent` will accept only sync tools and will reject async ones, `AsyncAgent` will accept both sync and async tools and will `await` async tools. Use `@tool` for both sync and async functions.\n\nNote: As opposed to standard OpenAI usage, Zipcoil associates the model with an agent to avoid having to specify it every time you call `run`.\n\n#### `Agent.run()`\n\nRuns the agentic loop, calling all tools as needed and iterating until the underlying model doesn't need to call any tools anymore, until it's ready to return a ChatCompletion.\n\n**Parameters:**\n- `max_iterations`: Maximum number of tool calling iterations (default: 10)\n- All other parameters are passed through to OpenAI's chat completion API\n- Returns the standard OpenAI [ChatCompletion](https://platform.openai.com/docs/api-reference/chat/object) object\n\n## Error Handling\n\nZipcoil handles several types of errors gracefully:\n\n1. **Tool execution errors**: Caught and passed back to the model as error messages\n2. **JSON parsing errors**: Invalid tool arguments are reported to the model\n3. **Missing tools**: Requests for non-existent tools return error messages\n4. **Iteration limits**: Prevents infinite loops with configurable max iterations\n\n## Contributing\n\nContributions are welcome! Please see our development setup:\n\n```bash\n# Install development dependencies\nuv sync --group dev\n\n# Run tests\nuv run pytest\n\n# Format code\nuv run ruff format src/ tests/\n```\n\n## Requirements\n\n- Python 3.11+\n- OpenAI Python library (\u22651.0.0)\n- docstring-parser (\u22650.16)\n\n## License\n\nThis project is open-source, licensed under the GNU Lesser General Public License v3.0 - see the [LICENSE](LICENSE) file for details.\n\n---\n\n**Zipcoil** - Making AI tool usage as simple as decorating a function.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Zipcoil simplifies OpenAI tool usage",
    "version": "0.2.1",
    "project_urls": {
        "Homepage": "https://github.com/vladiliescu/zipcoil",
        "Repository": "https://github.com/vladiliescu/zipcoil"
    },
    "split_keywords": [
        "openai",
        " tools",
        " ai"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "09c80b394a19846399b5241ecee8ac420b0e9cb865b5e17c8fbc0b00ad834273",
                "md5": "de0586a6db703b95576cb0654475fe73",
                "sha256": "dc70b8f9a7a657961cbf865bb919e6692bb27b7ac09044b64a4a2b08cf61ed0e"
            },
            "downloads": -1,
            "filename": "zipcoil-0.2.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "de0586a6db703b95576cb0654475fe73",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 12662,
            "upload_time": "2025-08-21T11:49:18",
            "upload_time_iso_8601": "2025-08-21T11:49:18.428263Z",
            "url": "https://files.pythonhosted.org/packages/09/c8/0b394a19846399b5241ecee8ac420b0e9cb865b5e17c8fbc0b00ad834273/zipcoil-0.2.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "79ef97b552ee96af233b8c6abcc6d6b72bd8ad168e31b795940b60b6e95a6827",
                "md5": "0cc97b36697c01bea338c55d01de5ca2",
                "sha256": "efbda1d44fac7f02362fbf5fe95684654cc1f4d3726d49d00c935898e6f4686d"
            },
            "downloads": -1,
            "filename": "zipcoil-0.2.1.tar.gz",
            "has_sig": false,
            "md5_digest": "0cc97b36697c01bea338c55d01de5ca2",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 18827,
            "upload_time": "2025-08-21T11:49:19",
            "upload_time_iso_8601": "2025-08-21T11:49:19.511049Z",
            "url": "https://files.pythonhosted.org/packages/79/ef/97b552ee96af233b8c6abcc6d6b72bd8ad168e31b795940b60b6e95a6827/zipcoil-0.2.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-21 11:49:19",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "vladiliescu",
    "github_project": "zipcoil",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "zipcoil"
}
        
Elapsed time: 4.02089s