Metadata-Version: 2.4
Name: openagentkit
Version: 0.1.0a4
Summary: An open-source framework for building and deploying AI agents.
Home-page: https://github.com/JustKiet/openagentkit
Author: Kiet Do
Author-email: kietdohuu@gmail.com
License: Apache-2.0
Project-URL: Bug Reports, https://github.com/JustKiet/openagentkit/issues
Project-URL: Source, https://github.com/JustKiet/openagentkit
Project-URL: Documentation, https://github.com/JustKiet/openagentkit#readme
Keywords: AI,agents,open-source,llm,tools,executors
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: annotated-types==0.7.0
Requires-Dist: anyio==4.9.0
Requires-Dist: certifi==2025.6.15
Requires-Dist: click==8.2.1
Requires-Dist: h11==0.16.0
Requires-Dist: httpcore==1.0.9
Requires-Dist: httpx==0.28.1
Requires-Dist: httpx-sse==0.4.1
Requires-Dist: idna==3.10
Requires-Dist: markdown-it-py==3.0.0
Requires-Dist: mcp[cli]==1.6.0
Requires-Dist: mdurl==0.1.2
Requires-Dist: numpy==2.2.4
Requires-Dist: pydantic==2.11.3
Requires-Dist: pydantic-core==2.33.1
Requires-Dist: pydantic-settings==2.10.1
Requires-Dist: pygments==2.19.2
Requires-Dist: python-dotenv==1.1.1
Requires-Dist: rich==14.0.0
Requires-Dist: scipy==1.16.0
Requires-Dist: shellingham==1.5.4
Requires-Dist: sniffio==1.3.1
Requires-Dist: sse-starlette==2.3.6
Requires-Dist: starlette==0.47.1
Requires-Dist: typer==0.16.0
Requires-Dist: typing-extensions==4.14.0
Requires-Dist: typing-inspection==0.4.1
Requires-Dist: uvicorn==0.35.0
Provides-Extra: openai
Requires-Dist: annotated-types==0.7.0; extra == "openai"
Requires-Dist: anyio==4.9.0; extra == "openai"
Requires-Dist: certifi==2025.6.15; extra == "openai"
Requires-Dist: charset-normalizer==3.4.2; extra == "openai"
Requires-Dist: click==8.2.1; extra == "openai"
Requires-Dist: distro==1.9.0; extra == "openai"
Requires-Dist: h11==0.16.0; extra == "openai"
Requires-Dist: httpcore==1.0.9; extra == "openai"
Requires-Dist: httpx==0.28.1; extra == "openai"
Requires-Dist: httpx-sse==0.4.1; extra == "openai"
Requires-Dist: idna==3.10; extra == "openai"
Requires-Dist: jiter==0.9.0; extra == "openai"
Requires-Dist: markdown-it-py==3.0.0; extra == "openai"
Requires-Dist: mcp[cli]==1.6.0; extra == "openai"
Requires-Dist: mdurl==0.1.2; extra == "openai"
Requires-Dist: numpy==2.2.4; extra == "openai"
Requires-Dist: openai==1.79.0; extra == "openai"
Requires-Dist: pydantic==2.11.3; extra == "openai"
Requires-Dist: pydantic-core==2.33.1; extra == "openai"
Requires-Dist: pydantic-settings==2.10.1; extra == "openai"
Requires-Dist: pygments==2.19.2; extra == "openai"
Requires-Dist: python-dotenv==1.1.1; extra == "openai"
Requires-Dist: regex==2024.11.6; extra == "openai"
Requires-Dist: requests==2.32.3; extra == "openai"
Requires-Dist: rich==14.0.0; extra == "openai"
Requires-Dist: scipy==1.16.0; extra == "openai"
Requires-Dist: shellingham==1.5.4; extra == "openai"
Requires-Dist: sniffio==1.3.1; extra == "openai"
Requires-Dist: sse-starlette==2.3.6; extra == "openai"
Requires-Dist: starlette==0.47.1; extra == "openai"
Requires-Dist: tiktoken==0.9.0; extra == "openai"
Requires-Dist: tqdm==4.67.1; extra == "openai"
Requires-Dist: typer==0.16.0; extra == "openai"
Requires-Dist: typing-extensions==4.14.0; extra == "openai"
Requires-Dist: typing-inspection==0.4.1; extra == "openai"
Requires-Dist: urllib3==2.4.0; extra == "openai"
Requires-Dist: uvicorn==0.35.0; extra == "openai"
Provides-Extra: milvus
Requires-Dist: annotated-types==0.7.0; extra == "milvus"
Requires-Dist: anyio==4.9.0; extra == "milvus"
Requires-Dist: certifi==2025.6.15; extra == "milvus"
Requires-Dist: click==8.2.1; extra == "milvus"
Requires-Dist: grpcio==1.67.1; extra == "milvus"
Requires-Dist: h11==0.16.0; extra == "milvus"
Requires-Dist: httpcore==1.0.9; extra == "milvus"
Requires-Dist: httpx==0.28.1; extra == "milvus"
Requires-Dist: httpx-sse==0.4.1; extra == "milvus"
Requires-Dist: idna==3.10; extra == "milvus"
Requires-Dist: markdown-it-py==3.0.0; extra == "milvus"
Requires-Dist: mcp[cli]==1.6.0; extra == "milvus"
Requires-Dist: mdurl==0.1.2; extra == "milvus"
Requires-Dist: milvus-lite==2.4.12; extra == "milvus"
Requires-Dist: numpy==2.2.4; extra == "milvus"
Requires-Dist: pandas==2.2.3; extra == "milvus"
Requires-Dist: protobuf==6.31.0; extra == "milvus"
Requires-Dist: pydantic==2.11.3; extra == "milvus"
Requires-Dist: pydantic-core==2.33.1; extra == "milvus"
Requires-Dist: pydantic-settings==2.10.1; extra == "milvus"
Requires-Dist: pygments==2.19.2; extra == "milvus"
Requires-Dist: pymilvus==2.5.8; extra == "milvus"
Requires-Dist: python-dateutil==2.9.0.post0; extra == "milvus"
Requires-Dist: python-dotenv==1.1.1; extra == "milvus"
Requires-Dist: pytz==2025.2; extra == "milvus"
Requires-Dist: rich==14.0.0; extra == "milvus"
Requires-Dist: scipy==1.16.0; extra == "milvus"
Requires-Dist: shellingham==1.5.4; extra == "milvus"
Requires-Dist: six==1.17.0; extra == "milvus"
Requires-Dist: sniffio==1.3.1; extra == "milvus"
Requires-Dist: sse-starlette==2.3.6; extra == "milvus"
Requires-Dist: starlette==0.47.1; extra == "milvus"
Requires-Dist: tqdm==4.67.1; extra == "milvus"
Requires-Dist: typer==0.16.0; extra == "milvus"
Requires-Dist: typing-extensions==4.14.0; extra == "milvus"
Requires-Dist: typing-inspection==0.4.1; extra == "milvus"
Requires-Dist: tzdata==2025.2; extra == "milvus"
Requires-Dist: ujson==5.10.0; extra == "milvus"
Requires-Dist: uvicorn==0.35.0; extra == "milvus"
Provides-Extra: voyageai
Requires-Dist: aiohappyeyeballs==2.6.1; extra == "voyageai"
Requires-Dist: aiohttp==3.11.18; extra == "voyageai"
Requires-Dist: aiolimiter==1.2.1; extra == "voyageai"
Requires-Dist: aiosignal==1.3.2; extra == "voyageai"
Requires-Dist: annotated-types==0.7.0; extra == "voyageai"
Requires-Dist: anyio==4.9.0; extra == "voyageai"
Requires-Dist: attrs==25.3.0; extra == "voyageai"
Requires-Dist: certifi==2025.6.15; extra == "voyageai"
Requires-Dist: charset-normalizer==3.4.2; extra == "voyageai"
Requires-Dist: click==8.2.1; extra == "voyageai"
Requires-Dist: filelock==3.18.0; extra == "voyageai"
Requires-Dist: frozenlist==1.6.0; extra == "voyageai"
Requires-Dist: fsspec==2025.3.2; extra == "voyageai"
Requires-Dist: h11==0.16.0; extra == "voyageai"
Requires-Dist: httpcore==1.0.9; extra == "voyageai"
Requires-Dist: httpx==0.28.1; extra == "voyageai"
Requires-Dist: httpx-sse==0.4.1; extra == "voyageai"
Requires-Dist: huggingface-hub==0.31.2; extra == "voyageai"
Requires-Dist: idna==3.10; extra == "voyageai"
Requires-Dist: markdown-it-py==3.0.0; extra == "voyageai"
Requires-Dist: mcp[cli]==1.6.0; extra == "voyageai"
Requires-Dist: mdurl==0.1.2; extra == "voyageai"
Requires-Dist: multidict==6.4.3; extra == "voyageai"
Requires-Dist: numpy==2.2.4; extra == "voyageai"
Requires-Dist: packaging==25.0; extra == "voyageai"
Requires-Dist: pillow==11.2.1; extra == "voyageai"
Requires-Dist: propcache==0.3.1; extra == "voyageai"
Requires-Dist: pydantic==2.11.3; extra == "voyageai"
Requires-Dist: pydantic-core==2.33.1; extra == "voyageai"
Requires-Dist: pydantic-settings==2.10.1; extra == "voyageai"
Requires-Dist: pygments==2.19.2; extra == "voyageai"
Requires-Dist: python-dotenv==1.1.1; extra == "voyageai"
Requires-Dist: pyyaml==6.0.2; extra == "voyageai"
Requires-Dist: requests==2.32.3; extra == "voyageai"
Requires-Dist: rich==14.0.0; extra == "voyageai"
Requires-Dist: scipy==1.16.0; extra == "voyageai"
Requires-Dist: shellingham==1.5.4; extra == "voyageai"
Requires-Dist: sniffio==1.3.1; extra == "voyageai"
Requires-Dist: sse-starlette==2.3.6; extra == "voyageai"
Requires-Dist: starlette==0.47.1; extra == "voyageai"
Requires-Dist: tenacity==9.1.2; extra == "voyageai"
Requires-Dist: tokenizers==0.21.1; extra == "voyageai"
Requires-Dist: tqdm==4.67.1; extra == "voyageai"
Requires-Dist: typer==0.16.0; extra == "voyageai"
Requires-Dist: typing-extensions==4.14.0; extra == "voyageai"
Requires-Dist: typing-inspection==0.4.1; extra == "voyageai"
Requires-Dist: urllib3==2.4.0; extra == "voyageai"
Requires-Dist: uvicorn==0.35.0; extra == "voyageai"
Requires-Dist: voyageai==0.3.2; extra == "voyageai"
Requires-Dist: yarl==1.20.0; extra == "voyageai"
Provides-Extra: redis
Requires-Dist: annotated-types==0.7.0; extra == "redis"
Requires-Dist: anyio==4.9.0; extra == "redis"
Requires-Dist: certifi==2025.6.15; extra == "redis"
Requires-Dist: click==8.2.1; extra == "redis"
Requires-Dist: h11==0.16.0; extra == "redis"
Requires-Dist: httpcore==1.0.9; extra == "redis"
Requires-Dist: httpx==0.28.1; extra == "redis"
Requires-Dist: httpx-sse==0.4.1; extra == "redis"
Requires-Dist: idna==3.10; extra == "redis"
Requires-Dist: markdown-it-py==3.0.0; extra == "redis"
Requires-Dist: mcp[cli]==1.6.0; extra == "redis"
Requires-Dist: mdurl==0.1.2; extra == "redis"
Requires-Dist: numpy==2.2.4; extra == "redis"
Requires-Dist: pydantic==2.11.3; extra == "redis"
Requires-Dist: pydantic-core==2.33.1; extra == "redis"
Requires-Dist: pydantic-settings==2.10.1; extra == "redis"
Requires-Dist: pygments==2.19.2; extra == "redis"
Requires-Dist: python-dotenv==1.1.1; extra == "redis"
Requires-Dist: redis==6.2.0; extra == "redis"
Requires-Dist: rich==14.0.0; extra == "redis"
Requires-Dist: scipy==1.16.0; extra == "redis"
Requires-Dist: shellingham==1.5.4; extra == "redis"
Requires-Dist: sniffio==1.3.1; extra == "redis"
Requires-Dist: sse-starlette==2.3.6; extra == "redis"
Requires-Dist: starlette==0.47.1; extra == "redis"
Requires-Dist: typer==0.16.0; extra == "redis"
Requires-Dist: typing-extensions==4.14.0; extra == "redis"
Requires-Dist: typing-inspection==0.4.1; extra == "redis"
Requires-Dist: uvicorn==0.35.0; extra == "redis"
Provides-Extra: valkey
Requires-Dist: annotated-types==0.7.0; extra == "valkey"
Requires-Dist: anyio==4.9.0; extra == "valkey"
Requires-Dist: certifi==2025.6.15; extra == "valkey"
Requires-Dist: click==8.2.1; extra == "valkey"
Requires-Dist: h11==0.16.0; extra == "valkey"
Requires-Dist: httpcore==1.0.9; extra == "valkey"
Requires-Dist: httpx==0.28.1; extra == "valkey"
Requires-Dist: httpx-sse==0.4.1; extra == "valkey"
Requires-Dist: idna==3.10; extra == "valkey"
Requires-Dist: markdown-it-py==3.0.0; extra == "valkey"
Requires-Dist: mcp[cli]==1.6.0; extra == "valkey"
Requires-Dist: mdurl==0.1.2; extra == "valkey"
Requires-Dist: numpy==2.2.4; extra == "valkey"
Requires-Dist: pydantic==2.11.3; extra == "valkey"
Requires-Dist: pydantic-core==2.33.1; extra == "valkey"
Requires-Dist: pydantic-settings==2.10.1; extra == "valkey"
Requires-Dist: pygments==2.19.2; extra == "valkey"
Requires-Dist: python-dotenv==1.1.1; extra == "valkey"
Requires-Dist: rich==14.0.0; extra == "valkey"
Requires-Dist: scipy==1.16.0; extra == "valkey"
Requires-Dist: shellingham==1.5.4; extra == "valkey"
Requires-Dist: sniffio==1.3.1; extra == "valkey"
Requires-Dist: sse-starlette==2.3.6; extra == "valkey"
Requires-Dist: starlette==0.47.1; extra == "valkey"
Requires-Dist: typer==0.16.0; extra == "valkey"
Requires-Dist: typing-extensions==4.14.0; extra == "valkey"
Requires-Dist: typing-inspection==0.4.1; extra == "valkey"
Requires-Dist: uvicorn==0.35.0; extra == "valkey"
Requires-Dist: valkey==6.1.0; extra == "valkey"
Provides-Extra: dev
Requires-Dist: aiohappyeyeballs==2.6.1; extra == "dev"
Requires-Dist: aiohttp==3.12.13; extra == "dev"
Requires-Dist: aiolimiter==1.2.1; extra == "dev"
Requires-Dist: aiosignal==1.4.0; extra == "dev"
Requires-Dist: annotated-types==0.7.0; extra == "dev"
Requires-Dist: anyio==4.9.0; extra == "dev"
Requires-Dist: attrs==25.3.0; extra == "dev"
Requires-Dist: build==1.2.2.post1; extra == "dev"
Requires-Dist: certifi==2025.6.15; extra == "dev"
Requires-Dist: charset-normalizer==3.4.2; extra == "dev"
Requires-Dist: click==8.2.1; extra == "dev"
Requires-Dist: distro==1.9.0; extra == "dev"
Requires-Dist: filelock==3.18.0; extra == "dev"
Requires-Dist: frozenlist==1.7.0; extra == "dev"
Requires-Dist: fsspec==2025.5.1; extra == "dev"
Requires-Dist: grpcio==1.67.1; extra == "dev"
Requires-Dist: h11==0.16.0; extra == "dev"
Requires-Dist: hf-xet==1.1.5; extra == "dev"
Requires-Dist: httpcore==1.0.9; extra == "dev"
Requires-Dist: httpx==0.28.1; extra == "dev"
Requires-Dist: httpx-sse==0.4.1; extra == "dev"
Requires-Dist: huggingface-hub==0.33.2; extra == "dev"
Requires-Dist: idna==3.10; extra == "dev"
Requires-Dist: jiter==0.10.0; extra == "dev"
Requires-Dist: markdown-it-py==3.0.0; extra == "dev"
Requires-Dist: mcp[cli]==1.6.0; extra == "dev"
Requires-Dist: mdurl==0.1.2; extra == "dev"
Requires-Dist: milvus-lite==2.5.1; extra == "dev"
Requires-Dist: multidict==6.6.3; extra == "dev"
Requires-Dist: numpy==2.2.4; extra == "dev"
Requires-Dist: openai==1.93.0; extra == "dev"
Requires-Dist: packaging==25.0; extra == "dev"
Requires-Dist: pandas==2.3.0; extra == "dev"
Requires-Dist: pillow==11.3.0; extra == "dev"
Requires-Dist: pip-tools==7.4.1; extra == "dev"
Requires-Dist: propcache==0.3.2; extra == "dev"
Requires-Dist: protobuf==6.31.1; extra == "dev"
Requires-Dist: pydantic==2.11.3; extra == "dev"
Requires-Dist: pydantic-core==2.33.1; extra == "dev"
Requires-Dist: pydantic-settings==2.10.1; extra == "dev"
Requires-Dist: pygments==2.19.2; extra == "dev"
Requires-Dist: pymilvus==2.5.12; extra == "dev"
Requires-Dist: pyproject-hooks==1.2.0; extra == "dev"
Requires-Dist: python-dateutil==2.9.0.post0; extra == "dev"
Requires-Dist: python-dotenv==1.1.1; extra == "dev"
Requires-Dist: pytz==2025.2; extra == "dev"
Requires-Dist: pyyaml==6.0.2; extra == "dev"
Requires-Dist: redis==6.2.0; extra == "dev"
Requires-Dist: regex==2024.11.6; extra == "dev"
Requires-Dist: requests==2.32.4; extra == "dev"
Requires-Dist: rich==14.0.0; extra == "dev"
Requires-Dist: scipy==1.16.0; extra == "dev"
Requires-Dist: shellingham==1.5.4; extra == "dev"
Requires-Dist: six==1.17.0; extra == "dev"
Requires-Dist: sniffio==1.3.1; extra == "dev"
Requires-Dist: sse-starlette==2.3.6; extra == "dev"
Requires-Dist: starlette==0.47.1; extra == "dev"
Requires-Dist: tenacity==9.1.2; extra == "dev"
Requires-Dist: tiktoken==0.9.0; extra == "dev"
Requires-Dist: tokenizers==0.21.2; extra == "dev"
Requires-Dist: tqdm==4.67.1; extra == "dev"
Requires-Dist: typer==0.16.0; extra == "dev"
Requires-Dist: typing-extensions==4.14.0; extra == "dev"
Requires-Dist: typing-inspection==0.4.1; extra == "dev"
Requires-Dist: tzdata==2025.2; extra == "dev"
Requires-Dist: ujson==5.10.0; extra == "dev"
Requires-Dist: urllib3==2.5.0; extra == "dev"
Requires-Dist: uvicorn==0.35.0; extra == "dev"
Requires-Dist: valkey==6.1.0; extra == "dev"
Requires-Dist: voyageai==0.3.3; extra == "dev"
Requires-Dist: wheel==0.45.1; extra == "dev"
Requires-Dist: yarl==1.20.1; extra == "dev"
Provides-Extra: all
Requires-Dist: aiohappyeyeballs==2.6.1; extra == "all"
Requires-Dist: aiohttp==3.11.18; extra == "all"
Requires-Dist: aiolimiter==1.2.1; extra == "all"
Requires-Dist: aiosignal==1.3.2; extra == "all"
Requires-Dist: annotated-types==0.7.0; extra == "all"
Requires-Dist: anyio==4.9.0; extra == "all"
Requires-Dist: attrs==25.3.0; extra == "all"
Requires-Dist: certifi==2025.6.15; extra == "all"
Requires-Dist: charset-normalizer==3.4.2; extra == "all"
Requires-Dist: click==8.2.1; extra == "all"
Requires-Dist: distro==1.9.0; extra == "all"
Requires-Dist: filelock==3.18.0; extra == "all"
Requires-Dist: frozenlist==1.6.0; extra == "all"
Requires-Dist: fsspec==2025.3.2; extra == "all"
Requires-Dist: grpcio==1.67.1; extra == "all"
Requires-Dist: h11==0.16.0; extra == "all"
Requires-Dist: httpcore==1.0.9; extra == "all"
Requires-Dist: httpx-sse==0.4.1; extra == "all"
Requires-Dist: httpx==0.28.1; extra == "all"
Requires-Dist: huggingface-hub==0.31.2; extra == "all"
Requires-Dist: idna==3.10; extra == "all"
Requires-Dist: jiter==0.9.0; extra == "all"
Requires-Dist: markdown-it-py==3.0.0; extra == "all"
Requires-Dist: mcp[cli]==1.6.0; extra == "all"
Requires-Dist: mdurl==0.1.2; extra == "all"
Requires-Dist: milvus-lite==2.4.12; extra == "all"
Requires-Dist: multidict==6.4.3; extra == "all"
Requires-Dist: numpy==2.2.4; extra == "all"
Requires-Dist: openai==1.79.0; extra == "all"
Requires-Dist: packaging==25.0; extra == "all"
Requires-Dist: pandas==2.2.3; extra == "all"
Requires-Dist: pillow==11.2.1; extra == "all"
Requires-Dist: propcache==0.3.1; extra == "all"
Requires-Dist: protobuf==6.31.0; extra == "all"
Requires-Dist: pydantic-core==2.33.1; extra == "all"
Requires-Dist: pydantic-settings==2.10.1; extra == "all"
Requires-Dist: pydantic==2.11.3; extra == "all"
Requires-Dist: pygments==2.19.2; extra == "all"
Requires-Dist: pymilvus==2.5.8; extra == "all"
Requires-Dist: python-dateutil==2.9.0.post0; extra == "all"
Requires-Dist: python-dotenv==1.1.1; extra == "all"
Requires-Dist: pytz==2025.2; extra == "all"
Requires-Dist: pyyaml==6.0.2; extra == "all"
Requires-Dist: regex==2024.11.6; extra == "all"
Requires-Dist: requests==2.32.3; extra == "all"
Requires-Dist: rich==14.0.0; extra == "all"
Requires-Dist: scipy==1.16.0; extra == "all"
Requires-Dist: shellingham==1.5.4; extra == "all"
Requires-Dist: six==1.17.0; extra == "all"
Requires-Dist: sniffio==1.3.1; extra == "all"
Requires-Dist: sse-starlette==2.3.6; extra == "all"
Requires-Dist: starlette==0.47.1; extra == "all"
Requires-Dist: tenacity==9.1.2; extra == "all"
Requires-Dist: tiktoken==0.9.0; extra == "all"
Requires-Dist: tokenizers==0.21.1; extra == "all"
Requires-Dist: tqdm==4.67.1; extra == "all"
Requires-Dist: typer==0.16.0; extra == "all"
Requires-Dist: typing-extensions==4.14.0; extra == "all"
Requires-Dist: typing-inspection==0.4.1; extra == "all"
Requires-Dist: tzdata==2025.2; extra == "all"
Requires-Dist: ujson==5.10.0; extra == "all"
Requires-Dist: urllib3==2.4.0; extra == "all"
Requires-Dist: uvicorn==0.35.0; extra == "all"
Requires-Dist: voyageai==0.3.2; extra == "all"
Requires-Dist: yarl==1.20.0; extra == "all"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# OpenAgentKit

