From fd9818031b60439be5889bf9d80be0610507b7e7 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Sun, 3 Sep 2023 22:38:23 +0800 Subject: [PATCH 01/24] add web search --- camel/functions/__init__.py | 3 + camel/functions/web_search.py | 243 ++++++++++++++++++ .../role_playing_with_function.py | 4 +- test/functions/test_web_search.py | 31 +++ 4 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 camel/functions/web_search.py create mode 100644 test/functions/test_web_search.py diff --git a/camel/functions/__init__.py b/camel/functions/__init__.py index be6c8830e..7834d0e4c 100644 --- a/camel/functions/__init__.py +++ b/camel/functions/__init__.py @@ -15,9 +15,12 @@ from .openai_function import OpenAIFunction from .math_functions import MATH_FUNCS from .search_functions import SEARCH_FUNCS +from .web_search import WEB_FUNCS __all__ = [ 'OpenAIFunction', 'MATH_FUNCS', 'SEARCH_FUNCS', + 'WEB_FUNCS', ] + diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py new file mode 100644 index 000000000..17f5bcb24 --- /dev/null +++ b/camel/functions/web_search.py @@ -0,0 +1,243 @@ +# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== +# Licensed under the Apache License, Version 2.0 (the “License”); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an “AS IS” BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== +import os +from typing import Any, Dict, List + +import requests +from bs4 import BeautifulSoup + +import camel.agents +from camel.messages import BaseMessage + +from .openai_function import OpenAIFunction + + +def search_google(query: str) -> List[Dict[str, Any]]: + r"""using google search engine to search information for the given query. + + Args: + query (string): what question to search. + + Returns: + List: a list of web information, include title, descrption, url. + """ + # https://developers.google.com/custom-search/v1/overview + GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") + # https://cse.google.com/cse/all + SEARCH_ENGINE_ID = os.getenv("SEARCH_ENGINE_ID") + + # using the first page + start = 1 + # different language may get different result + language = "en" + # how many pages to return + numbers = 10 + # constructing the URL + # doc: https://developers.google.com/custom-search/v1/using_rest + url = f"https://www.googleapis.com/customsearch/v1?" \ + f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start=" \ + f"{start}&lr={language}&num={numbers}" + + responses = [] + # make the get + try: + result = requests.get(url) + data = result.json() + + # get the result items + search_items = data.get("items") + + # iterate over 10 results found + for i, search_item in enumerate(search_items, start=1): + try: + long_description = \ + search_item["pagemap"]["metatags"][0]["og:description"] + except KeyError: + long_description = "N/A" + # get the page title + title = search_item.get("title") + # page snippet + snippet = search_item.get("snippet") + + # extract the page url + link = search_item.get("link") + response = { + "Result_id": i, + "Title": title, + "Description": snippet, + "Long_description": long_description, + "URL": link + } + responses.append(response) + + except requests.RequestException: + responses.append({"erro": "google search failed."}) + + return responses + + +def text_extract_from_web(url: str) -> str: + r"""Get the text information from given url. + + Args: + url (string): The web site you want to search. + + Returns: + string: All texts extract from the web. + """ + try: + # request the target page + response_text = requests.get(url).text + + # parse the obtained page + soup = BeautifulSoup(response_text, features="html.parser") + + for script in soup(["script", "style"]): + script.extract() + + text = soup.get_text() + # strip text + lines = (line.strip() for line in text.splitlines()) + chunks = (phrase.strip() for line in lines + for phrase in line.split(" ")) + text = ".".join(chunk for chunk in chunks if chunk) + + except requests.RequestException: + text = f"can't access {url}" + + return text + + +# Split a text into smaller chunks of size n +def create_chunks(text: str, n: int) -> List[str]: + r"""Returns successive n-sized chunks from provided text." + + Args: + text: what need to be cut. + n: max length of chunk. + + Returns: + List[str]: a list of chunks + """ + + chunks = [] + i = 0 + while i < len(text): + # Find the nearest end of sentence within a range of 0.5 * n + # and 1.5 * n tokens + j = min(i + int(1.2 * n), len(text)) + while j > i + int(0.8 * n): + # Decode the tokens and check for full stop or newline + chunk = text[i:j] + if chunk.endswith(".") or chunk.endswith("\n"): + break + j -= 1 + # If no end of sentence found, use n tokens as the chunk size + if j == i + int(0.8 * n): + j = min(i + n, len(text)) + chunks.append(text[i:j]) + i = j + return chunks + + +def single_step_agent(prompt: str) -> str: + """single step agent.""" + + assistant_sys_msg = BaseMessage.make_assistant_message( + role_name="Assistant", + content="You are a helpful assistant.", + ) + agent = camel.agents.ChatAgent(assistant_sys_msg) + agent.reset() + + user_msg = BaseMessage.make_user_message( + role_name="User", + content=prompt, + ) + assistant_response = agent.step(user_msg) + if assistant_response.msgs is not None: + return assistant_response.msg.content + return "" + + +def summarise_text(text: str, query: str) -> str: + r"""Summarise the information from the text, base on the query if query is + given. + + Args: + text (string): text to summarise. + query (string): what information you want. + + Returns: + string: Strings with information. + """ + summary_prompt = f"Gather information from this text that relative to " \ + f"the question, but do not directly answer " \ + f"the question.\nquestion: {query}\ntext " + # max length of each chunk + max_len = 3000 + results = "" + chunks = create_chunks(text, max_len) + # summarise + for i, chunk in enumerate(chunks, start=1): + prompt = summary_prompt + str(i) + ": " + chunk + result = single_step_agent(prompt) + results += result + "\n" + + # final summarise + final_prompt = f"Here are some summarised texts which split from one " \ + f"text, Using the information to " \ + f"answer the question: {query}.\n\nText: " + prompt = final_prompt + results + + response = single_step_agent(prompt) + + return response + + +def search_web(query: str) -> str: + r"""search webs for information. + + Args: + query (string): question you want to be answered. + + Returns: + string: Summarised information from webs. + """ + # google search will return a list of urls + result = search_google(query) + answer: str = "" + for item in result: + url = item.get("URL") + # extract text + text = text_extract_from_web(str(url)) + # using chatgpt summarise text + answer = summarise_text(text, query) + + # let chatgpt decide whether to continue search or not + prompt = f"Do you think the answer: {answer} can answer the query: " \ + f"{query}. Use only 'yes' or 'no' to answer." + # add the source + answer += f"\nFrom: {url}" + + reply = single_step_agent(prompt) + if "yes" in str(reply).lower(): + break + + return answer + + +WEB_FUNCS: List[OpenAIFunction] = [ + OpenAIFunction(func) for func in [search_web, search_google] +] diff --git a/examples/function_call/role_playing_with_function.py b/examples/function_call/role_playing_with_function.py index a79410454..2e67dea89 100644 --- a/examples/function_call/role_playing_with_function.py +++ b/examples/function_call/role_playing_with_function.py @@ -17,7 +17,7 @@ from camel.agents.chat_agent import FunctionCallingRecord from camel.configs import ChatGPTConfig, FunctionCallingConfig -from camel.functions import MATH_FUNCS, SEARCH_FUNCS +from camel.functions import MATH_FUNCS, SEARCH_FUNCS, WEB_FUNCS from camel.societies import RolePlaying from camel.typing import ModelType from camel.utils import print_text_animated @@ -29,7 +29,7 @@ def main(model_type=ModelType.GPT_4) -> None: user_model_config = ChatGPTConfig(temperature=0.0) - function_list = [*MATH_FUNCS, *SEARCH_FUNCS] + function_list = [*WEB_FUNCS, *MATH_FUNCS, *SEARCH_FUNCS] assistant_model_config = FunctionCallingConfig.from_openai_function_list( function_list=function_list, kwargs=dict(temperature=0.0), diff --git a/test/functions/test_web_search.py b/test/functions/test_web_search.py new file mode 100644 index 000000000..24f9dce1f --- /dev/null +++ b/test/functions/test_web_search.py @@ -0,0 +1,31 @@ +# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== +# Licensed under the Apache License, Version 2.0 (the “License”); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an “AS IS” BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== +import os + +import requests + + +def test_google_api(): + # check the google search api + + # https://developers.google.com/custom-search/v1/overview + GOOGLE_API_KEY = "AIzaSyAFATycX7C9SgqpeL5ciCZ7dFBsqIqLhtY" + # https://cse.google.com/cse/all + SEARCH_ENGINE_ID = "50393d7ebc1ef4bf9" + + url = f"https://www.googleapis.com/customsearch/v1?" \ + f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q=any" + result = requests.get(url) + + assert result.status_code == 200 From a19aba26abed74587d34b318507bfc51bf487c73 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Sun, 3 Sep 2023 22:43:00 +0800 Subject: [PATCH 02/24] update --- camel/functions/__init__.py | 1 - test/functions/test_web_search.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/camel/functions/__init__.py b/camel/functions/__init__.py index 7834d0e4c..02469e181 100644 --- a/camel/functions/__init__.py +++ b/camel/functions/__init__.py @@ -23,4 +23,3 @@ 'SEARCH_FUNCS', 'WEB_FUNCS', ] - diff --git a/test/functions/test_web_search.py b/test/functions/test_web_search.py index 24f9dce1f..edef2c939 100644 --- a/test/functions/test_web_search.py +++ b/test/functions/test_web_search.py @@ -11,8 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -import os - import requests From ce8a4a6ee6bcf54d220a1fdf018fc21026d14701 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:43:28 +0800 Subject: [PATCH 03/24] update --- camel/functions/web_search.py | 64 ++++++++++++++++--------------- test/functions/test_web_search.py | 2 +- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index 17f5bcb24..fe7e17f05 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -27,50 +27,52 @@ def search_google(query: str) -> List[Dict[str, Any]]: r"""using google search engine to search information for the given query. Args: - query (string): what question to search. + query (string): The query to be searched. Returns: - List: a list of web information, include title, descrption, url. + List[Dict[str, Any]]: A list of dictionary objects, each of which + title, descrption, url of a website. """ # https://developers.google.com/custom-search/v1/overview GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") # https://cse.google.com/cse/all SEARCH_ENGINE_ID = os.getenv("SEARCH_ENGINE_ID") - # using the first page - start = 1 - # different language may get different result - language = "en" - # how many pages to return - numbers = 10 - # constructing the URL - # doc: https://developers.google.com/custom-search/v1/using_rest + # Using the first page + start_page_idx = 1 + # Different language may get different result + search_language = "en" + # How many pages to return + num_result_pages = 10 + # Constructing the URL + # Doc: https://developers.google.com/custom-search/v1/using_rest url = f"https://www.googleapis.com/customsearch/v1?" \ f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start=" \ - f"{start}&lr={language}&num={numbers}" + f"{start_page_idx}&lr={search_language}&num={num_result_pages}" responses = [] - # make the get + # Fetch the results given the URL try: + # Make the get result = requests.get(url) data = result.json() - # get the result items + # Get the result items search_items = data.get("items") - # iterate over 10 results found + # Iterate over 10 results found for i, search_item in enumerate(search_items, start=1): try: long_description = \ search_item["pagemap"]["metatags"][0]["og:description"] except KeyError: long_description = "N/A" - # get the page title + # Get the page title title = search_item.get("title") - # page snippet + # Page snippet snippet = search_item.get("snippet") - # extract the page url + # Extract the page url link = search_item.get("link") response = { "Result_id": i, @@ -124,11 +126,11 @@ def create_chunks(text: str, n: int) -> List[str]: r"""Returns successive n-sized chunks from provided text." Args: - text: what need to be cut. - n: max length of chunk. + text (string): The text to be split. + n (int): The max length of a single chunk. Returns: - List[str]: a list of chunks + List[str]: A list of splited texts. """ chunks = [] @@ -152,7 +154,7 @@ def create_chunks(text: str, n: int) -> List[str]: def single_step_agent(prompt: str) -> str: - """single step agent.""" + """Single step agent. Summarise texts or answer a question.""" assistant_sys_msg = BaseMessage.make_assistant_message( role_name="Assistant", @@ -185,17 +187,17 @@ def summarise_text(text: str, query: str) -> str: summary_prompt = f"Gather information from this text that relative to " \ f"the question, but do not directly answer " \ f"the question.\nquestion: {query}\ntext " - # max length of each chunk + # Max length of each chunk max_len = 3000 results = "" chunks = create_chunks(text, max_len) - # summarise + # Summarise for i, chunk in enumerate(chunks, start=1): prompt = summary_prompt + str(i) + ": " + chunk result = single_step_agent(prompt) results += result + "\n" - # final summarise + # Final summarise final_prompt = f"Here are some summarised texts which split from one " \ f"text, Using the information to " \ f"answer the question: {query}.\n\nText: " @@ -207,28 +209,28 @@ def summarise_text(text: str, query: str) -> str: def search_web(query: str) -> str: - r"""search webs for information. + r"""Search webs for information. Args: - query (string): question you want to be answered. + query (string): Question you want to be answered. Returns: string: Summarised information from webs. """ - # google search will return a list of urls + # Google search will return a list of urls result = search_google(query) answer: str = "" for item in result: url = item.get("URL") - # extract text + # Extract text text = text_extract_from_web(str(url)) - # using chatgpt summarise text + # Using chatgpt summarise text answer = summarise_text(text, query) - # let chatgpt decide whether to continue search or not + # Let chatgpt decide whether to continue search or not prompt = f"Do you think the answer: {answer} can answer the query: " \ f"{query}. Use only 'yes' or 'no' to answer." - # add the source + # Add the source answer += f"\nFrom: {url}" reply = single_step_agent(prompt) diff --git a/test/functions/test_web_search.py b/test/functions/test_web_search.py index edef2c939..938f329c3 100644 --- a/test/functions/test_web_search.py +++ b/test/functions/test_web_search.py @@ -15,7 +15,7 @@ def test_google_api(): - # check the google search api + # Check the google search api # https://developers.google.com/custom-search/v1/overview GOOGLE_API_KEY = "AIzaSyAFATycX7C9SgqpeL5ciCZ7dFBsqIqLhtY" From 7f8450daa47dcff9db8617c1303a4d5a63905636 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:27:49 +0800 Subject: [PATCH 04/24] Update web_search.py --- camel/functions/web_search.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index fe7e17f05..b0060bee6 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -24,7 +24,7 @@ def search_google(query: str) -> List[Dict[str, Any]]: - r"""using google search engine to search information for the given query. + r"""Use google search engine to search information for the given query. Args: query (string): The query to be searched. @@ -99,17 +99,17 @@ def text_extract_from_web(url: str) -> str: string: All texts extract from the web. """ try: - # request the target page + # Request the target page response_text = requests.get(url).text - # parse the obtained page + # Parse the obtained page soup = BeautifulSoup(response_text, features="html.parser") for script in soup(["script", "style"]): script.extract() text = soup.get_text() - # strip text + # Strip text lines = (line.strip() for line in text.splitlines()) chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) @@ -178,8 +178,8 @@ def summarise_text(text: str, query: str) -> str: given. Args: - text (string): text to summarise. - query (string): what information you want. + text (string): Text to summarise. + query (string): What information you want. Returns: string: Strings with information. @@ -209,7 +209,9 @@ def summarise_text(text: str, query: str) -> str: def search_web(query: str) -> str: - r"""Search webs for information. + r"""Search webs for information. Input a query, this function will use + google search engine search related information from the internet, then + return a summarised answer. Args: query (string): Question you want to be answered. From 591fa476cb4ca09a549045e9a6207a7f767333c8 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Tue, 12 Sep 2023 06:43:17 +0800 Subject: [PATCH 05/24] update --- camel/functions/web_search.py | 4 ++-- test/functions/test_web_search.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index b0060bee6..5d89018f3 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -62,10 +62,10 @@ def search_google(query: str) -> List[Dict[str, Any]]: # Iterate over 10 results found for i, search_item in enumerate(search_items, start=1): - try: + if "og:description" in search_item["pagemap"]["metatags"][0]: long_description = \ search_item["pagemap"]["metatags"][0]["og:description"] - except KeyError: + else: long_description = "N/A" # Get the page title title = search_item.get("title") diff --git a/test/functions/test_web_search.py b/test/functions/test_web_search.py index 938f329c3..17ef0b15b 100644 --- a/test/functions/test_web_search.py +++ b/test/functions/test_web_search.py @@ -13,6 +13,8 @@ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== import requests +from camel.functions.web_search import search_web + def test_google_api(): # Check the google search api @@ -27,3 +29,10 @@ def test_google_api(): result = requests.get(url) assert result.status_code == 200 + + +def test_web_search(): + query = "What big things are happening in 2023?" + answer = search_web(query) + + assert answer is not None From 5f16411f546a8d1fae581571b9d0ead6b893da92 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Tue, 12 Sep 2023 06:54:58 +0800 Subject: [PATCH 06/24] Update web_search.py --- camel/functions/web_search.py | 51 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index 5d89018f3..91c6ebdd8 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -58,30 +58,33 @@ def search_google(query: str) -> List[Dict[str, Any]]: data = result.json() # Get the result items - search_items = data.get("items") - - # Iterate over 10 results found - for i, search_item in enumerate(search_items, start=1): - if "og:description" in search_item["pagemap"]["metatags"][0]: - long_description = \ - search_item["pagemap"]["metatags"][0]["og:description"] - else: - long_description = "N/A" - # Get the page title - title = search_item.get("title") - # Page snippet - snippet = search_item.get("snippet") - - # Extract the page url - link = search_item.get("link") - response = { - "Result_id": i, - "Title": title, - "Description": snippet, - "Long_description": long_description, - "URL": link - } - responses.append(response) + if "items" in data: + search_items = data.get("items") + + # Iterate over 10 results found + for i, search_item in enumerate(search_items, start=1): + if "og:description" in search_item["pagemap"]["metatags"][0]: + long_description = \ + search_item["pagemap"]["metatags"][0]["og:description"] + else: + long_description = "N/A" + # Get the page title + title = search_item.get("title") + # Page snippet + snippet = search_item.get("snippet") + + # Extract the page url + link = search_item.get("link") + response = { + "Result_id": i, + "Title": title, + "Description": snippet, + "Long_description": long_description, + "URL": link + } + responses.append(response) + else: + responses.append({"erro": "google search failed."}) except requests.RequestException: responses.append({"erro": "google search failed."}) From 9ecb3e43699f5be70d5641cb70ab662005181396 Mon Sep 17 00:00:00 2001 From: Zhiyu Wang <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 22:04:51 +0800 Subject: [PATCH 07/24] Update camel/functions/web_search.py Co-authored-by: Guohao Li --- camel/functions/web_search.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index 91c6ebdd8..4e1451750 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -30,7 +30,18 @@ def search_google(query: str) -> List[Dict[str, Any]]: query (string): The query to be searched. Returns: - List[Dict[str, Any]]: A list of dictionary objects, each of which + List[Dict[str, Any]]: A list of dictionaries where each dictionary represents a website. + Each dictionary contains the following keys: + - 'title': The title of the website. + - 'description': A brief description of the website. + - 'url': The URL of the website. + + Example: + { + 'title': 'OpenAI', + 'description': 'An organization focused on ensuring that artificial general intelligence benefits all of humanity.', + 'url': 'https://www.openai.com' + } title, descrption, url of a website. """ # https://developers.google.com/custom-search/v1/overview From 51d9ca2e934933a398e3f548731730b6aa72ab94 Mon Sep 17 00:00:00 2001 From: Zhiyu Wang <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 22:05:09 +0800 Subject: [PATCH 08/24] Update camel/functions/web_search.py Co-authored-by: Guohao Li --- camel/functions/web_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index 4e1451750..3c140bf76 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -95,7 +95,7 @@ def search_google(query: str) -> List[Dict[str, Any]]: } responses.append(response) else: - responses.append({"erro": "google search failed."}) + responses.append({"error": "google search failed."}) except requests.RequestException: responses.append({"erro": "google search failed."}) From 1cf40ffd5cd889defdc7cc16bbc1471cb51ee28f Mon Sep 17 00:00:00 2001 From: Zhiyu Wang <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 22:05:21 +0800 Subject: [PATCH 09/24] Update camel/functions/web_search.py Co-authored-by: Guohao Li --- camel/functions/web_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index 3c140bf76..66ef4b7f3 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -20,7 +20,7 @@ import camel.agents from camel.messages import BaseMessage -from .openai_function import OpenAIFunction +from camel.functions import OpenAIFunction def search_google(query: str) -> List[Dict[str, Any]]: From f3f65dc7d8e4d8adcc72a247ba235ca967259ac5 Mon Sep 17 00:00:00 2001 From: Zhiyu Wang <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 22:10:57 +0800 Subject: [PATCH 10/24] Update camel/functions/web_search.py Co-authored-by: Guohao Li --- camel/functions/web_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py index 66ef4b7f3..de75a47a3 100644 --- a/camel/functions/web_search.py +++ b/camel/functions/web_search.py @@ -167,7 +167,7 @@ def create_chunks(text: str, n: int) -> List[str]: return chunks -def single_step_agent(prompt: str) -> str: +def prompt_single_step_agent(prompt: str) -> str: """Single step agent. Summarise texts or answer a question.""" assistant_sys_msg = BaseMessage.make_assistant_message( From e8ba9690617c849f6104c7943d822f7c5857f2bd Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:29:03 +0800 Subject: [PATCH 11/24] update put it into the 'SEARCH_FUNCS' --- camel/functions/__init__.py | 2 - camel/functions/search_functions.py | 251 ++++++++++++++++- camel/functions/web_search.py | 261 ------------------ .../role_playing_with_function.py | 4 +- test/functions/test_search_functions.py | 26 +- test/functions/test_web_search.py | 38 --- 6 files changed, 275 insertions(+), 307 deletions(-) delete mode 100644 camel/functions/web_search.py delete mode 100644 test/functions/test_web_search.py diff --git a/camel/functions/__init__.py b/camel/functions/__init__.py index 02469e181..be6c8830e 100644 --- a/camel/functions/__init__.py +++ b/camel/functions/__init__.py @@ -15,11 +15,9 @@ from .openai_function import OpenAIFunction from .math_functions import MATH_FUNCS from .search_functions import SEARCH_FUNCS -from .web_search import WEB_FUNCS __all__ = [ 'OpenAIFunction', 'MATH_FUNCS', 'SEARCH_FUNCS', - 'WEB_FUNCS', ] diff --git a/camel/functions/search_functions.py b/camel/functions/search_functions.py index faaca8e77..7b2572c09 100644 --- a/camel/functions/search_functions.py +++ b/camel/functions/search_functions.py @@ -11,9 +11,17 @@ # See the License for the specific language governing permissions and # limitations under the License. # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -from typing import List +import os +from typing import Any, Dict, List -from .openai_function import OpenAIFunction +import requests +from bs4 import BeautifulSoup + +import camel.agents + +from camel.functions import OpenAIFunction +from camel.messages import BaseMessage +from camel.prompts import TextPrompt def search_wiki(entity: str) -> str: @@ -45,6 +53,243 @@ def search_wiki(entity: str) -> str: return result +def search_google(query: str) -> List[Dict[str, Any]]: + r"""Use google search engine to search information for the given query. + + Args: + query (string): The query to be searched. + + Returns: + List[Dict[str, Any]]: A list of dictionaries where each dictionary + represents a website. + Each dictionary contains the following keys: + - 'title': The title of the website. + - 'description': A brief description of the website. + - 'url': The URL of the website. + + Example: + { + 'title': 'OpenAI', + 'description': 'An organization focused on ensuring that + artificial general intelligence benefits all of humanity.', + 'url': 'https://www.openai.com' + } + title, descrption, url of a website. + """ + # https://developers.google.com/custom-search/v1/overview + GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") + # https://cse.google.com/cse/all + SEARCH_ENGINE_ID = os.getenv("SEARCH_ENGINE_ID") + + # Using the first page + start_page_idx = 1 + # Different language may get different result + search_language = "en" + # How many pages to return + num_result_pages = 10 + # Constructing the URL + # Doc: https://developers.google.com/custom-search/v1/using_rest + url = f"https://www.googleapis.com/customsearch/v1?" \ + f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start=" \ + f"{start_page_idx}&lr={search_language}&num={num_result_pages}" + + responses = [] + # Fetch the results given the URL + try: + # Make the get + result = requests.get(url) + data = result.json() + + # Get the result items + if "items" in data: + search_items = data.get("items") + + # Iterate over 10 results found + for i, search_item in enumerate(search_items, start=1): + if "og:description" in search_item["pagemap"]["metatags"][0]: + long_description = \ + search_item["pagemap"]["metatags"][0]["og:description"] + else: + long_description = "N/A" + # Get the page title + title = search_item.get("title") + # Page snippet + snippet = search_item.get("snippet") + + # Extract the page url + link = search_item.get("link") + response = { + "result_id": i, + "title": title, + "description": snippet, + "long_description": long_description, + "url": link + } + responses.append(response) + else: + responses.append({"error": "google search failed."}) + + except requests.RequestException: + responses.append({"erro": "google search failed."}) + + return responses + + +def text_extract_from_web(url: str) -> str: + r"""Get the text information from given url. + + Args: + url (string): The web site you want to search. + + Returns: + string: All texts extract from the web. + """ + try: + # Request the target page + response_text = requests.get(url).text + + # Parse the obtained page + soup = BeautifulSoup(response_text, features="html.parser") + + for script in soup(["script", "style"]): + script.extract() + + text = soup.get_text() + # Strip text + lines = (line.strip() for line in text.splitlines()) + chunks = (phrase.strip() for line in lines + for phrase in line.split(" ")) + text = ".".join(chunk for chunk in chunks if chunk) + + except requests.RequestException: + text = f"can't access {url}" + + return text + + +# Split a text into smaller chunks of size n +def create_chunks(text: str, n: int) -> List[str]: + r"""Returns successive n-sized chunks from provided text." + + Args: + text (string): The text to be split. + n (int): The max length of a single chunk. + + Returns: + List[str]: A list of splited texts. + """ + + chunks = [] + i = 0 + while i < len(text): + # Find the nearest end of sentence within a range of 0.5 * n + # and 1.5 * n tokens + j = min(i + int(1.2 * n), len(text)) + while j > i + int(0.8 * n): + # Decode the tokens and check for full stop or newline + chunk = text[i:j] + if chunk.endswith(".") or chunk.endswith("\n"): + break + j -= 1 + # If no end of sentence found, use n tokens as the chunk size + if j == i + int(0.8 * n): + j = min(i + n, len(text)) + chunks.append(text[i:j]) + i = j + return chunks + + +def prompt_single_step_agent(prompt: str) -> str: + """Prompt a single-step agent to summarize texts or answer a question.""" + + assistant_sys_msg = BaseMessage.make_assistant_message( + role_name="Assistant", + content="You are a helpful assistant.", + ) + agent = camel.agents.ChatAgent(assistant_sys_msg) + agent.reset() + + user_msg = BaseMessage.make_user_message( + role_name="User", + content=prompt, + ) + assistant_response = agent.step(user_msg) + if assistant_response.msgs is not None: + return assistant_response.msg.content + return "" + + +def summarize_text(text: str, query: str) -> str: + r"""Summarize the information from the text, base on the query if query is + given. + + Args: + text (string): Text to summarise. + query (string): What information you want. + + Returns: + string: Strings with information. + """ + summary_prompt = TextPrompt( + '''Gather information from this text that relative to the question, but + do not directly answer the question.\nquestion: {query}\ntext ''') + summary_prompt = summary_prompt.format(query=query) + # Max length of each chunk + max_len = 3000 + results = "" + chunks = create_chunks(text, max_len) + # Summarize + for i, chunk in enumerate(chunks, start=1): + prompt = summary_prompt + str(i) + ": " + chunk + result = prompt_single_step_agent(prompt) + results += result + "\n" + + # Final summarise + final_prompt = TextPrompt( + '''Here are some summarized texts which split from one text, Using the + information to answer the question: {query}.\n\nText: ''') + final_prompt = final_prompt.format(query=query) + prompt = final_prompt + results + + response = prompt_single_step_agent(prompt) + + return response + + +def search_google_and_summarize(query: str) -> str: + r"""Search webs for information. Given a query, this function will use + the google search engine to search for related information from the + internet, and then return a summarized answer. + + Args: + query (string): Question you want to be answered. + + Returns: + string: Summarized information from webs. + """ + # Google search will return a list of urls + responses = search_google(query) + for item in responses: + if "url" in item: + url = item.get("url") + # Extract text + text = text_extract_from_web(str(url)) + # Using chatgpt summarise text + answer = summarize_text(text, query) + + # Let chatgpt decide whether to continue search or not + prompt = TextPrompt( + '''Do you think the answer: {answer} can answer the query: {query}. + Use only 'yes' or 'no' to answer.''') + prompt = prompt.format(answer=answer, query=query) + reply = prompt_single_step_agent(prompt) + if "yes" in str(reply).lower(): + return answer + + return "Failed to find the answer from google search." + + SEARCH_FUNCS: List[OpenAIFunction] = [ - OpenAIFunction(func) for func in [search_wiki] + OpenAIFunction(func) for func in + [search_wiki, search_google_and_summarize, search_google] ] diff --git a/camel/functions/web_search.py b/camel/functions/web_search.py deleted file mode 100644 index de75a47a3..000000000 --- a/camel/functions/web_search.py +++ /dev/null @@ -1,261 +0,0 @@ -# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -# Licensed under the Apache License, Version 2.0 (the “License”); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an “AS IS” BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -import os -from typing import Any, Dict, List - -import requests -from bs4 import BeautifulSoup - -import camel.agents -from camel.messages import BaseMessage - -from camel.functions import OpenAIFunction - - -def search_google(query: str) -> List[Dict[str, Any]]: - r"""Use google search engine to search information for the given query. - - Args: - query (string): The query to be searched. - - Returns: - List[Dict[str, Any]]: A list of dictionaries where each dictionary represents a website. - Each dictionary contains the following keys: - - 'title': The title of the website. - - 'description': A brief description of the website. - - 'url': The URL of the website. - - Example: - { - 'title': 'OpenAI', - 'description': 'An organization focused on ensuring that artificial general intelligence benefits all of humanity.', - 'url': 'https://www.openai.com' - } - title, descrption, url of a website. - """ - # https://developers.google.com/custom-search/v1/overview - GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") - # https://cse.google.com/cse/all - SEARCH_ENGINE_ID = os.getenv("SEARCH_ENGINE_ID") - - # Using the first page - start_page_idx = 1 - # Different language may get different result - search_language = "en" - # How many pages to return - num_result_pages = 10 - # Constructing the URL - # Doc: https://developers.google.com/custom-search/v1/using_rest - url = f"https://www.googleapis.com/customsearch/v1?" \ - f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start=" \ - f"{start_page_idx}&lr={search_language}&num={num_result_pages}" - - responses = [] - # Fetch the results given the URL - try: - # Make the get - result = requests.get(url) - data = result.json() - - # Get the result items - if "items" in data: - search_items = data.get("items") - - # Iterate over 10 results found - for i, search_item in enumerate(search_items, start=1): - if "og:description" in search_item["pagemap"]["metatags"][0]: - long_description = \ - search_item["pagemap"]["metatags"][0]["og:description"] - else: - long_description = "N/A" - # Get the page title - title = search_item.get("title") - # Page snippet - snippet = search_item.get("snippet") - - # Extract the page url - link = search_item.get("link") - response = { - "Result_id": i, - "Title": title, - "Description": snippet, - "Long_description": long_description, - "URL": link - } - responses.append(response) - else: - responses.append({"error": "google search failed."}) - - except requests.RequestException: - responses.append({"erro": "google search failed."}) - - return responses - - -def text_extract_from_web(url: str) -> str: - r"""Get the text information from given url. - - Args: - url (string): The web site you want to search. - - Returns: - string: All texts extract from the web. - """ - try: - # Request the target page - response_text = requests.get(url).text - - # Parse the obtained page - soup = BeautifulSoup(response_text, features="html.parser") - - for script in soup(["script", "style"]): - script.extract() - - text = soup.get_text() - # Strip text - lines = (line.strip() for line in text.splitlines()) - chunks = (phrase.strip() for line in lines - for phrase in line.split(" ")) - text = ".".join(chunk for chunk in chunks if chunk) - - except requests.RequestException: - text = f"can't access {url}" - - return text - - -# Split a text into smaller chunks of size n -def create_chunks(text: str, n: int) -> List[str]: - r"""Returns successive n-sized chunks from provided text." - - Args: - text (string): The text to be split. - n (int): The max length of a single chunk. - - Returns: - List[str]: A list of splited texts. - """ - - chunks = [] - i = 0 - while i < len(text): - # Find the nearest end of sentence within a range of 0.5 * n - # and 1.5 * n tokens - j = min(i + int(1.2 * n), len(text)) - while j > i + int(0.8 * n): - # Decode the tokens and check for full stop or newline - chunk = text[i:j] - if chunk.endswith(".") or chunk.endswith("\n"): - break - j -= 1 - # If no end of sentence found, use n tokens as the chunk size - if j == i + int(0.8 * n): - j = min(i + n, len(text)) - chunks.append(text[i:j]) - i = j - return chunks - - -def prompt_single_step_agent(prompt: str) -> str: - """Single step agent. Summarise texts or answer a question.""" - - assistant_sys_msg = BaseMessage.make_assistant_message( - role_name="Assistant", - content="You are a helpful assistant.", - ) - agent = camel.agents.ChatAgent(assistant_sys_msg) - agent.reset() - - user_msg = BaseMessage.make_user_message( - role_name="User", - content=prompt, - ) - assistant_response = agent.step(user_msg) - if assistant_response.msgs is not None: - return assistant_response.msg.content - return "" - - -def summarise_text(text: str, query: str) -> str: - r"""Summarise the information from the text, base on the query if query is - given. - - Args: - text (string): Text to summarise. - query (string): What information you want. - - Returns: - string: Strings with information. - """ - summary_prompt = f"Gather information from this text that relative to " \ - f"the question, but do not directly answer " \ - f"the question.\nquestion: {query}\ntext " - # Max length of each chunk - max_len = 3000 - results = "" - chunks = create_chunks(text, max_len) - # Summarise - for i, chunk in enumerate(chunks, start=1): - prompt = summary_prompt + str(i) + ": " + chunk - result = single_step_agent(prompt) - results += result + "\n" - - # Final summarise - final_prompt = f"Here are some summarised texts which split from one " \ - f"text, Using the information to " \ - f"answer the question: {query}.\n\nText: " - prompt = final_prompt + results - - response = single_step_agent(prompt) - - return response - - -def search_web(query: str) -> str: - r"""Search webs for information. Input a query, this function will use - google search engine search related information from the internet, then - return a summarised answer. - - Args: - query (string): Question you want to be answered. - - Returns: - string: Summarised information from webs. - """ - # Google search will return a list of urls - result = search_google(query) - answer: str = "" - for item in result: - url = item.get("URL") - # Extract text - text = text_extract_from_web(str(url)) - # Using chatgpt summarise text - answer = summarise_text(text, query) - - # Let chatgpt decide whether to continue search or not - prompt = f"Do you think the answer: {answer} can answer the query: " \ - f"{query}. Use only 'yes' or 'no' to answer." - # Add the source - answer += f"\nFrom: {url}" - - reply = single_step_agent(prompt) - if "yes" in str(reply).lower(): - break - - return answer - - -WEB_FUNCS: List[OpenAIFunction] = [ - OpenAIFunction(func) for func in [search_web, search_google] -] diff --git a/examples/function_call/role_playing_with_function.py b/examples/function_call/role_playing_with_function.py index 2e67dea89..a79410454 100644 --- a/examples/function_call/role_playing_with_function.py +++ b/examples/function_call/role_playing_with_function.py @@ -17,7 +17,7 @@ from camel.agents.chat_agent import FunctionCallingRecord from camel.configs import ChatGPTConfig, FunctionCallingConfig -from camel.functions import MATH_FUNCS, SEARCH_FUNCS, WEB_FUNCS +from camel.functions import MATH_FUNCS, SEARCH_FUNCS from camel.societies import RolePlaying from camel.typing import ModelType from camel.utils import print_text_animated @@ -29,7 +29,7 @@ def main(model_type=ModelType.GPT_4) -> None: user_model_config = ChatGPTConfig(temperature=0.0) - function_list = [*WEB_FUNCS, *MATH_FUNCS, *SEARCH_FUNCS] + function_list = [*MATH_FUNCS, *SEARCH_FUNCS] assistant_model_config = FunctionCallingConfig.from_openai_function_list( function_list=function_list, kwargs=dict(temperature=0.0), diff --git a/test/functions/test_search_functions.py b/test/functions/test_search_functions.py index 3852410e0..9b5432afc 100644 --- a/test/functions/test_search_functions.py +++ b/test/functions/test_search_functions.py @@ -12,8 +12,10 @@ # limitations under the License. # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== import wikipedia +import requests -from camel.functions.search_functions import search_wiki +from camel.functions.search_functions import search_wiki, \ + search_google_and_summarize def test_search_wiki_normal(): @@ -38,3 +40,25 @@ def test_search_wiki_with_ambiguity(): expected_output = wikipedia.summary("New York (state)", sentences=5, auto_suggest=False) assert search_wiki("New York") == expected_output + + +def test_google_api(): + # Check the google search api + + # https://developers.google.com/custom-search/v1/overview + GOOGLE_API_KEY = "AIzaSyAFATycX7C9SgqpeL5ciCZ7dFBsqIqLhtY" + # https://cse.google.com/cse/all + SEARCH_ENGINE_ID = "50393d7ebc1ef4bf9" + + url = f"https://www.googleapis.com/customsearch/v1?" \ + f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q=any" + result = requests.get(url) + + assert result.status_code == 200 + + +def test_web_search(): + query = "What big things are happening in 2023?" + answer = search_google_and_summarize(query) + + assert answer is not None diff --git a/test/functions/test_web_search.py b/test/functions/test_web_search.py deleted file mode 100644 index 17ef0b15b..000000000 --- a/test/functions/test_web_search.py +++ /dev/null @@ -1,38 +0,0 @@ -# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -# Licensed under the Apache License, Version 2.0 (the “License”); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an “AS IS” BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -import requests - -from camel.functions.web_search import search_web - - -def test_google_api(): - # Check the google search api - - # https://developers.google.com/custom-search/v1/overview - GOOGLE_API_KEY = "AIzaSyAFATycX7C9SgqpeL5ciCZ7dFBsqIqLhtY" - # https://cse.google.com/cse/all - SEARCH_ENGINE_ID = "50393d7ebc1ef4bf9" - - url = f"https://www.googleapis.com/customsearch/v1?" \ - f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q=any" - result = requests.get(url) - - assert result.status_code == 200 - - -def test_web_search(): - query = "What big things are happening in 2023?" - answer = search_web(query) - - assert answer is not None From 48515fc8a5a965bbaf0411c1b8dad7af003c87ac Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:35:27 +0800 Subject: [PATCH 12/24] update --- camel/functions/search_functions.py | 5 ++--- test/functions/test_search_functions.py | 8 +++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/camel/functions/search_functions.py b/camel/functions/search_functions.py index 7b2572c09..4c7d21b6f 100644 --- a/camel/functions/search_functions.py +++ b/camel/functions/search_functions.py @@ -18,7 +18,6 @@ from bs4 import BeautifulSoup import camel.agents - from camel.functions import OpenAIFunction from camel.messages import BaseMessage from camel.prompts import TextPrompt @@ -290,6 +289,6 @@ def search_google_and_summarize(query: str) -> str: SEARCH_FUNCS: List[OpenAIFunction] = [ - OpenAIFunction(func) for func in - [search_wiki, search_google_and_summarize, search_google] + OpenAIFunction(func) + for func in [search_wiki, search_google_and_summarize, search_google] ] diff --git a/test/functions/test_search_functions.py b/test/functions/test_search_functions.py index 9b5432afc..e3b5096fa 100644 --- a/test/functions/test_search_functions.py +++ b/test/functions/test_search_functions.py @@ -11,11 +11,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== -import wikipedia import requests +import wikipedia -from camel.functions.search_functions import search_wiki, \ - search_google_and_summarize +from camel.functions.search_functions import ( + search_google_and_summarize, + search_wiki, +) def test_search_wiki_normal(): From eb9521c69b6b10749e01b9376589b7ae917a40f2 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:43:43 +0800 Subject: [PATCH 13/24] update --- camel/functions/search_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/camel/functions/search_functions.py b/camel/functions/search_functions.py index 4c7d21b6f..888ca2f0e 100644 --- a/camel/functions/search_functions.py +++ b/camel/functions/search_functions.py @@ -245,7 +245,7 @@ def summarize_text(text: str, query: str) -> str: # Final summarise final_prompt = TextPrompt( - '''Here are some summarized texts which split from one text, Using the + '''Here are some summarized texts which split from one text, Using the information to answer the question: {query}.\n\nText: ''') final_prompt = final_prompt.format(query=query) prompt = final_prompt + results @@ -278,8 +278,8 @@ def search_google_and_summarize(query: str) -> str: # Let chatgpt decide whether to continue search or not prompt = TextPrompt( - '''Do you think the answer: {answer} can answer the query: {query}. - Use only 'yes' or 'no' to answer.''') + '''Do you think the answer: {answer} can answer the query: + {query}. Use only 'yes' or 'no' to answer.''') prompt = prompt.format(answer=answer, query=query) reply = prompt_single_step_agent(prompt) if "yes" in str(reply).lower(): From 25590bafa49c80c1324502acc3785f73300ae8d6 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:29:00 +0800 Subject: [PATCH 14/24] update --- camel/functions/search_functions.py | 15 ++++++++++++--- test/functions/test_search_functions.py | 6 ++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/camel/functions/search_functions.py b/camel/functions/search_functions.py index 888ca2f0e..814370acc 100644 --- a/camel/functions/search_functions.py +++ b/camel/functions/search_functions.py @@ -14,9 +14,6 @@ import os from typing import Any, Dict, List -import requests -from bs4 import BeautifulSoup - import camel.agents from camel.functions import OpenAIFunction from camel.messages import BaseMessage @@ -62,19 +59,28 @@ def search_google(query: str) -> List[Dict[str, Any]]: List[Dict[str, Any]]: A list of dictionaries where each dictionary represents a website. Each dictionary contains the following keys: + - 'result_id': A number in order. - 'title': The title of the website. - 'description': A brief description of the website. + - 'long_description': More detail of the website. - 'url': The URL of the website. Example: { + 'result_id': 1, 'title': 'OpenAI', 'description': 'An organization focused on ensuring that artificial general intelligence benefits all of humanity.', + 'long_description': 'OpenAI is a non-profit artificial + intelligence research company. Our goal is to advance digital + intelligence in the way that is most likely to benefit humanity + as a whole', 'url': 'https://www.openai.com' } title, descrption, url of a website. """ + import requests + # https://developers.google.com/custom-search/v1/overview GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") # https://cse.google.com/cse/all @@ -143,6 +149,9 @@ def text_extract_from_web(url: str) -> str: Returns: string: All texts extract from the web. """ + import requests + from bs4 import BeautifulSoup + try: # Request the target page response_text = requests.get(url).text diff --git a/test/functions/test_search_functions.py b/test/functions/test_search_functions.py index e3b5096fa..ea878fb59 100644 --- a/test/functions/test_search_functions.py +++ b/test/functions/test_search_functions.py @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. =========== +import os + import requests import wikipedia @@ -48,9 +50,9 @@ def test_google_api(): # Check the google search api # https://developers.google.com/custom-search/v1/overview - GOOGLE_API_KEY = "AIzaSyAFATycX7C9SgqpeL5ciCZ7dFBsqIqLhtY" + GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") # https://cse.google.com/cse/all - SEARCH_ENGINE_ID = "50393d7ebc1ef4bf9" + SEARCH_ENGINE_ID = os.getenv("SEARCH_ENGINE_ID") url = f"https://www.googleapis.com/customsearch/v1?" \ f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q=any" From 13a3bb82fbcd79c0b8748c8b40b8fe3ed2a188fb Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:41:47 +0800 Subject: [PATCH 15/24] update --- .github/workflows/pytest_apps.yml | 4 ++++ .github/workflows/pytest_package.yml | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/pytest_apps.yml b/.github/workflows/pytest_apps.yml index ffac4c78f..bf6c0fb57 100644 --- a/.github/workflows/pytest_apps.yml +++ b/.github/workflows/pytest_apps.yml @@ -26,6 +26,8 @@ jobs: - name: Run pytest env: OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}" run: poetry run pytest -v apps/ pytest_examples: @@ -41,4 +43,6 @@ jobs: - name: Run pytest env: OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}" run: poetry run pytest -v examples/ diff --git a/.github/workflows/pytest_package.yml b/.github/workflows/pytest_package.yml index 4ce22b9b2..883fe859c 100644 --- a/.github/workflows/pytest_package.yml +++ b/.github/workflows/pytest_package.yml @@ -25,6 +25,8 @@ jobs: - name: Run pytest env: OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}" run: poetry run pytest --fast-test-mode test/ pytest_package_llm_test: @@ -38,6 +40,8 @@ jobs: - name: Run pytest env: OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}" run: poetry run pytest --llm-test-only test/ pytest_package_very_slow_test: @@ -51,4 +55,6 @@ jobs: - name: Run pytest env: OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}" run: poetry run pytest --very-slow-test-only test/ From 70c6ef6bbb27df7ee67caa5025fd13ec1ef86bda Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:25:11 +0800 Subject: [PATCH 16/24] update --- camel/agents/chat_agent.py | 3 ++- examples/function_call/role_playing_with_function.py | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index 5cddd64c4..dbfb9a1e4 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -294,7 +294,8 @@ def step( a boolean indicating whether the chat session has terminated, and information about the chat session. """ - messages = self.update_messages('user', input_message) + messages = self.update_messages(input_message.role_type.value, + input_message) output_messages: List[BaseMessage] info: Dict[str, Any] diff --git a/examples/function_call/role_playing_with_function.py b/examples/function_call/role_playing_with_function.py index a79410454..296028a7e 100644 --- a/examples/function_call/role_playing_with_function.py +++ b/examples/function_call/role_playing_with_function.py @@ -24,8 +24,7 @@ def main(model_type=ModelType.GPT_4) -> None: - task_prompt = ("Assuming the current year is 2023, estimate KAUST's " - "current age and then add 10 more years to this age.") + task_prompt = "How to learn Python." user_model_config = ChatGPTConfig(temperature=0.0) @@ -37,7 +36,7 @@ def main(model_type=ModelType.GPT_4) -> None: role_play_session = RolePlaying( assistant_role_name="Searcher", - user_role_name="Professor", + user_role_name="Student", assistant_agent_kwargs=dict( model=model_type, model_config=assistant_model_config, From 685e7d44fc28e8a33137b39ac4e4a3aede2a3f14 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:12:58 +0800 Subject: [PATCH 17/24] update --- camel/functions/search_functions.py | 2 +- examples/function_call/role_playing_with_function.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/camel/functions/search_functions.py b/camel/functions/search_functions.py index 814370acc..a25a2063c 100644 --- a/camel/functions/search_functions.py +++ b/camel/functions/search_functions.py @@ -137,7 +137,7 @@ def search_google(query: str) -> List[Dict[str, Any]]: except requests.RequestException: responses.append({"erro": "google search failed."}) - return responses + return responses[:5] def text_extract_from_web(url: str) -> str: diff --git a/examples/function_call/role_playing_with_function.py b/examples/function_call/role_playing_with_function.py index 296028a7e..a79410454 100644 --- a/examples/function_call/role_playing_with_function.py +++ b/examples/function_call/role_playing_with_function.py @@ -24,7 +24,8 @@ def main(model_type=ModelType.GPT_4) -> None: - task_prompt = "How to learn Python." + task_prompt = ("Assuming the current year is 2023, estimate KAUST's " + "current age and then add 10 more years to this age.") user_model_config = ChatGPTConfig(temperature=0.0) @@ -36,7 +37,7 @@ def main(model_type=ModelType.GPT_4) -> None: role_play_session = RolePlaying( assistant_role_name="Searcher", - user_role_name="Student", + user_role_name="Professor", assistant_agent_kwargs=dict( model=model_type, model_config=assistant_model_config, From a7d8dbe91a32e88da6d616ad62742c358c6dfd25 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Wed, 20 Sep 2023 16:13:03 +0800 Subject: [PATCH 18/24] Update search_functions.py --- camel/functions/search_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/camel/functions/search_functions.py b/camel/functions/search_functions.py index a25a2063c..2784ab41a 100644 --- a/camel/functions/search_functions.py +++ b/camel/functions/search_functions.py @@ -137,7 +137,7 @@ def search_google(query: str) -> List[Dict[str, Any]]: except requests.RequestException: responses.append({"erro": "google search failed."}) - return responses[:5] + return responses def text_extract_from_web(url: str) -> str: @@ -299,5 +299,5 @@ def search_google_and_summarize(query: str) -> str: SEARCH_FUNCS: List[OpenAIFunction] = [ OpenAIFunction(func) - for func in [search_wiki, search_google_and_summarize, search_google] + for func in [search_wiki, search_google_and_summarize] ] From a9435b2d46f1ca7207493b656e120b1cef4b6a6b Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:45:28 +0800 Subject: [PATCH 19/24] update --- camel/agents/chat_agent.py | 2 +- camel/societies/role_playing.py | 6 +++--- test/agents/test_role_playing.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index dbfb9a1e4..fe30593a3 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -272,7 +272,7 @@ def submit_message(self, message: BaseMessage) -> None: message (BaseMessage): An external message to be added as an assistant response. """ - self.stored_messages.append(ChatRecord('assistant', message)) + self.stored_messages.append(ChatRecord(message.role_type.value, message)) @retry(wait=wait_exponential(min=5, max=60), stop=stop_after_attempt(5)) @openai_api_key_required diff --git a/camel/societies/role_playing.py b/camel/societies/role_playing.py index d65786736..0d8697670 100644 --- a/camel/societies/role_playing.py +++ b/camel/societies/role_playing.py @@ -365,14 +365,14 @@ def init_chat(self) -> Tuple[BaseMessage, List[BaseMessage]]: # Send the system messages again to the agents using chat messages assistant_msg = BaseMessage.make_assistant_message( role_name=self.assistant_sys_msg.role_name, - content=(f"{self.user_sys_msg.content}. " + content=(f"{self.assistant_sys_msg.content}. " "Now start to give me instructions one by one. " "Only reply with Instruction and Input.")) user_msg = BaseMessage.make_user_message( role_name=self.user_sys_msg.role_name, - content=f"{self.assistant_sys_msg.content}") - assistant_response = self.assistant_agent.step(user_msg) + content=f"{self.user_sys_msg.content}") + assistant_response = self.assistant_agent.step(assistant_msg) if assistant_response.terminated or assistant_response.msgs is None: raise ValueError(f"Assistant agent terminated unexpectedly. " f"Error info: {assistant_response.info}") diff --git a/test/agents/test_role_playing.py b/test/agents/test_role_playing.py index 390a74f8f..ce08a1352 100644 --- a/test/agents/test_role_playing.py +++ b/test/agents/test_role_playing.py @@ -146,3 +146,33 @@ def test_role_playing_with_function(): assert isinstance(response.terminated, bool) assert response.terminated is False assert isinstance(response.info, dict) + + +def test_role_playing_role_sequence(model_type=None): + task_prompt = "Develop a trading bot for the stock market" + role_playing = RolePlaying( + assistant_role_name="Python Programmer", + assistant_agent_kwargs=dict(model=model_type), + user_role_name="Stock Trader", + user_agent_kwargs=dict(model=model_type), + task_prompt=task_prompt, + with_task_specify=True, + task_specify_agent_kwargs=dict(model=model_type), + ) + assistant_role_sequence = [] + user_role_sequence = [] + + input_assistant_msg, _ = role_playing.init_chat() + assistant_response, user_response = role_playing.step(input_assistant_msg) + input_assistant_msg = assistant_response.msg + assistant_response, user_response = role_playing.step(input_assistant_msg) + + for record in role_playing.user_agent.stored_messages: + user_role_sequence.append(record.role_at_backend) + for record in role_playing.assistant_agent.stored_messages: + assistant_role_sequence.append(record.role_at_backend) + + assert user_role_sequence == \ + ['system', 'assistant', 'user', 'assistant', 'user'] + assert assistant_role_sequence == \ + ['system', 'assistant', 'user', 'assistant', 'user', 'assistant'] From 5c826ba19a11296858933160234e2fd9c4236b12 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Wed, 20 Sep 2023 23:01:00 +0800 Subject: [PATCH 20/24] Update chat_agent.py --- camel/agents/chat_agent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index fe30593a3..ed0265717 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -272,7 +272,8 @@ def submit_message(self, message: BaseMessage) -> None: message (BaseMessage): An external message to be added as an assistant response. """ - self.stored_messages.append(ChatRecord(message.role_type.value, message)) + self.stored_messages.append( + ChatRecord(message.role_type.value, message)) @retry(wait=wait_exponential(min=5, max=60), stop=stop_after_attempt(5)) @openai_api_key_required From db92799a6dcdf4d6cdc3c58301f01bc9de6a1e93 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Wed, 20 Sep 2023 23:45:11 +0800 Subject: [PATCH 21/24] update --- camel/agents/chat_agent.py | 6 ++---- camel/societies/role_playing.py | 6 +++--- test/agents/test_role_playing.py | 30 ------------------------------ 3 files changed, 5 insertions(+), 37 deletions(-) diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index ed0265717..5cddd64c4 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -272,8 +272,7 @@ def submit_message(self, message: BaseMessage) -> None: message (BaseMessage): An external message to be added as an assistant response. """ - self.stored_messages.append( - ChatRecord(message.role_type.value, message)) + self.stored_messages.append(ChatRecord('assistant', message)) @retry(wait=wait_exponential(min=5, max=60), stop=stop_after_attempt(5)) @openai_api_key_required @@ -295,8 +294,7 @@ def step( a boolean indicating whether the chat session has terminated, and information about the chat session. """ - messages = self.update_messages(input_message.role_type.value, - input_message) + messages = self.update_messages('user', input_message) output_messages: List[BaseMessage] info: Dict[str, Any] diff --git a/camel/societies/role_playing.py b/camel/societies/role_playing.py index 0d8697670..d65786736 100644 --- a/camel/societies/role_playing.py +++ b/camel/societies/role_playing.py @@ -365,14 +365,14 @@ def init_chat(self) -> Tuple[BaseMessage, List[BaseMessage]]: # Send the system messages again to the agents using chat messages assistant_msg = BaseMessage.make_assistant_message( role_name=self.assistant_sys_msg.role_name, - content=(f"{self.assistant_sys_msg.content}. " + content=(f"{self.user_sys_msg.content}. " "Now start to give me instructions one by one. " "Only reply with Instruction and Input.")) user_msg = BaseMessage.make_user_message( role_name=self.user_sys_msg.role_name, - content=f"{self.user_sys_msg.content}") - assistant_response = self.assistant_agent.step(assistant_msg) + content=f"{self.assistant_sys_msg.content}") + assistant_response = self.assistant_agent.step(user_msg) if assistant_response.terminated or assistant_response.msgs is None: raise ValueError(f"Assistant agent terminated unexpectedly. " f"Error info: {assistant_response.info}") diff --git a/test/agents/test_role_playing.py b/test/agents/test_role_playing.py index ce08a1352..390a74f8f 100644 --- a/test/agents/test_role_playing.py +++ b/test/agents/test_role_playing.py @@ -146,33 +146,3 @@ def test_role_playing_with_function(): assert isinstance(response.terminated, bool) assert response.terminated is False assert isinstance(response.info, dict) - - -def test_role_playing_role_sequence(model_type=None): - task_prompt = "Develop a trading bot for the stock market" - role_playing = RolePlaying( - assistant_role_name="Python Programmer", - assistant_agent_kwargs=dict(model=model_type), - user_role_name="Stock Trader", - user_agent_kwargs=dict(model=model_type), - task_prompt=task_prompt, - with_task_specify=True, - task_specify_agent_kwargs=dict(model=model_type), - ) - assistant_role_sequence = [] - user_role_sequence = [] - - input_assistant_msg, _ = role_playing.init_chat() - assistant_response, user_response = role_playing.step(input_assistant_msg) - input_assistant_msg = assistant_response.msg - assistant_response, user_response = role_playing.step(input_assistant_msg) - - for record in role_playing.user_agent.stored_messages: - user_role_sequence.append(record.role_at_backend) - for record in role_playing.assistant_agent.stored_messages: - assistant_role_sequence.append(record.role_at_backend) - - assert user_role_sequence == \ - ['system', 'assistant', 'user', 'assistant', 'user'] - assert assistant_role_sequence == \ - ['system', 'assistant', 'user', 'assistant', 'user', 'assistant'] From 84f032c8347408e26a8d198f163b8341b5acc41c Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Sun, 24 Sep 2023 16:33:10 +0800 Subject: [PATCH 22/24] update --- camel/agents/chat_agent.py | 6 ++++-- camel/societies/role_playing.py | 8 ++------ test/agents/test_role_playing.py | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index 5cddd64c4..ed0265717 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -272,7 +272,8 @@ def submit_message(self, message: BaseMessage) -> None: message (BaseMessage): An external message to be added as an assistant response. """ - self.stored_messages.append(ChatRecord('assistant', message)) + self.stored_messages.append( + ChatRecord(message.role_type.value, message)) @retry(wait=wait_exponential(min=5, max=60), stop=stop_after_attempt(5)) @openai_api_key_required @@ -294,7 +295,8 @@ def step( a boolean indicating whether the chat session has terminated, and information about the chat session. """ - messages = self.update_messages('user', input_message) + messages = self.update_messages(input_message.role_type.value, + input_message) output_messages: List[BaseMessage] info: Dict[str, Any] diff --git a/camel/societies/role_playing.py b/camel/societies/role_playing.py index d65786736..399735750 100644 --- a/camel/societies/role_playing.py +++ b/camel/societies/role_playing.py @@ -365,14 +365,10 @@ def init_chat(self) -> Tuple[BaseMessage, List[BaseMessage]]: # Send the system messages again to the agents using chat messages assistant_msg = BaseMessage.make_assistant_message( role_name=self.assistant_sys_msg.role_name, - content=(f"{self.user_sys_msg.content}. " + content=(f"{self.assistant_sys_msg.content}. " "Now start to give me instructions one by one. " "Only reply with Instruction and Input.")) - - user_msg = BaseMessage.make_user_message( - role_name=self.user_sys_msg.role_name, - content=f"{self.assistant_sys_msg.content}") - assistant_response = self.assistant_agent.step(user_msg) + assistant_response = self.assistant_agent.step(assistant_msg) if assistant_response.terminated or assistant_response.msgs is None: raise ValueError(f"Assistant agent terminated unexpectedly. " f"Error info: {assistant_response.info}") diff --git a/test/agents/test_role_playing.py b/test/agents/test_role_playing.py index 390a74f8f..b1908a27b 100644 --- a/test/agents/test_role_playing.py +++ b/test/agents/test_role_playing.py @@ -146,3 +146,36 @@ def test_role_playing_with_function(): assert isinstance(response.terminated, bool) assert response.terminated is False assert isinstance(response.info, dict) + + +def test_role_playing_role_sequence(model_type=None): + task_prompt = "Develop a trading bot for the stock market" + role_playing = RolePlaying( + assistant_role_name="Python Programmer", + assistant_agent_kwargs=dict(model=model_type), + user_role_name="Stock Trader", + user_agent_kwargs=dict(model=model_type), + task_prompt=task_prompt, + with_task_specify=True, + task_specify_agent_kwargs=dict(model=model_type), + ) + assistant_role_sequence = [] + user_role_sequence = [] + + input_assistant_msg, _ = role_playing.init_chat() + assistant_response, user_response = role_playing.step(input_assistant_msg) + input_assistant_msg = assistant_response.msg + assistant_response, user_response = role_playing.step(input_assistant_msg) + + for record in role_playing.user_agent.stored_messages: + user_role_sequence.append(record.role_at_backend) + for record in role_playing.assistant_agent.stored_messages: + assistant_role_sequence.append(record.role_at_backend) + + assert user_role_sequence == \ + ['system', 'assistant', 'user', 'assistant', 'user'] + assert assistant_role_sequence == \ + ['system', 'assistant', 'user', 'assistant', 'user', 'assistant'] + + + From 3e2514cc36b87e59412b0a9953675477f3ad430d Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Sun, 24 Sep 2023 16:37:25 +0800 Subject: [PATCH 23/24] Update test_role_playing.py --- test/agents/test_role_playing.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/agents/test_role_playing.py b/test/agents/test_role_playing.py index b1908a27b..ce08a1352 100644 --- a/test/agents/test_role_playing.py +++ b/test/agents/test_role_playing.py @@ -176,6 +176,3 @@ def test_role_playing_role_sequence(model_type=None): ['system', 'assistant', 'user', 'assistant', 'user'] assert assistant_role_sequence == \ ['system', 'assistant', 'user', 'assistant', 'user', 'assistant'] - - - From 2f0dc19a6e0b92b29cca196a2e442ad834798550 Mon Sep 17 00:00:00 2001 From: zhiyu-01 <121875294+zhiyu-01@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:32:50 +0800 Subject: [PATCH 24/24] update --- camel/agents/chat_agent.py | 6 ++---- camel/societies/role_playing.py | 7 +++++-- test/agents/test_role_playing.py | 30 ------------------------------ 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index ed0265717..5cddd64c4 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -272,8 +272,7 @@ def submit_message(self, message: BaseMessage) -> None: message (BaseMessage): An external message to be added as an assistant response. """ - self.stored_messages.append( - ChatRecord(message.role_type.value, message)) + self.stored_messages.append(ChatRecord('assistant', message)) @retry(wait=wait_exponential(min=5, max=60), stop=stop_after_attempt(5)) @openai_api_key_required @@ -295,8 +294,7 @@ def step( a boolean indicating whether the chat session has terminated, and information about the chat session. """ - messages = self.update_messages(input_message.role_type.value, - input_message) + messages = self.update_messages('user', input_message) output_messages: List[BaseMessage] info: Dict[str, Any] diff --git a/camel/societies/role_playing.py b/camel/societies/role_playing.py index 399735750..00bc265d3 100644 --- a/camel/societies/role_playing.py +++ b/camel/societies/role_playing.py @@ -365,10 +365,13 @@ def init_chat(self) -> Tuple[BaseMessage, List[BaseMessage]]: # Send the system messages again to the agents using chat messages assistant_msg = BaseMessage.make_assistant_message( role_name=self.assistant_sys_msg.role_name, - content=(f"{self.assistant_sys_msg.content}. " + content=(f"{self.user_sys_msg.content}. " "Now start to give me instructions one by one. " "Only reply with Instruction and Input.")) - assistant_response = self.assistant_agent.step(assistant_msg) + user_msg = BaseMessage.make_user_message( + role_name=self.user_sys_msg.role_name, + content=f"{self.assistant_sys_msg.content}") + assistant_response = self.assistant_agent.step(user_msg) if assistant_response.terminated or assistant_response.msgs is None: raise ValueError(f"Assistant agent terminated unexpectedly. " f"Error info: {assistant_response.info}") diff --git a/test/agents/test_role_playing.py b/test/agents/test_role_playing.py index ce08a1352..390a74f8f 100644 --- a/test/agents/test_role_playing.py +++ b/test/agents/test_role_playing.py @@ -146,33 +146,3 @@ def test_role_playing_with_function(): assert isinstance(response.terminated, bool) assert response.terminated is False assert isinstance(response.info, dict) - - -def test_role_playing_role_sequence(model_type=None): - task_prompt = "Develop a trading bot for the stock market" - role_playing = RolePlaying( - assistant_role_name="Python Programmer", - assistant_agent_kwargs=dict(model=model_type), - user_role_name="Stock Trader", - user_agent_kwargs=dict(model=model_type), - task_prompt=task_prompt, - with_task_specify=True, - task_specify_agent_kwargs=dict(model=model_type), - ) - assistant_role_sequence = [] - user_role_sequence = [] - - input_assistant_msg, _ = role_playing.init_chat() - assistant_response, user_response = role_playing.step(input_assistant_msg) - input_assistant_msg = assistant_response.msg - assistant_response, user_response = role_playing.step(input_assistant_msg) - - for record in role_playing.user_agent.stored_messages: - user_role_sequence.append(record.role_at_backend) - for record in role_playing.assistant_agent.stored_messages: - assistant_role_sequence.append(record.role_at_backend) - - assert user_role_sequence == \ - ['system', 'assistant', 'user', 'assistant', 'user'] - assert assistant_role_sequence == \ - ['system', 'assistant', 'user', 'assistant', 'user', 'assistant']