Spaces:
Runtime error
Runtime error
| # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. ========= | |
| import os | |
| from datetime import datetime | |
| from typing import List, Literal, Optional, Tuple, Union | |
| from camel.toolkits import FunctionTool | |
| from camel.toolkits.base import BaseToolkit | |
| def _process_response( | |
| response, return_type: str | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Process the response based on the specified return type. | |
| This helper method processes the API response and returns the content | |
| in the specified format, which could be a string, a dictionary, or | |
| both. | |
| Args: | |
| response: The response object returned by the API call. | |
| return_type (str): Specifies the format of the return value. It | |
| can be "string" to return the response as a string, "dicts" to | |
| return it as a dictionary, or "both" to return both formats as | |
| a tuple. | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: The processed response, | |
| formatted according to the return_type argument. If "string", | |
| returns the response as a string. If "dicts", returns the | |
| response as a dictionary. If "both", returns a tuple | |
| containing both formats. | |
| Raises: | |
| ValueError: If the return_type provided is invalid. | |
| """ | |
| if return_type == "string": | |
| return response.as_string | |
| elif return_type == "dicts": | |
| return response.as_dicts | |
| elif return_type == "both": | |
| return (response.as_string, response.as_dicts) | |
| else: | |
| raise ValueError(f"Invalid return_type: {return_type}") | |
| class AskNewsToolkit(BaseToolkit): | |
| r"""A class representing a toolkit for interacting with the AskNews API. | |
| This class provides methods for fetching news, stories, and other content | |
| based on user queries using the AskNews API. | |
| """ | |
| def __init__(self): | |
| r"""Initialize the AskNewsToolkit with API clients.The API keys and | |
| credentials are retrieved from environment variables. | |
| """ | |
| from asknews_sdk import AskNewsSDK | |
| client_id = os.environ.get("ASKNEWS_CLIENT_ID") | |
| client_secret = os.environ.get("ASKNEWS_CLIENT_SECRET") | |
| self.asknews_client = AskNewsSDK(client_id, client_secret) | |
| def get_news( | |
| self, | |
| query: str, | |
| n_articles: int = 10, | |
| return_type: Literal["string", "dicts", "both"] = "string", | |
| method: Literal["nl", "kw"] = "kw", | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Fetch news or stories based on a user query. | |
| Args: | |
| query (str): The search query for fetching relevant news. | |
| n_articles (int): Number of articles to include in the response. | |
| (default: :obj:`10`) | |
| return_type (Literal["string", "dicts", "both"]): The format of the | |
| return value. (default: :obj:`"string"`) | |
| method (Literal["nl", "kw"]): The search method, either "nl" for | |
| natural language or "kw" for keyword search. (default: | |
| :obj:`"kw"`) | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: A string, dictionary, | |
| or both containing the news or story content, or error message | |
| if the process fails. | |
| """ | |
| try: | |
| response = self.asknews_client.news.search_news( | |
| query=query, | |
| n_articles=n_articles, | |
| return_type=return_type, | |
| method=method, | |
| ) | |
| return _process_response(response, return_type) | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| def get_stories( | |
| self, | |
| query: str, | |
| categories: List[ | |
| Literal[ | |
| 'Politics', | |
| 'Economy', | |
| 'Finance', | |
| 'Science', | |
| 'Technology', | |
| 'Sports', | |
| 'Climate', | |
| 'Environment', | |
| 'Culture', | |
| 'Entertainment', | |
| 'Business', | |
| 'Health', | |
| 'International', | |
| ] | |
| ], | |
| reddit: int = 3, | |
| expand_updates: bool = True, | |
| max_updates: int = 2, | |
| max_articles: int = 10, | |
| ) -> Union[dict, str]: | |
| r"""Fetch stories based on the provided parameters. | |
| Args: | |
| query (str): The search query for fetching relevant stories. | |
| categories (list): The categories to filter stories by. | |
| reddit (int): Number of Reddit threads to include. | |
| (default: :obj:`3`) | |
| expand_updates (bool): Whether to include detailed updates. | |
| (default: :obj:`True`) | |
| max_updates (int): Maximum number of recent updates per story. | |
| (default: :obj:`2`) | |
| max_articles (int): Maximum number of articles associated with | |
| each update. (default: :obj:`10`) | |
| Returns: | |
| Unio[dict, str]: A dictionary containing the stories and their | |
| associated data, or error message if the process fails. | |
| """ | |
| try: | |
| response = self.asknews_client.stories.search_stories( | |
| query=query, | |
| categories=categories, | |
| reddit=reddit, | |
| expand_updates=expand_updates, | |
| max_updates=max_updates, | |
| max_articles=max_articles, | |
| ) | |
| # Collect only the headline and story content from the updates | |
| stories_data = { | |
| "stories": [ | |
| { | |
| "headline": story.updates[0].headline, | |
| "updates": [ | |
| { | |
| "headline": update.headline, | |
| "story": update.story, | |
| } | |
| for update in story.updates[:max_updates] | |
| ], | |
| } | |
| for story in response.stories | |
| ] | |
| } | |
| return stories_data | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| def get_web_search( | |
| self, | |
| queries: List[str], | |
| return_type: Literal["string", "dicts", "both"] = "string", | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Perform a live web search based on the given queries. | |
| Args: | |
| queries (List[str]): A list of search queries. | |
| return_type (Literal["string", "dicts", "both"]): The format of the | |
| return value. (default: :obj:`"string"`) | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: A string, | |
| dictionary, or both containing the search results, or | |
| error message if the process fails. | |
| """ | |
| try: | |
| response = self.asknews_client.chat.live_web_search( | |
| queries=queries | |
| ) | |
| return _process_response(response, return_type) | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| def search_reddit( | |
| self, | |
| keywords: List[str], | |
| n_threads: int = 5, | |
| return_type: Literal["string", "dicts", "both"] = "string", | |
| method: Literal["nl", "kw"] = "kw", | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Search Reddit based on the provided keywords. | |
| Args: | |
| keywords (List[str]): The keywords to search for on Reddit. | |
| n_threads (int): Number of Reddit threads to summarize and return. | |
| (default: :obj:`5`) | |
| return_type (Literal["string", "dicts", "both"]): The format of the | |
| return value. (default: :obj:`"string"`) | |
| method (Literal["nl", "kw"]): The search method, either "nl" for | |
| natural language or "kw" for keyword search. | |
| (default: :obj:`"kw"`) | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: The Reddit search | |
| results as a string, dictionary, or both, or error message if | |
| the process fails. | |
| """ | |
| try: | |
| response = self.asknews_client.news.search_reddit( | |
| keywords=keywords, n_threads=n_threads, method=method | |
| ) | |
| return _process_response(response, return_type) | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| def query_finance( | |
| self, | |
| asset: Literal[ | |
| 'bitcoin', | |
| 'ethereum', | |
| 'cardano', | |
| 'uniswap', | |
| 'ripple', | |
| 'solana', | |
| 'polkadot', | |
| 'polygon', | |
| 'chainlink', | |
| 'tether', | |
| 'dogecoin', | |
| 'monero', | |
| 'tron', | |
| 'binance', | |
| 'aave', | |
| 'tesla', | |
| 'microsoft', | |
| 'amazon', | |
| ], | |
| metric: Literal[ | |
| 'news_positive', | |
| 'news_negative', | |
| 'news_total', | |
| 'news_positive_weighted', | |
| 'news_negative_weighted', | |
| 'news_total_weighted', | |
| ] = "news_positive", | |
| return_type: Literal["list", "string"] = "string", | |
| date_from: Optional[datetime] = None, | |
| date_to: Optional[datetime] = None, | |
| ) -> Union[list, str]: | |
| r"""Fetch asset sentiment data for a given asset, metric, and date | |
| range. | |
| Args: | |
| asset (Literal): The asset for which to fetch sentiment data. | |
| metric (Literal): The sentiment metric to analyze. | |
| return_type (Literal["list", "string"]): The format of the return | |
| value. (default: :obj:`"string"`) | |
| date_from (datetime, optional): The start date and time for the | |
| data in ISO 8601 format. | |
| date_to (datetime, optional): The end date and time for the data | |
| in ISO 8601 format. | |
| Returns: | |
| Union[list, str]: A list of dictionaries containing the datetime | |
| and value or a string describing all datetime and value pairs | |
| for providing quantified time-series data for news sentiment | |
| on topics of interest, or an error message if the process | |
| fails. | |
| """ | |
| try: | |
| response = self.asknews_client.analytics.get_asset_sentiment( | |
| asset=asset, | |
| metric=metric, | |
| date_from=date_from, | |
| date_to=date_to, | |
| ) | |
| time_series_data = response.data.timeseries | |
| if return_type == "list": | |
| return time_series_data | |
| elif return_type == "string": | |
| header = ( | |
| f"This is the sentiment analysis for '{asset}' based " | |
| + f"on the '{metric}' metric from {date_from} to {date_to}" | |
| + ". The values reflect the aggregated sentiment from news" | |
| + " sources for each given time period.\n" | |
| ) | |
| descriptive_text = "\n".join( | |
| [ | |
| f"On {entry.datetime}, the sentiment value was " | |
| f"{entry.value}." | |
| for entry in time_series_data | |
| ] | |
| ) | |
| return header + descriptive_text | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| def get_tools(self) -> List[FunctionTool]: | |
| r"""Returns a list of FunctionTool objects representing the functions | |
| in the toolkit. | |
| Returns: | |
| List[FunctionTool]: A list of FunctionTool objects representing | |
| the functions in the toolkit. | |
| """ | |
| return [ | |
| FunctionTool(self.get_news), | |
| FunctionTool(self.get_stories), | |
| FunctionTool(self.get_web_search), | |
| FunctionTool(self.search_reddit), | |
| FunctionTool(self.query_finance), | |
| ] | |
| class AsyncAskNewsToolkit(BaseToolkit): | |
| r"""A class representing a toolkit for interacting with the AskNews API | |
| asynchronously. | |
| This class provides methods for fetching news, stories, and other | |
| content based on user queries using the AskNews API. | |
| """ | |
| def __init__(self): | |
| r"""Initialize the AsyncAskNewsToolkit with API clients.The API keys | |
| and credentials are retrieved from environment variables. | |
| """ | |
| from asknews_sdk import AsyncAskNewsSDK # type: ignore[import] | |
| client_id = os.environ.get("ASKNEWS_CLIENT_ID") | |
| client_secret = os.environ.get("ASKNEWS_CLIENT_SECRET") | |
| self.asknews_client = AsyncAskNewsSDK(client_id, client_secret) | |
| async def get_news( | |
| self, | |
| query: str, | |
| n_articles: int = 10, | |
| return_type: Literal["string", "dicts", "both"] = "string", | |
| method: Literal["nl", "kw"] = "kw", | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Fetch news or stories based on a user query. | |
| Args: | |
| query (str): The search query for fetching relevant news or | |
| stories. | |
| n_articles (int): Number of articles to include in the response. | |
| (default: :obj:10) | |
| return_type (Literal["string", "dicts", "both"]): The format of the | |
| return value. (default: :obj:"string") | |
| method (Literal["nl", "kw"]): The search method, either "nl" for | |
| natural language or "kw" for keyword search. (default: | |
| :obj:"kw") | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: A string, | |
| dictionary, or both containing the news or story content, or | |
| error message if the process fails. | |
| """ | |
| try: | |
| response = await self.asknews_client.news.search_news( | |
| query=query, | |
| n_articles=n_articles, | |
| return_type=return_type, | |
| method=method, | |
| ) | |
| return _process_response(response, return_type) | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| async def get_stories( | |
| self, | |
| query: str, | |
| categories: List[ | |
| Literal[ | |
| 'Politics', | |
| 'Economy', | |
| 'Finance', | |
| 'Science', | |
| 'Technology', | |
| 'Sports', | |
| 'Climate', | |
| 'Environment', | |
| 'Culture', | |
| 'Entertainment', | |
| 'Business', | |
| 'Health', | |
| 'International', | |
| ] | |
| ], | |
| reddit: int = 3, | |
| expand_updates: bool = True, | |
| max_updates: int = 2, | |
| max_articles: int = 10, | |
| ) -> Union[dict, str]: | |
| r"""Fetch stories based on the provided parameters. | |
| Args: | |
| query (str): The search query for fetching relevant stories. | |
| categories (list): The categories to filter stories by. | |
| reddit (int): Number of Reddit threads to include. | |
| (default: :obj:`3`) | |
| expand_updates (bool): Whether to include detailed updates. | |
| (default: :obj:`True`) | |
| max_updates (int): Maximum number of recent updates per story. | |
| (default: :obj:`2`) | |
| max_articles (int): Maximum number of articles associated with | |
| each update. (default: :obj:`10`) | |
| Returns: | |
| Unio[dict, str]: A dictionary containing the stories and their | |
| associated data, or error message if the process fails. | |
| """ | |
| try: | |
| response = await self.asknews_client.stories.search_stories( | |
| query=query, | |
| categories=categories, | |
| reddit=reddit, | |
| expand_updates=expand_updates, | |
| max_updates=max_updates, | |
| max_articles=max_articles, | |
| ) | |
| # Collect only the headline and story content from the updates | |
| stories_data = { | |
| "stories": [ | |
| { | |
| "headline": story.updates[0].headline, | |
| "updates": [ | |
| { | |
| "headline": update.headline, | |
| "story": update.story, | |
| } | |
| for update in story.updates[:max_updates] | |
| ], | |
| } | |
| for story in response.stories | |
| ] | |
| } | |
| return stories_data | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| async def get_web_search( | |
| self, | |
| queries: List[str], | |
| return_type: Literal["string", "dicts", "both"] = "string", | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Perform a live web search based on the given queries. | |
| Args: | |
| queries (List[str]): A list of search queries. | |
| return_type (Literal["string", "dicts", "both"]): The format of the | |
| return value. (default: :obj:`"string"`) | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: A string, | |
| dictionary, or both containing the search results, or | |
| error message if the process fails. | |
| """ | |
| try: | |
| response = await self.asknews_client.chat.live_web_search( | |
| queries=queries | |
| ) | |
| return _process_response(response, return_type) | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| async def search_reddit( | |
| self, | |
| keywords: List[str], | |
| n_threads: int = 5, | |
| return_type: Literal["string", "dicts", "both"] = "string", | |
| method: Literal["nl", "kw"] = "kw", | |
| ) -> Union[str, dict, Tuple[str, dict]]: | |
| r"""Search Reddit based on the provided keywords. | |
| Args: | |
| keywords (list): The keywords to search for on Reddit. | |
| n_threads (int): Number of Reddit threads to summarize and return. | |
| (default: :obj:5) | |
| return_type (Literal["string", "dicts", "both"]): The format of the | |
| return value. (default: :obj:"string") | |
| method (Literal["nl", "kw"]): The search method, either "nl" for | |
| natural language or "kw" for keyword search. | |
| (default: :obj:"kw") | |
| Returns: | |
| Union[str, dict, Tuple[str, dict]]: The Reddit search | |
| results as a string, dictionary, or both, or error message if | |
| the process fails. | |
| """ | |
| try: | |
| response = await self.asknews_client.news.search_reddit( | |
| keywords=keywords, n_threads=n_threads, method=method | |
| ) | |
| return _process_response(response, return_type) | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| async def query_finance( | |
| self, | |
| asset: Literal[ | |
| 'bitcoin', | |
| 'ethereum', | |
| 'cardano', | |
| 'uniswap', | |
| 'ripple', | |
| 'solana', | |
| 'polkadot', | |
| 'polygon', | |
| 'chainlink', | |
| 'tether', | |
| 'dogecoin', | |
| 'monero', | |
| 'tron', | |
| 'binance', | |
| 'aave', | |
| 'tesla', | |
| 'microsoft', | |
| 'amazon', | |
| ], | |
| metric: Literal[ | |
| 'news_positive', | |
| 'news_negative', | |
| 'news_total', | |
| 'news_positive_weighted', | |
| 'news_negative_weighted', | |
| 'news_total_weighted', | |
| ] = "news_positive", | |
| return_type: Literal["list", "string"] = "string", | |
| date_from: Optional[datetime] = None, | |
| date_to: Optional[datetime] = None, | |
| ) -> Union[list, str]: | |
| r"""Fetch asset sentiment data for a given asset, metric, and date | |
| range. | |
| Args: | |
| asset (Literal): The asset for which to fetch sentiment data. | |
| metric (Literal): The sentiment metric to analyze. | |
| return_type (Literal["list", "string"]): The format of the return | |
| value. (default: :obj:`"string"`) | |
| date_from (datetime, optional): The start date and time for the | |
| data in ISO 8601 format. | |
| date_to (datetime, optional): The end date and time for the data | |
| in ISO 8601 format. | |
| Returns: | |
| Union[list, str]: A list of dictionaries containing the datetime | |
| and value or a string describing all datetime and value pairs | |
| for providing quantified time-series data for news sentiment | |
| on topics of interest, or an error message if the process | |
| fails. | |
| """ | |
| try: | |
| response = await self.asknews_client.analytics.get_asset_sentiment( | |
| asset=asset, | |
| metric=metric, | |
| date_from=date_from, | |
| date_to=date_to, | |
| ) | |
| time_series_data = response.data.timeseries | |
| if return_type == "list": | |
| return time_series_data | |
| elif return_type == "string": | |
| header = ( | |
| f"This is the sentiment analysis for '{asset}' based " | |
| + f"on the '{metric}' metric from {date_from} to {date_to}" | |
| + ". The values reflect the aggregated sentiment from news" | |
| + " sources for each given time period.\n" | |
| ) | |
| descriptive_text = "\n".join( | |
| [ | |
| f"On {entry.datetime}, the sentiment value was " | |
| f"{entry.value}." | |
| for entry in time_series_data | |
| ] | |
| ) | |
| return header + descriptive_text | |
| except Exception as e: | |
| return f"Got error: {e}" | |
| def get_tools(self) -> List[FunctionTool]: | |
| r"""Returns a list of FunctionTool objects representing the functions | |
| in the toolkit. | |
| Returns: | |
| List[FunctionTool]: A list of FunctionTool objects representing | |
| the functions in the toolkit. | |
| """ | |
| return [ | |
| FunctionTool(self.get_news), | |
| FunctionTool(self.get_stories), | |
| FunctionTool(self.get_web_search), | |
| FunctionTool(self.search_reddit), | |
| FunctionTool(self.query_finance), | |
| ] | |