From db1d9ef3cf5a6d5faa4fc7c50da33ef8cc50a994 Mon Sep 17 00:00:00 2001 From: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com> Date: Sun, 17 Nov 2024 09:05:43 +0800 Subject: [PATCH] chore: minor fix and update (#1188) --- camel/models/samba_model.py | 3 +- camel/societies/workforce/workforce.py | 6 ++-- camel/toolkits/__init__.py | 16 +++------ camel/toolkits/dalle_toolkit.py | 3 -- camel/toolkits/function_tool.py | 31 ++++++++++++++-- camel/toolkits/math_toolkit.py | 3 -- camel/toolkits/search_toolkit.py | 3 -- camel/toolkits/twitter_toolkit.py | 15 ++++---- camel/toolkits/weather_toolkit.py | 3 -- docs/key_modules/tools.md | 36 +++++++++++++++++-- examples/toolkits/post_weather_on_twitter.py | 8 +++-- examples/workforce/multiple_single_agents.py | 4 +-- .../workforce/role_playing_with_agents.py | 6 ++-- 13 files changed, 90 insertions(+), 47 deletions(-) diff --git a/camel/models/samba_model.py b/camel/models/samba_model.py index fa7a9442a2..a14fcfe0bc 100644 --- a/camel/models/samba_model.py +++ b/camel/models/samba_model.py @@ -158,7 +158,8 @@ def run( # type: ignore[misc] `ChatCompletion` in the non-stream mode, or `Stream[ChatCompletionChunk]` in the stream mode. """ - + if "tools" in self.model_config_dict: + del self.model_config_dict["tools"] if self.model_config_dict.get("stream") is True: return self._run_streaming(messages) else: diff --git a/camel/societies/workforce/workforce.py b/camel/societies/workforce/workforce.py index 1745ab8147..3c9c98ae10 100644 --- a/camel/societies/workforce/workforce.py +++ b/camel/societies/workforce/workforce.py @@ -41,7 +41,7 @@ ) from camel.societies.workforce.worker import Worker from camel.tasks.task import Task, TaskState -from camel.toolkits import SEARCH_FUNCS, WEATHER_FUNCS, GoogleMapsToolkit +from camel.toolkits import GoogleMapsToolkit, SearchToolkit, WeatherToolkit from camel.types import ModelPlatformType, ModelType logger = logging.getLogger(__name__) @@ -353,8 +353,8 @@ def _create_new_agent(self, role: str, sys_msg: str) -> ChatAgent: # Default tools for a new agent function_list = [ - *SEARCH_FUNCS, - *WEATHER_FUNCS, + *SearchToolkit().get_tools(), + *WeatherToolkit().get_tools(), *GoogleMapsToolkit().get_tools(), ] diff --git a/camel/toolkits/__init__.py b/camel/toolkits/__init__.py index c394ac8b68..451d947609 100644 --- a/camel/toolkits/__init__.py +++ b/camel/toolkits/__init__.py @@ -20,12 +20,11 @@ ) from .open_api_specs.security_config import openapi_security_config -from .math_toolkit import MathToolkit, MATH_FUNCS -from .search_toolkit import SearchToolkit, SEARCH_FUNCS -from .weather_toolkit import WeatherToolkit, WEATHER_FUNCS -from .dalle_toolkit import DalleToolkit, DALLE_FUNCS +from .math_toolkit import MathToolkit +from .search_toolkit import SearchToolkit +from .weather_toolkit import WeatherToolkit +from .dalle_toolkit import DalleToolkit from .ask_news_toolkit import AskNewsToolkit, AsyncAskNewsToolkit - from .linkedin_toolkit import LinkedInToolkit from .reddit_toolkit import RedditToolkit from .base import BaseToolkit @@ -35,7 +34,7 @@ from .google_scholar_toolkit import GoogleScholarToolkit from .arxiv_toolkit import ArxivToolkit from .slack_toolkit import SlackToolkit -from .twitter_toolkit import TwitterToolkit, TWITTER_FUNCS +from .twitter_toolkit import TwitterToolkit from .open_api_toolkit import OpenAPIToolkit from .retrieval_toolkit import RetrievalToolkit from .notion_toolkit import NotionToolkit @@ -65,9 +64,4 @@ 'GoogleScholarToolkit', 'NotionToolkit', 'ArxivToolkit', - 'MATH_FUNCS', - 'SEARCH_FUNCS', - 'WEATHER_FUNCS', - 'DALLE_FUNCS', - 'TWITTER_FUNCS', ] diff --git a/camel/toolkits/dalle_toolkit.py b/camel/toolkits/dalle_toolkit.py index 8b2907e786..7f1811ceed 100644 --- a/camel/toolkits/dalle_toolkit.py +++ b/camel/toolkits/dalle_toolkit.py @@ -140,6 +140,3 @@ def get_tools(self) -> List[FunctionTool]: representing the functions in the toolkit. """ return [FunctionTool(self.get_dalle_img)] - - -DALLE_FUNCS: List[FunctionTool] = DalleToolkit().get_tools() diff --git a/camel/toolkits/function_tool.py b/camel/toolkits/function_tool.py index b70532bddb..efcf04b66f 100644 --- a/camel/toolkits/function_tool.py +++ b/camel/toolkits/function_tool.py @@ -42,6 +42,31 @@ def _remove_a_key(d: Dict, remove_key: Any) -> None: _remove_a_key(d[key], remove_key) +def _remove_title_recursively(data, parent_key=None): + r"""Recursively removes the 'title' key from all levels of a nested + dictionary, except when 'title' is an argument name in the schema. + """ + if isinstance(data, dict): + # Only remove 'title' if it's not an argument name + if parent_key not in [ + "properties", + "$defs", + "items", + "allOf", + "oneOf", + "anyOf", + ]: + data.pop("title", None) + + # Recursively process each key-value pair + for key, value in data.items(): + _remove_title_recursively(value, parent_key=key) + elif isinstance(data, list): + # Recursively process each element in the list + for item in data: + _remove_title_recursively(item, parent_key=parent_key) + + def get_openai_function_schema(func: Callable) -> Dict[str, Any]: r"""Generates a schema dict for an OpenAI function based on its signature. @@ -120,9 +145,11 @@ def _create_mol(name, field): model = _create_mol(to_pascal(func.__name__), fields) parameters_dict = get_pydantic_object_schema(model) + # The `"title"` is generated by `model.model_json_schema()` - # but is useless for openai json schema - _remove_a_key(parameters_dict, "title") + # but is useless for openai json schema, remove generated 'title' from + # parameters_dict + _remove_title_recursively(parameters_dict) docstring = parse(func.__doc__ or "") for param in docstring.params: diff --git a/camel/toolkits/math_toolkit.py b/camel/toolkits/math_toolkit.py index 0f3762f743..c728825ca6 100644 --- a/camel/toolkits/math_toolkit.py +++ b/camel/toolkits/math_toolkit.py @@ -74,6 +74,3 @@ def get_tools(self) -> List[FunctionTool]: FunctionTool(self.sub), FunctionTool(self.mul), ] - - -MATH_FUNCS: List[FunctionTool] = MathToolkit().get_tools() diff --git a/camel/toolkits/search_toolkit.py b/camel/toolkits/search_toolkit.py index e2bb653be9..34abd9d1de 100644 --- a/camel/toolkits/search_toolkit.py +++ b/camel/toolkits/search_toolkit.py @@ -472,6 +472,3 @@ def get_tools(self) -> List[FunctionTool]: FunctionTool(self.query_wolfram_alpha), FunctionTool(self.tavily_search), ] - - -SEARCH_FUNCS: List[FunctionTool] = SearchToolkit().get_tools() diff --git a/camel/toolkits/twitter_toolkit.py b/camel/toolkits/twitter_toolkit.py index bd63446e50..95d82673aa 100644 --- a/camel/toolkits/twitter_toolkit.py +++ b/camel/toolkits/twitter_toolkit.py @@ -407,14 +407,6 @@ def _handle_http_error(response: requests.Response) -> str: return "Unexpected Exception" -TWITTER_FUNCS = [ - FunctionTool(create_tweet), - FunctionTool(delete_tweet), - FunctionTool(get_my_user_profile), - FunctionTool(get_user_by_username), -] - - class TwitterToolkit(BaseToolkit): r"""A class representing a toolkit for Twitter operations. @@ -442,4 +434,9 @@ def get_tools(self) -> List[FunctionTool]: List[FunctionTool]: A list of FunctionTool objects representing the functions in the toolkit. """ - return TWITTER_FUNCS + return [ + FunctionTool(create_tweet), + FunctionTool(delete_tweet), + FunctionTool(get_my_user_profile), + FunctionTool(get_user_by_username), + ] diff --git a/camel/toolkits/weather_toolkit.py b/camel/toolkits/weather_toolkit.py index 84e795fbc7..f44530562b 100644 --- a/camel/toolkits/weather_toolkit.py +++ b/camel/toolkits/weather_toolkit.py @@ -168,6 +168,3 @@ def get_tools(self) -> List[FunctionTool]: return [ FunctionTool(self.get_weather_data), ] - - -WEATHER_FUNCS: List[FunctionTool] = WeatherToolkit().get_tools() diff --git a/docs/key_modules/tools.md b/docs/key_modules/tools.md index 63daa7845b..c0b0d22f22 100644 --- a/docs/key_modules/tools.md +++ b/docs/key_modules/tools.md @@ -21,7 +21,11 @@ In CAMEL, a tool is an `FunctionTool` that LLMs can call. ### 2.1 How to Define Your Own Tool? -Developers can create custom tools tailored to their agent’s specific needs: +Developers can create custom tools tailored to their agent’s specific needs with ease. Here's how you can define and use a custom tool: + +**Example: Adding Two Numbers** + +First, define your function and wrap it using the `FunctionTool` ```python from camel.toolkits import FunctionTool @@ -37,10 +41,17 @@ def add(a: int, b: int) -> int: integer: The sum of the two numbers. """ return a + b - + +# Wrap the function with FunctionTool add_tool = FunctionTool(add) ``` +**Accessing Tool Properties** + +Once your tool is defined, you can inspect its properties using built-in methods: + +Retrieve the function's name: + ```python print(add_tool.get_function_name()) ``` @@ -49,6 +60,8 @@ print(add_tool.get_function_name()) >>> add ``` +Get the description of what the function does: + ```python print(add_tool.get_function_description()) ``` @@ -57,6 +70,8 @@ print(add_tool.get_function_description()) >>> Adds two numbers. ``` +Fetch the schema representation for OpenAI functions: + ```python print(add_tool.get_openai_function_schema()) ``` @@ -72,6 +87,8 @@ print(add_tool.get_openai_function_schema()) 'type': 'object'}} ``` +Retrieve the tool schema, compatible with OpenAI's structure: + ```python print(add_tool.get_openai_tool_schema()) ``` @@ -111,6 +128,21 @@ google_tool = FunctionTool(SearchToolkit().search_google) wiki_tool = FunctionTool(SearchToolkit().search_wiki) ``` +### 2.3 Passing Tools to `ChatAgent` +Enhance the functionality of a ChatAgent by passing custom tools during initialization. Here's how you can do it: + +```python +from camel.agents import ChatAgent + +# Initialize a ChatAgent with your custom tools +tool_agent = ChatAgent( + tools=tools, +) + +# Interact with the agent +response = tool_agent.step("A query related to the tool you added") +``` + Here is a list of the available CAMEL tools and their descriptions: | Toolkit | Description | diff --git a/examples/toolkits/post_weather_on_twitter.py b/examples/toolkits/post_weather_on_twitter.py index 598879bc89..2a60b70068 100644 --- a/examples/toolkits/post_weather_on_twitter.py +++ b/examples/toolkits/post_weather_on_twitter.py @@ -15,7 +15,7 @@ from camel.agents import ChatAgent from camel.messages import BaseMessage -from camel.toolkits import SEARCH_FUNCS, TWITTER_FUNCS, WEATHER_FUNCS +from camel.toolkits import SearchToolkit, TwitterToolkit, WeatherToolkit from camel.utils import print_text_animated @@ -38,7 +38,11 @@ def main(): agent = ChatAgent( system_message=sys_msg, - tools=[*TWITTER_FUNCS, *WEATHER_FUNCS, *SEARCH_FUNCS], + tools=[ + *TwitterToolkit().get_tools(), + *WeatherToolkit().get_tools(), + *SearchToolkit().get_tools(), + ], ) usr_msg = BaseMessage.make_user_message( diff --git a/examples/workforce/multiple_single_agents.py b/examples/workforce/multiple_single_agents.py index 0ad46170c4..33052c4d3f 100644 --- a/examples/workforce/multiple_single_agents.py +++ b/examples/workforce/multiple_single_agents.py @@ -18,10 +18,10 @@ from camel.societies.workforce import Workforce from camel.tasks.task import Task from camel.toolkits import ( - WEATHER_FUNCS, FunctionTool, GoogleMapsToolkit, SearchToolkit, + WeatherToolkit, ) from camel.types import ModelPlatformType, ModelType @@ -44,7 +44,7 @@ def main(): content="You can search online for information", ), model=search_agent_model, - tools=[*search_tools, *WEATHER_FUNCS], + tools=[*search_tools, *WeatherToolkit().get_tools()], ) # Set up tour guide agent diff --git a/examples/workforce/role_playing_with_agents.py b/examples/workforce/role_playing_with_agents.py index a5127e7df9..cd9ba5b8d5 100644 --- a/examples/workforce/role_playing_with_agents.py +++ b/examples/workforce/role_playing_with_agents.py @@ -17,7 +17,7 @@ from camel.models import ModelFactory from camel.societies.workforce import Workforce from camel.tasks.task import Task -from camel.toolkits import SEARCH_FUNCS, WEATHER_FUNCS, GoogleMapsToolkit +from camel.toolkits import GoogleMapsToolkit, SearchToolkit, WeatherToolkit from camel.types import ModelPlatformType, ModelType @@ -36,8 +36,8 @@ def main(): planner_agent = ChatAgent(planner_sysmsg) function_list = [ - *SEARCH_FUNCS, - *WEATHER_FUNCS, + *SearchToolkit().get_tools(), + *WeatherToolkit().get_tools(), *GoogleMapsToolkit().get_tools(), ]