[![PyPI version](https://badge.fury.io/py/openagentkit.svg)](https://pypi.org/project/openagentkit/0.1.0a3/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

A comprehensive open-source toolkit for building agentic applications. OpenAgentKit provides a unified interface to work with various LLM providers, tools, and agent frameworks.

**WARNING**: Everything here is still in development, expect many bugs and unsupported features, please feel free to contribute! 

## Features

- **Lightweight Structure**: Keeping core features of AI agents while still create rooms for custom extension without cluttering.
- **Unified LLM Interface**: Consistent API across multiple LLM providers by leveraging OpenAI APIs (will be extended in the future!)
- **Generator-based event stream**: Event-driven processing using a generator
- **Async Support**: Built-in asynchronous processing for high-performance applications
- **Tool Integration**: Pre-built tools for common agent tasks
- **Extensible Architecture**: Easily add custom models and tools
- **Type Safety**: Comprehensive typing support with Pydantic models

## Installation

```bash
pip install openagentkit==0.1.0a4
```

## Quick Start

```python
from openagentkit.modules.openai import OpenAIAgent
from openagentkit.core.tools.base_tool import tool
from pydantic import BaseModel
import openai
import os
import json

# Define a tool
@tool # Wrap the function in a tool decorator to automatically create a schema
def get_weather(city: str):
    """Get the weather of a city"""

    # Actual implementation here...
    # ...

    return f"Weather in {city}: sunny, 20°C, feels like 22°C, humidity: 50%"

# Initialize OpenAI client
client = openai.OpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
)

agent = OpenAIAgent(
    client=client,
    model="gpt-4o-mini",
    system_message="""
    You are a helpful assistant that can answer questions and help with tasks.
    You are also able to use tools to get information.
    """,
    tools=[get_weather],
    temperature=0.5,
    max_tokens=100,
    top_p=1.0,
)

generator = agent.execute(
    messages=[
        {"role": "user", "content": "What's the weather like in New York?"}
    ],
)

for response in generator:
    print(response)

print(json.dumps(agent.get_history(), indent=2))
```

## Supported Integrations

- **LLM Providers**:

  - OpenAI
  - SmallestAI
  - Azure OpenAI (via OpenAI integration)
  - More coming soon!
- **Tools** *(Mostly for prototyping purposes)*:

  - Weather information *(Requires WEATHERAPI_API_KEY)*

## Architecture

OpenAgentKit is built with a modular architecture:

- **Interfaces**: Abstract base classes defining the contract for all implementations
- **Models**: Pydantic models for type-safe data handling
- **Modules**: Implementation of various services and integrations
- **Handlers**: Processors for tools and other extensions
- **Utils**: Helper functions and utilities

## Advanced Usage

### Asynchronous Processing

```python
from openagentkit.modules.openai import OpenAIAgent
from openagentkit.core.tools.base_tool import tool
from pydantic import BaseModel
from typing import Annotated
import asyncio
import openai
import os

# Define an async tool
@tool # Wrap the function in a tool decorator to automatically create a schema
async def get_weather(city: str):
    """Get the weather of a city"""

    # Actual implementation here...
    # ...

    return f"Weather in {city}: sunny, 20°C, feels like 22°C, humidity: 50%"

# Initialize OpenAI client
client = openai.AsyncOpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
)

async def main():
    # Initialize LLM service
    agent = AsyncOpenAIAgent(
        client=client,
        model="gpt-4o-mini",
        system_message="""
        You are a helpful assistant that can answer questions and help with tasks.
        You are also able to use tools to get information.
        """,
        tools=[get_weather],
        temperature=0.5,
        max_tokens=100,
        top_p=1.0,
    )

    generator = agent.execute(
        messages=[
            {"role": "user", "content": "What's the weather like in New York?"}
        ]
    )

    async for response in generator:
        print(response.content)

if __name__ == "__main__":
    asyncio.run(main())
```

### Custom Tool Integration

#### Using the `@tool` decorator:

```python
from openagentkit.core.utils.tool_wrapper import tool
from pydantic import BaseModel
from typing import Annotated

# Define a tool
@tool # Wrap the function in a tool decorator to automatically create a schema
def get_weather(city: str):
    """Get the weather of a city""" # Always try to add pydoc in the function for better comprehension by LLM 

    # Actual implementation here...
    # ...

    return f"Weather in {city}: sunny, 20°C, feels like 22°C, humidity: 50%"

# Get the tool schema
print(get_weather.schema)

# Run the tool like any other function
weather_response = get_weather("Hanoi")
print(weather_response) 
```

#### By subclassing Tool:

```python
from openagentkit.core.tools.base_tool import Tool

class GetWeather(Tool):
    """
    A tool to get the current weather of a city.
    """
    def __call__(self, city: str) -> str:
        """
        Get the current weather in a city.
        """
        # Simulate a weather API call
        return f"The current weather in {city} is sunny with a temperature of 25°C."
    
get_weather = GetWeather()

# Get the tool schema
print(get_weather.schema)

# Run the tool like any other function
weather_response = get_weather("Hanoi")
print(weather_response) 
```

### Custom Context Store

An Agent must have access to context (chat) history to be truly an agent. OpenAgentKit has a ContextStore module that supports various cache providers (Redis, Valkey) and a quick module for testing (InMemory)

```python
from openagentkit.modules.openai import AsyncOpenAIAgent
from openagentkit.core.context import InMemoryContextStore
import openai
import asyncio
from dotenv import load_dotenv
from pydantic import BaseModel
import os

load_dotenv()

context_store = InMemoryContextStore()

async def main():
    client = openai.AsyncOpenAI(
        api_key=os.getenv("OPENAI_API_KEY"),
    )

    # When initializing an agent, you can pass in a thread_id or agent_id as identifier for the default context scope. The 2 values are immutable for consistency.
    agent = AsyncOpenAIAgent(
        client=client,
        system_message="You are a helpful assistant.",
        context_store=context_store,
        thread_id="test"
        agent_id="AssistantA"
    )

    # Access the thread_id property
    print(f"Thread ID: {agent.thread_id}")

    async for event in agent.execute(
        messages=[
            {
                "role": "user",
                "content": "Hi, my name is John."
            }
        ]
    ):
        if event.content:
            print(f"Response: {event.content}")

    # If no thread_id is defined when executing the agent, it will defaults to the initialized thread_id attribute.
    async for event in agent.execute(
        messages=[
            {
                "role": "user",
                "content": "What is my name?"
            }
        ]
    ):
        if event.content:
            print(f"Response: {event.content}")

    async for event in agent.execute(
        messages=[
            {
                "role": "user",
                "content": "What is my name?"
            }
        ],
        thread_id="new_context" # Since this is a new thread, the agent will no longer knowledge of the previous interaction
    ):
        if event.content:
            print(f"Response: {event.content}")

    # If no thread_id is defined when executing the agent, it will defaults to the initialized thread_id attribute.
    async for event in agent.execute(
        messages=[
            {
                "role": "user",
                "content": "Okay lovely, can you refer me to my name at the end of your sentence always?"
            }
        ],
    ):
        if event.content:
            print(f"Response: {event.content}")

    # Get Contexts related to agent instance (Agent ID)
    print(context_store.get_agent_context(agent.agent_id))

if __name__ == "__main__":
    asyncio.run(main())
    # Get Context from thread_id
    print(context_store.get_context("new_context"))
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the Apache License 2.0 - see the `LICENSE` file for details.
