diff --git a/cookbook/teams/journalist.py b/cookbook/teams/journalist.py index 6136bdaeb..6a6160498 100644 --- a/cookbook/teams/journalist.py +++ b/cookbook/teams/journalist.py @@ -1,48 +1,43 @@ -from shutil import rmtree -from phi.assistant.team import Assistant -from phi.tools.serpapi_toolkit import SerpapiToolkit +""" +Inspired by the fantastic work by Matt Shumer (@mattshumer_): https://twitter.com/mattshumer_/status/1772286375817011259 -from newspaper import Article +Please run: +pip install openai anthropic google-search-results newspaper3k lxml_html_clean phidata +""" -def get_article_text(url): - """Get the text of an article from a URL. - Args: - url (str): The URL of the article. - Returns: - str: The text of the article. - """ - article = Article(url) - article.download() - article.parse() - return article.text +from phi.assistant.team import Assistant +from phi.tools.serpapi_toolkit import SerpApiToolkit +from phi.tools.newspaper_toolkit import NewspaperToolkit +from phi.llm.anthropic import Claude search_journalist = Assistant( name="Search Journalist", + llm=Claude(model="claude-3-haiku-20240307"), role="Searches for top URLs based on a topic", - description="You are a world-class search journalist. You search the web and retreive the top URLs based on a topic.", + description="You are a world-class search journalist. You search the web and retrieve the top URLs based on a topic.", instructions=[ "Given a topic, conduct a search to find the top results.", "For a search topic, return the top 3 URLs.", ], - tools=[SerpapiToolkit()], - # debug_mode=True, + tools=[SerpApiToolkit()], + debug_mode=True, ) - research_journalist = Assistant( - name=" Research Journalist", - role="Retrives text from URLs", - description="You are a world-class research journalist. You retreive the text from the URLs.", + name="Research Journalist", + llm=Claude(model="claude-3-haiku-20240307"), + role="Retrieves text from URLs", + description="You are a world-class research journalist. You retrieve the text from the URLs.", instructions=["For a list of URLs, return the text of the articles."], - tools=[get_article_text], - # debug_mode=True, + tools=[NewspaperToolkit()], + debug_mode=True, ) editor = Assistant( name="Editor", team=[search_journalist, research_journalist], - description="You are the senior editor. Given a topic, use the journalists to write a NYT worthy article.", + description="You are a senior NYT editor. Given a topic, use the journalists to write a NYT worthy article.", instructions=[ "Given a topic, ask the search journalist to search for the top 3 URLs.", "Then pass on these URLs to research journalist to get the text of the articles.", @@ -54,5 +49,4 @@ def get_article_text(url): debug_mode=True, markdown=True, ) - -editor.print_response("Write an acticle about the Anthropic claude.") \ No newline at end of file +editor.print_response("Write an article about latest developments in AI.") diff --git a/phi/assistant/assistant.py b/phi/assistant/assistant.py index e845f5241..f3868ca6a 100644 --- a/phi/assistant/assistant.py +++ b/phi/assistant/assistant.py @@ -308,7 +308,11 @@ def get_delegation_prompt(self) -> Optional[str]: if assistant.tools is not None: _tools = [] for _tool in assistant.tools: - if callable(_tool): + if isinstance(_tool, Toolkit): + _tools.extend(list(_tool.functions.keys())) + elif isinstance(_tool, Function): + _tools.append(_tool.name) + elif callable(_tool): _tools.append(_tool.__name__) delegation_prompt += f"Available tools: {', '.join(_tools)}\n" delegation_prompt += "" diff --git a/phi/tools/function.py b/phi/tools/function.py index 835d36f3a..afea78b34 100644 --- a/phi/tools/function.py +++ b/phi/tools/function.py @@ -58,11 +58,16 @@ def get_definition_for_prompt(self) -> Optional[str]: return None type_hints = get_type_hints(self.entrypoint) + return_type = type_hints.get("return", None) + returns = None + if return_type is not None: + returns = self.get_type_name(return_type) + function_info = { "name": self.name, "description": self.description, "arguments": self.parameters.get("properties", {}), - "returns": type_hints.get("return", "void").__name__, + "returns": returns, } return json.dumps(function_info, indent=2) @@ -73,11 +78,16 @@ def get_definition_for_prompt_dict(self) -> Optional[Dict[str, Any]]: return None type_hints = get_type_hints(self.entrypoint) + return_type = type_hints.get("return", None) + returns = None + if return_type is not None: + returns = self.get_type_name(return_type) + function_info = { "name": self.name, "description": self.description, "arguments": self.parameters.get("properties", {}), - "returns": type_hints.get("return", "void").__name__, + "returns": returns, } return function_info @@ -128,7 +138,7 @@ def execute(self) -> bool: return True except Exception as e: logger.warning(f"Could not run function {self.get_call_str()}") - logger.error(e) + logger.exception(e) self.result = str(e) return False @@ -137,6 +147,6 @@ def execute(self) -> bool: return True except Exception as e: logger.warning(f"Could not run function {self.get_call_str()}") - logger.error(e) + logger.exception(e) self.result = str(e) return False diff --git a/phi/tools/newspaper_toolkit.py b/phi/tools/newspaper_toolkit.py new file mode 100644 index 000000000..a474e627a --- /dev/null +++ b/phi/tools/newspaper_toolkit.py @@ -0,0 +1,35 @@ +from phi.tools import Toolkit + +try: + from newspaper import Article +except ImportError: + raise ImportError("`newspaper3k` not installed.") + + +class NewspaperToolkit(Toolkit): + def __init__( + self, + get_article_text: bool = True, + ): + super().__init__(name="newspaper_toolkit") + + if get_article_text: + self.register(self.get_article_text) + + def get_article_text(self, url: str) -> str: + """Get the text of an article from a URL. + + Args: + url (str): The URL of the article. + + Returns: + str: The text of the article. + """ + + try: + article = Article(url) + article.download() + article.parse() + return article.text + except Exception as e: + return f"Error getting article text from {url}: {e}" diff --git a/phi/tools/serpapi_toolkit.py b/phi/tools/serpapi_toolkit.py index c8e845e17..5d19dc173 100644 --- a/phi/tools/serpapi_toolkit.py +++ b/phi/tools/serpapi_toolkit.py @@ -1,5 +1,6 @@ -from typing import Optional import json +from os import getenv +from typing import Optional from phi.tools import Toolkit from phi.utils.log import logger @@ -7,20 +8,20 @@ try: import serpapi except ImportError: - raise ImportError("`serpapi` not installed.") + raise ImportError("`google-search-results` not installed.") -class SerpapiToolkit(Toolkit): +class SerpApiToolkit(Toolkit): def __init__( self, - api_key: Optional[str] = None, + api_key: Optional[str] = getenv("SERPAPI_KEY"), search_youtube: bool = False, ): super().__init__(name="serpapi_tools") self.api_key = api_key if not self.api_key: - logger.error("No Serpapi API key provided") + logger.warning("No Serpapi API key provided") self.register(self.search_google) if search_youtube: diff --git a/pyproject.toml b/pyproject.toml index ce94d2448..6b425e33e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,7 @@ module = [ "langchain.*", "langchain_core.*", "mistralai.*", + "newspaper.*", "numpy.*", "ollama.*", "openai.*",