diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 1c1901d..6edaef0 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -23,6 +23,6 @@ jobs: python setup.py sdist bdist_wheel twine check dist/* - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@release/v2 with: password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file diff --git a/Readme.md b/Readme.md index 4dc2f1f..bbff023 100644 --- a/Readme.md +++ b/Readme.md @@ -1 +1,183 @@ -# Claudetools \ No newline at end of file +# Claudetools + +**Claudetools** is a Python library that provides a convenient way to use Claude 3 family's structured data generation capabilities for function calling. The function calling capabilities are similar to ones available with OpenAI models. + +With `claudetools` one can now use any model from the **Claude 3** family of models for function calling. + +## Key Features + +- **Function Calling**: Define and call custom functions within your prompts, leveraging the advanced capabilities of Claude 3 models. +- **Synchronous and Asynchronous Clients**: Choose between synchronous or asynchronous interaction modes depending on your use case. +- **Flexible Tool Definition**: Specify function names, descriptions, and parameters using the Pydantic library for type safety and validation. +- **Multiple Tools Support**: Opt to call multiple tools within a single prompt, enabling more complex interactions. +- **Customizable System Prompts**: Attach custom system prompts to your conversations for better context and control. + + +## Installation + +You can install `claudetools` from PyPI or directly from the source: + +### From PyPi + +```bash +pip install caludetools +``` + +### Install from source + +```bash +pip install git+https://github.com/vatsalsaglani/claudetools +``` + +## Usage + +Here's a basic example of how to use `claudetools` for function calling with the Claude 3 model: + +```py +import asyncio +from claudetools.tools.tool import Tool +from pydantic import BaseModel, Field +from typing import List, Dict +import json + +# create a tool instance with your Anthropic API Key +tool = Tool(ANTHROPIC_API_KEY) + +# define your function parameters +class AddTodo(BaseModel): + text: str = Field(..., description="Text to add for the TODO to remember.") + +class MarkCompleted(BaseModel): + text: str = Field(..., description="Text of the completed TODO.") + + +class ReOpen(BaseModel): + text: str = Field(..., description="Text of the TODO to reopen.") + + +# specify the functions you want to use +functions = [{ + "name": "AddTodo", + "description": "Add a TODO with text to remember.", + "parameters": AddTodo.model_json_schema() +}, { + "name": "MarkCompleted", + "description": "Get text of the todo mark it complete", + "parameters": MarkCompleted.model_json_schema() +}, { + "name": "ReOpen", + "description": "Get text of the todo reopen it.", + "parameters": ReOpen.model_json_schema() +}] + +# set up the user messages +user_messages = [{ + "role": + "user", + "content": + """I have to pick up my daughter from school. After which I've to do the laundary. And now I need to cook lunch.""" +}] + +# dependency prompt to attach to the main system prompt +DEPENDENCY_PROMPT = """You are a helpful assistant that helps a user with their tasks and todos. The user can add a todos, mark todos as completed, or reopen certain todos. +The user can provide multiple actions at once so you've to break those down and call the appropriate functions in the correct sequence.""" + + +# call the tool with the required parameters +output = tool(model="claude-3-sonnet-20240229", + messages=user_messages, + tools=functions, + tool_choice=None, + multiple_tools=True, + attach_system=DEPENDENCY_PROMPT, + max_tokens=3000) + +if output: + print(json.dumps(output, indent=4)) +else: + print("Unable to find a function!") +``` + +> No default value of `max_tokens` is assumed hence, please provide `max_tokens` to avoid getting an error from the Claude APIs. + +_The parameter explanation is provided below._ + +- `model`: The name of the model from the Claude 3 family. +- `messages`: Messages transferred between the user and the assistant. +- `tools`: Set of function specification to use. +- `tool_choice`: User a particular function. By default the value is `None`. The model will figure out the function to call and provide the related parameters. If a specific function needs to be called provide `{"name": "function name"}` in the `tool_choice` argument. +- `multiple_tools`: Defaults to `False`. Can be set to `True` to get multiple function calls. +- `attach_system`: Defaults to `None`. Can also accept string which will be attached as part of the system prompt. +- `max_tokens`: As mentioned above, no default value of `max_tokens` is assumed. Hence, please provide `max_tokens` to avoid getting an error. + + +### Asynchronous Interaction + +Just import `AsyncTool` instead of `Tool` to and use `await` with each call. + +```py +import asyncio +from claudetools.tools.tool import AsyncTool +from pydantic import BaseModel, Field +from typing import List, Dict +import json + +tool = AsyncTool(ANTHROPIC_API_KEY) + +class AddTodo(BaseModel): + text: str = Field(..., description="Text to add for the TODO to remember.") + + +functions = [{ + "name": "AddTodo", + "description": "Add a TODO with text to remember.", + "parameters": AddTodo.model_json_schema() +}, { + "name": "MarkCompleted", + "description": "Get text of the todo mark it complete", + "parameters": MarkCompleted.model_json_schema() +}, { + "name": "ReOpen", + "description": "Get text of the todo reopen it.", + "parameters": ReOpen.model_json_schema() +}] + +user_messages = [{ + "role": + "user", + "content": + """I have to pick up my daughter from school. After which I've to do the laundary. And now I need to cook lunch.""" +}] + +async def main(): + + output = await tool(model="claude-3-sonnet-20240229", + messages=user_messages, + tools=functions, + tool_choice=None, + multiple_tools=True, + attach_system=None, + max_tokens=3000) + if output: + print(json.dumps(output, indent=4)) + else: + print("Unable to find a function!") + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Requirements + +Python 3.7 or higher. + +## TODOs + +- [ ] Add examples notebook. +- [ ] Enable logging. +- [ ] Add support for AWS Bedrock. +- [ ] Validate outputs in `tool_choice`. + +## Contributing + +Contributions to `claudetools` are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on this GitHub repository. diff --git a/claudetools/completion/async_complete.py b/claudetools/completion/async_complete.py index ef411fa..c6f1e30 100644 --- a/claudetools/completion/async_complete.py +++ b/claudetools/completion/async_complete.py @@ -29,5 +29,5 @@ async def __call__(self, model: str, messages: List[Dict], **kwargs): headers=self.headers) response.raise_for_status() output = response.json() - print("MODEL OUTPUT\n", output) + # print("MODEL OUTPUT\n", output) return output.get("content")[0].get("text") diff --git a/claudetools/completion/complete.py b/claudetools/completion/complete.py index eddf148..0788677 100644 --- a/claudetools/completion/complete.py +++ b/claudetools/completion/complete.py @@ -11,5 +11,5 @@ def __call__(self, model: str, messages: List[Dict], **kwargs): output = self.client.messages.create(model=model, messages=messages, **kwargs) - print("MODEL OUTPUT\n", output) + # print("MODEL OUTPUT\n", output) return output.content[0].text diff --git a/claudetools/tools/tool.py b/claudetools/tools/tool.py index 81222bc..932a797 100644 --- a/claudetools/tools/tool.py +++ b/claudetools/tools/tool.py @@ -51,7 +51,7 @@ async def tool_call(self, if tool_choice: ToolChoice.model_validate(tool_choice) tool_name = tool_choice.get("name") - print(tool_choice, type(tool_choice)) + # print(tool_choice, type(tool_choice)) system = SINGLE_FUNCTION_SPECIFIC_CALL.format( functions=tools, function_name=tool_name) else: diff --git a/setup.py b/setup.py index 5e07ec0..fca8a98 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,14 @@ setup( name="claudetools", - version="0.2.0", + version="0.4.0", author="Vatsal J. Saglani", author_email="saglanivatsal@gmail.com", - description="Function calling using the Claude 3 family.", + description= + "Claudetools is a Python library that enables function calling with the Claude 3 family of language models from Anthropic.", long_description=open("Readme.md").read(), long_description_content_type="text/markdown", url="https://github.com/vatsalsaglani/claudetools", packages=find_packages(), install_requires=["httpx==0.25.0", "anthropic==0.19.1", "pydantic==2.4.2"], - python_requires=">=3.9") + python_requires=">=3.7")