Name | zipcoil JSON |
Version |
0.2.1
JSON |
| download |
home_page | None |
Summary | Zipcoil simplifies OpenAI tool usage |
upload_time | 2025-08-21 11:49:19 |
maintainer | None |
docs_url | None |
author | Vlad Iliescu |
requires_python | >=3.11 |
license | None |
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"
}