{"id":16277459,"url":"https://github.com/maximilian-winter/toolagents","last_synced_at":"2026-03-09T07:13:25.002Z","repository":{"id":251111356,"uuid":"836430709","full_name":"Maximilian-Winter/ToolAgents","owner":"Maximilian-Winter","description":"ToolAgents is a lightweight and flexible framework for creating function-calling agents with various language models and APIs.","archived":false,"fork":false,"pushed_at":"2025-10-03T23:20:46.000Z","size":12404,"stargazers_count":27,"open_issues_count":3,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-04T01:14:16.165Z","etag":null,"topics":["agent","agents","function-calling","llm","llm-agent","llm-agents","llms","local-llm"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Maximilian-Winter.png","metadata":{"files":{"readme":"ReadMe.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2024-07-31T20:43:56.000Z","updated_at":"2025-08-29T23:44:37.000Z","dependencies_parsed_at":"2025-02-11T01:28:19.903Z","dependency_job_id":"7d2413be-36df-45fb-b3b2-27fd7c189a2f","html_url":"https://github.com/Maximilian-Winter/ToolAgents","commit_stats":null,"previous_names":["maximilian-winter/toolagents","maximilian-winter/funkyflow"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/Maximilian-Winter/ToolAgents","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maximilian-Winter%2FToolAgents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maximilian-Winter%2FToolAgents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maximilian-Winter%2FToolAgents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maximilian-Winter%2FToolAgents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Maximilian-Winter","download_url":"https://codeload.github.com/Maximilian-Winter/ToolAgents/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maximilian-Winter%2FToolAgents/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30286282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T02:57:19.223Z","status":"ssl_error","status_checked_at":"2026-03-09T02:56:26.373Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","agents","function-calling","llm","llm-agent","llm-agents","llms","local-llm"],"created_at":"2024-10-10T18:54:49.533Z","updated_at":"2026-03-09T07:13:24.959Z","avatar_url":"https://github.com/Maximilian-Winter.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ToolAgents\n\nToolAgents is a lightweight and flexible framework for creating function-calling agents with various language models and APIs. It provides a unified interface for integrating different LLM providers and executing function calls seamlessly.\n\n\n## Table of Contents\n\n1. [Features](#features)\n2. [Installation](#installation)\n3. [Usage](#usage)\n  - [Simple ChatToolAgent Usage](#ChatToolAgent)\n  - [Using different Providers](#Different-Providers)\n  - [ChatToolAgent with User Loop and Chat History](#Use-ChatToolAgent-with-ChatHistory-class)\n  - [Streaming ChatToolAgent with User Loop and Chat History](#Use-Streaming-ChatToolAgent-with-ChatHistory-class)\n4. [Custom Tools](#custom-tools)\n  - [Pydantic Model-based Tools](#1-pydantic-model-based-tools)\n  - [Function-based Tools](#2-function-based-tools)\n  - [OpenAI-style Function Specifications](#3-openai-style-function-specifications)\n  - [The Importance of Good Docstrings and Descriptions](#the-importance-of-good-docstrings-and-descriptions)\n5. [Contributing](#contributing)\n6. [License](#license)\n\n## Features\n\n- Support for multiple LLM providers:\n  - OpenAI API\n  - Anthropic API\n  - Mistral API\n  - OpenAI like API, like OpenRouter, VLLM, llama-cpp-server\n- Easy-to-use interface for passing functions, Pydantic models, and tools to LLMs\n- Streamlined process for function calling and result handling\n- Unified Message format, making switching of providers while keeping the same chat history easy.\n\n## Installation\n\n```bash\npip install ToolAgents\n```\n\n## Usage\n\n### ChatToolAgent\n\n```python\nimport os\n\nfrom ToolAgents import ToolRegistry\nfrom ToolAgents.agents import ChatToolAgent\nfrom ToolAgents.messages.chat_message import ChatMessage\nfrom ToolAgents.provider import OpenAIChatAPI\nfrom example_tools import calculator_function_tool, current_datetime_function_tool, get_weather_function_tool\n\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# Official OpenAI API\napi = OpenAIChatAPI(api_key=os.getenv(\"OPENAI_API_KEY\"), model=\"gpt-4o-mini\")\n\n# Create the ChatAPIAgent\nagent = ChatToolAgent(chat_api=api)\nsettings = api.get_default_settings()\nsettings.temperature = 0.45\nsettings.top_p = 1.0\n\n# Define the tools\ntools = [calculator_function_tool, current_datetime_function_tool, get_weather_function_tool]\ntool_registry = ToolRegistry()\n\ntool_registry.add_tools(tools)\nmessages = [\n    ChatMessage.create_system_message(\"You are a helpful assistant with tool calling capabilities. Only reply with a tool call if the function exists in the library provided by the user. Use JSON format to output your function calls. If it doesn't exist, just reply directly in natural language. When you receive a tool call response, use the output to format an answer to the original user question.\"),\n    ChatMessage.create_user_message(\"Get the weather in London and New York. Calculate 420 x 420 and retrieve the date and time in the format: %Y-%m-%d %H:%M:%S.\")\n]\n\nresult = agent.get_streaming_response(\n    messages=messages,\n    settings=settings, tool_registry=tool_registry)\n\n\nfor res in result:\n    print(res.chunk, end='', flush=True)\n\n```\n\n### Different Providers\n```python\n# Import different providers\nfrom ToolAgents.provider import AnthropicChatAPI, OpenAIChatAPI, GroqChatAPI, MistralChatAPI, CompletionProvider\n\n# Official OpenAI API\napi = OpenAIChatAPI(api_key=os.getenv(\"OPENAI_API_KEY\"), model=\"gpt-4o-mini\")\n\n# Local OpenAI like API, like vllm or llama-cpp-server\napi = OpenAIChatAPI(api_key=\"token-abc123\", base_url=\"http://127.0.0.1:8080/v1\", model=\"unsloth/Meta-Llama-3.1-8B-Instruct-bnb-4bit\")\n\n# Anthropic API\napi = AnthropicChatAPI(api_key=os.getenv(\"ANTHROPIC_API_KEY\"), model=\"claude-3-5-sonnet-20241022\")\n\n# Groq API\napi = GroqChatAPI(api_key=os.getenv(\"GROQ_API_KEY\"), model=\"llama-3.3-70b-versatile\")\n\n\n# Mistral API\napi = MistralChatAPI(api_key=os.getenv(\"MISTRAL_API_KEY\"), model=\"mistral-small-latest\")\n\n```\n\n### Use ChatToolAgent with ChatHistory class\n```python\nimport os\n\nfrom ToolAgents import ToolRegistry\nfrom ToolAgents.agents import ChatToolAgent\nfrom ToolAgents.messages import ChatHistory\n\nfrom ToolAgents.provider import OpenAIChatAPI\n\nfrom example_tools import calculator_function_tool, current_datetime_function_tool, get_weather_function_tool\n\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# Openrouter API\napi = OpenAIChatAPI(api_key=os.getenv(\"OPENROUTER_API_KEY\"), model=\"google/gemini-2.0-pro-exp-02-05:free\", base_url=\"https://openrouter.ai/api/v1\")\n\n# Create the ChatAPIAgent\nagent = ChatToolAgent(chat_api=api)\n\n# Create a samplings settings object\nsettings = api.get_default_settings()\n\n# Set sampling settings\nsettings.temperature = 0.45\nsettings.top_p = 1.0\n\n# Define the tools\ntools = [calculator_function_tool, current_datetime_function_tool, get_weather_function_tool]\ntool_registry = ToolRegistry()\n\ntool_registry.add_tools(tools)\n\nchat_history = ChatHistory()\nchat_history.add_system_message(\"You are a helpful assistant with tool calling capabilities. Only reply with a tool call if the function exists in the library provided by the user. Use JSON format to output your function calls. If it doesn't exist, just reply directly in natural language. When you receive a tool call response, use the output to format an answer to the original user question.\")\n\nwhile True:\n    user_input = input(\"User input \u003e\")\n    if user_input == \"quit\":\n        break\n    elif user_input == \"save\":\n        chat_history.save_to_json(\"example_chat_history.json\")\n    elif user_input == \"load\":\n        chat_history = ChatHistory.load_from_json(\"example_chat_history.json\")\n    else:\n        chat_history.add_user_message(user_input)\n\n        chat_response = agent.get_response(\n            messages=chat_history.get_messages(),\n            settings=settings, tool_registry=tool_registry)\n\n        print(chat_response.response.strip())\n        chat_history.add_messages(chat_response.messages)\n\n```\n\n### Use Streaming ChatToolAgent with ChatHistory class\n```python\nimport os\n\nfrom ToolAgents import ToolRegistry\nfrom ToolAgents.agents import ChatToolAgent\nfrom ToolAgents.messages import ChatHistory\n\nfrom ToolAgents.provider import OpenAIChatAPI\n\nfrom example_tools import calculator_function_tool, current_datetime_function_tool, get_weather_function_tool\n\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# Openrouter API\napi = OpenAIChatAPI(api_key=os.getenv(\"OPENROUTER_API_KEY\"), model=\"google/gemini-2.0-pro-exp-02-05:free\", base_url=\"https://openrouter.ai/api/v1\")\n\n# Create the ChatAPIAgent\nagent = ChatToolAgent(chat_api=api)\n\n# Create a samplings settings object\nsettings = api.get_default_settings()\n\n# Set sampling settings\nsettings.temperature = 0.45\nsettings.top_p = 1.0\n\n# Define the tools\ntools = [calculator_function_tool, current_datetime_function_tool, get_weather_function_tool]\ntool_registry = ToolRegistry()\n\ntool_registry.add_tools(tools)\n\nchat_history = ChatHistory()\nchat_history.add_system_message(\"You are a helpful assistant with tool calling capabilities. Only reply with a tool call if the function exists in the library provided by the user. Use JSON format to output your function calls. If it doesn't exist, just reply directly in natural language. When you receive a tool call response, use the output to format an answer to the original user question.\")\n\nwhile True:\n    user_input = input(\"User input \u003e\")\n    if user_input == \"quit\":\n        break\n    elif user_input == \"save\":\n        chat_history.save_to_json(\"example_chat_history.json\")\n    elif user_input == \"load\":\n        chat_history = ChatHistory.load_from_json(\"example_chat_history.json\")\n    else:\n        chat_history.add_user_message(user_input)\n\n        stream = agent.get_streaming_response(\n            messages=chat_history.get_messages(),\n            settings=settings, tool_registry=tool_registry)\n        chat_response = None\n        for res in stream:\n            print(res.chunk, end='', flush=True)\n            if res.finished:\n              chat_response = res.finished_response\n        if chat_response is not None:\n            chat_history.add_messages(chat_response.messages)\n        else:\n          raise Exception(\"Error during response generation\")\n```\n## Custom Tools\n\nToolAgents supports various ways to create custom tools, allowing you to integrate specific functionalities into your agents. Here are different approaches to creating custom tools:\n\n### 1. Pydantic Model-based Tools\n\nYou can create tools using Pydantic models, which provide strong typing and automatic validation. Here's an example of a calculator tool:\n\n```python\nfrom enum import Enum\nfrom typing import Union\nfrom pydantic import BaseModel, Field\nfrom ToolAgents import FunctionTool\n\nclass MathOperation(Enum):\n    ADD = \"add\"\n    SUBTRACT = \"subtract\"\n    MULTIPLY = \"multiply\"\n    DIVIDE = \"divide\"\n\nclass Calculator(BaseModel):\n    \"\"\"\n    Perform a math operation on two numbers.\n    \"\"\"\n    number_one: Union[int, float] = Field(..., description=\"First number.\")\n    operation: MathOperation = Field(..., description=\"Math operation to perform.\")\n    number_two: Union[int, float] = Field(..., description=\"Second number.\")\n\n    def run(self):\n        if self.operation == MathOperation.ADD:\n            return self.number_one + self.number_two\n        elif self.operation == MathOperation.SUBTRACT:\n            return self.number_one - self.number_two\n        elif self.operation == MathOperation.MULTIPLY:\n            return self.number_one * self.number_two\n        elif self.operation == MathOperation.DIVIDE:\n            return self.number_one / self.number_two\n        else:\n            raise ValueError(\"Unknown operation.\")\n\ncalculator_tool = FunctionTool(Calculator)\n```\n\n### 2. Function-based Tools\n\nYou can also create tools from simple Python functions. Here's an example of a datetime tool:\n\n```python\nimport datetime\nfrom ToolAgents import FunctionTool\n\ndef get_current_datetime(output_format: str = '%Y-%m-%d %H:%M:%S'):\n    \"\"\"\n    Get the current date and time in the given format.\n\n    Args:\n        output_format: formatting string for the date and time, defaults to '%Y-%m-%d %H:%M:%S'\n    \"\"\"\n    return datetime.datetime.now().strftime(output_format)\n\ncurrent_datetime_tool = FunctionTool(get_current_datetime)\n```\n\n### 3. OpenAI-style Function Specifications\n\nToolAgents supports creating tools from OpenAI-style function specifications:\n\n```python\nfrom ToolAgents import FunctionTool\n\ndef get_current_weather(location, unit):\n    \"\"\"Get the current weather in a given location\"\"\"\n    # Implementation details...\n\nopen_ai_tool_spec = {\n    \"type\": \"function\",\n    \"function\": {\n        \"name\": \"get_current_weather\",\n        \"description\": \"Get the current weather in a given location\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"location\": {\n                    \"type\": \"string\",\n                    \"description\": \"The city and state, e.g. San Francisco, CA\",\n                },\n                \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n            },\n            \"required\": [\"location\", \"unit\"],\n        },\n    },\n}\n\nweather_tool = FunctionTool.from_openai_tool(open_ai_tool_spec, get_current_weather)\n```\n\n### The Importance of Good Docstrings and Descriptions\n\nWhen creating custom tools, it's crucial to provide clear and comprehensive docstrings and descriptions. Here's why they matter:\n\n1. **AI Understanding**: The language model uses these descriptions to understand the purpose and functionality of each tool. Better descriptions lead to more accurate tool selection and usage.\n\n2. **Parameter Clarity**: Detailed descriptions for each parameter help the AI understand what input is expected, reducing errors and improving the quality of the generated calls.\n\n3. **Proper Usage**: Good docstrings guide the AI on how to use the tool correctly, including any specific formats or constraints for the input.\n\n4. **Error Prevention**: By clearly stating the expected input types and any limitations, you can prevent many potential errors before they occur.\n\nHere's an example of a well-documented tool:\n\n```python\nfrom pydantic import BaseModel, Field\nfrom ToolAgents import FunctionTool\n\nclass FlightTimes(BaseModel):\n    \"\"\"\n    Retrieve flight information between two locations.\n\n    This tool provides estimated flight times, including departure and arrival times,\n    for flights between major airports. It uses airport codes for input.\n    \"\"\"\n\n    departure: str = Field(\n        ...,\n        description=\"The departure airport code (e.g., 'NYC' for New York)\",\n        min_length=3,\n        max_length=3\n    )\n    arrival: str = Field(\n        ...,\n        description=\"The arrival airport code (e.g., 'LAX' for Los Angeles)\",\n        min_length=3,\n        max_length=3\n    )\n\n    def run(self) -\u003e str:\n        \"\"\"\n        Retrieve flight information for the given departure and arrival locations.\n\n        Returns:\n            str: A JSON string containing flight information including departure time,\n                 arrival time, and flight duration. If no flight is found, returns an error message.\n        \"\"\"\n        # Implementation details...\n\nget_flight_times_tool = FunctionTool(FlightTimes)\n```\n\nIn this example, the docstrings and field descriptions provide clear information about the tool's purpose, input requirements, and expected output, enabling both the AI and human developers to use the tool effectively.\n\n## Contributing\n\nContributions to ToolAgents are welcome! Please feel free to submit pull requests, create issues, or suggest improvements.\n\n## License\n\nToolAgents is released under the MIT License. See the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximilian-winter%2Ftoolagents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaximilian-winter%2Ftoolagents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximilian-winter%2Ftoolagents/lists"}