community[patch]: adding artifact to Tavily search (#24376)

This allows you to get raw content as well as the answer, instead of
just getting the results.

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
This commit is contained in:
Isaac Francisco 2024-08-01 21:12:11 -07:00 committed by GitHub
parent 7b08de8909
commit d7688a4328
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,6 +1,6 @@
"""Tool for the Tavily search API.""" """Tool for the Tavily search API."""
from typing import Dict, List, Optional, Type, Union from typing import Dict, List, Literal, Optional, Tuple, Type, Union
from langchain_core.callbacks import ( from langchain_core.callbacks import (
AsyncCallbackManagerForToolRun, AsyncCallbackManagerForToolRun,
@ -33,35 +33,61 @@ class TavilySearchResults(BaseTool):
.. code-block:: python .. code-block:: python
from langchain_community.tools.tavily_search import TavilySearchResults from langchain_community.tools import TavilySearchResults
tool = TavilySearchResults( tool = TavilySearchResults(
# max_results= 5 max_results=5,
# search_depth = "advanced" include_answer=True,
include_raw_content=True,
include_images=True,
# search_depth="advanced",
# include_domains = [] # include_domains = []
# exclude_domains = [] # exclude_domains = []
# include_answer = False
# include_raw_content = False
# include_images = False
) )
Invoke: Invoke directly with args:
.. code-block:: python .. code-block:: python
tool = TavilySearchResults(max_results=3) tool.invoke({'query': 'who won the last french open'})
tool.invoke("What is the weather?")
.. code-block:: python .. code-block:: python
[{'url': 'https://www.weatherapi.com/', '{\n "answer": "Novak Djokovic won the last French Open by beating Casper Ruud ...',
'content': "{'location': {'name': 'Current', 'region': 'Harbour Island', 'country': 'Bahamas', 'lat': 25.43, 'lon': -76.78, 'tz_id': 'America/Nassau', 'localtime_epoch': 1718077801, 'localtime': '2024-06-10 23:50'}, 'current': {'last_updated_epoch': 1718077500, 'last_updated': '2024-06-10 23:45', 'temp_c': 27.9, 'temp_f': 82.1, 'is_day': 0, 'condition': {'text': 'Patchy rain nearby', 'icon': '//cdn.weatherapi.com/weather/64x64/night/176.png', 'code': 1063}, 'wind_mph': 14.5, 'wind_kph': 23.4, 'wind_degree': 161, 'wind_dir': 'SSE', 'pressure_mb': 1014.0, 'pressure_in': 29.94, 'precip_mm': 0.01, 'precip_in': 0.0, 'humidity': 88, 'cloud': 74, 'feelslike_c': 33.1, 'feelslike_f': 91.5, 'windchill_c': 27.9, 'windchill_f': 82.1, 'heatindex_c': 33.1, 'heatindex_f': 91.5, 'dewpoint_c': 25.6, 'dewpoint_f': 78.1, 'vis_km': 10.0, 'vis_miles': 6.0, 'uv': 1.0, 'gust_mph': 20.4, 'gust_kph': 32.9}}"},
{'url': 'https://www.localconditions.com/weather-ninnescah-kansas/67069/',
'content': 'The following chart reports what the hourly Ninnescah, KS temperature has been today, from 12:56 AM to 3:56 AM Tue, May 21st 2024. The lowest temperature reading has been 73.04 degrees fahrenheit at 3:56 AM, while the highest temperature is 75.92 degrees fahrenheit at 12:56 AM. Ninnescah KS detailed current weather report for 67069 in Kansas.'},
{'url': 'https://www.weather.gov/forecastmaps/',
'content': 'Short Range Forecasts. Short range forecast products depicting pressure patterns, circulation centers and fronts, and types and extent of precipitation. 12 Hour | 24 Hour | 36 Hour | 48 Hour.'}]
When converting ``TavilySearchResults`` to a tool, you may want to not return all of the content resulting from ``invoke``. You can select what parts of the response to keep depending on your use case. Invoke with tool call:
.. code-block:: python
tool.invoke({"args": {'query': 'who won the last french open'}, "type": "tool_call", "id": "foo", "name": "tavily"})
.. code-block:: python
ToolMessage(
content='{\n "answer": "Novak Djokovic won the last French Open by beating Casper Ruud ...',
artifact={
'query': 'who won the last french open',
'follow_up_questions': None,
'answer': 'Novak ...',
'images': [
'https://www.amny.com/wp-content/uploads/2023/06/AP23162622181176-1200x800.jpg',
...
],
'results': [
{
'title': 'Djokovic ...',
'url': 'https://www.nytimes.com...',
'content': "Novak...",
'score': 0.99505633,
'raw_content': 'Tennis\nNovak ...'
},
...
],
'response_time': 2.92
},
tool_call_id='1',
name='tavily_search_results_json',
)
""" # noqa: E501 """ # noqa: E501
@ -71,7 +97,9 @@ class TavilySearchResults(BaseTool):
"Useful for when you need to answer questions about current events. " "Useful for when you need to answer questions about current events. "
"Input should be a search query." "Input should be a search query."
) )
api_wrapper: TavilySearchAPIWrapper = Field(default_factory=TavilySearchAPIWrapper) # type: ignore[arg-type] args_schema: Type[BaseModel] = TavilyInput
"""The tool response format."""
max_results: int = 5 max_results: int = 5
"""Max search results to return, default is 5""" """Max search results to return, default is 5"""
search_depth: str = "advanced" search_depth: str = "advanced"
@ -86,16 +114,19 @@ class TavilySearchResults(BaseTool):
"""Include cleaned and parsed HTML of each site search results. Default is False.""" """Include cleaned and parsed HTML of each site search results. Default is False."""
include_images: bool = False include_images: bool = False
"""Include a list of query related images in the response. Default is False.""" """Include a list of query related images in the response. Default is False."""
args_schema: Type[BaseModel] = TavilyInput
api_wrapper: TavilySearchAPIWrapper = Field(default_factory=TavilySearchAPIWrapper) # type: ignore[arg-type]
response_format: Literal["content_and_artifact"] = "content_and_artifact"
def _run( def _run(
self, self,
query: str, query: str,
run_manager: Optional[CallbackManagerForToolRun] = None, run_manager: Optional[CallbackManagerForToolRun] = None,
) -> Union[List[Dict], str]: ) -> Tuple[Union[List[Dict[str, str]], str], Dict]:
"""Use the tool.""" """Use the tool."""
# TODO: remove try/except, should be handled by BaseTool
try: try:
return self.api_wrapper.results( raw_results = self.api_wrapper.raw_results(
query, query,
self.max_results, self.max_results,
self.search_depth, self.search_depth,
@ -106,16 +137,17 @@ class TavilySearchResults(BaseTool):
self.include_images, self.include_images,
) )
except Exception as e: except Exception as e:
return repr(e) return repr(e), {}
return self.api_wrapper.clean_results(raw_results["results"]), raw_results
async def _arun( async def _arun(
self, self,
query: str, query: str,
run_manager: Optional[AsyncCallbackManagerForToolRun] = None, run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
) -> Union[List[Dict], str]: ) -> Tuple[Union[List[Dict[str, str]], str], Dict]:
"""Use the tool asynchronously.""" """Use the tool asynchronously."""
try: try:
return await self.api_wrapper.results_async( raw_results = await self.api_wrapper.raw_results_async(
query, query,
self.max_results, self.max_results,
self.search_depth, self.search_depth,
@ -126,7 +158,8 @@ class TavilySearchResults(BaseTool):
self.include_images, self.include_images,
) )
except Exception as e: except Exception as e:
return repr(e) return repr(e), {}
return self.api_wrapper.clean_results(raw_results["results"]), raw_results
class TavilyAnswer(BaseTool): class TavilyAnswer(BaseTool):