langchain icon indicating copy to clipboard operation
langchain copied to clipboard

Async Support | Search Tools

Open jordanparker6 opened this issue 1 year ago • 9 comments

Feature request

None of the search tools have async support.

Motivation

Async calls are being used langchain setup with FastAPI. Not having async support for these tools blocks there use.

Your contribution

Happy to help out where needed.

jordanparker6 avatar May 20 '23 01:05 jordanparker6

I faced the same problem and created a custom tool to handle it. here's the code for your reference.

import os
from langchain.agents import Tool
from dotenv import load_dotenv
import aiohttp

async def search_bing(query):
    subscription_key = str(os.getenv("BING_SUBSCRIPTION_KEY"))
    endpoint = str(os.getenv("BING_SEARCH_URL"))
    headers = {"Ocp-Apim-Subscription-Key": subscription_key}
    params = {"q": query, "count": 3}
    async with aiohttp.ClientSession() as session:
        async with session.get(endpoint, headers=headers, params=params) as response:
            response.raise_for_status()
            return await response.json()

a_bing_tool = Tool(
        name = "search",
        func=search_bing,
        description="useful for when you need to answer questions about current events",
        coroutine=search_bing
    )

somethingwentwell avatar May 25 '23 04:05 somethingwentwell

Hi, @jordanparker6! I'm Dosu, and I'm helping the LangChain team manage their backlog. I wanted to let you know that we are marking this issue as stale.

From what I understand, this issue is a feature request to add async support to the search tools. It seems that not having async support is blocking the use of these tools in a FastAPI setup. Another user named somethingwentwell faced the same problem and created a custom tool to handle it, providing code as a reference. This comment received positive reactions from other users, indicating that it may be a viable solution.

Before we close this issue, we wanted to check with you if it is still relevant to the latest version of the LangChain repository. If it is, please let us know by commenting on the issue. Otherwise, feel free to close the issue yourself or it will be automatically closed in 7 days.

Thank you for your understanding and contribution to the LangChain project!

dosubot[bot] avatar Sep 15 '23 16:09 dosubot[bot]

I think a more general purpose solution available to make any tool async (not fully native async but does the purpose) Use asyncer library to convert a sync function to async. For example DuckDuckGo search tool doesn't have a _arun function

but it is pretty easy to implement one

from asyncer import asyncify

class DuckDuckGoSearchRun(BaseTool):
    """Tool that queries the DuckDuckGo search API."""

    name = "duckduckgo_search"
    description = ("...")
    
    ...

    def _run(...) -> Any:
        "..."
           
    def _arun(self, *args: Any, **kwargs: Any) -> Any:
        """Async version of the tool"""
        return asyncify(self._run, cancellable=True)(*args, **kwargs)

oneryalcin avatar Sep 19 '23 11:09 oneryalcin

@baskaryan Could you please help @jordanparker6 with this issue? They have indicated that the issue is still relevant and provided a potential solution using the asyncer library. Thank you!

dosubot[bot] avatar Sep 19 '23 11:09 dosubot[bot]

Please disregard as this has been addressed in https://github.com/langchain-ai/langchain/pull/10058 recently. No need anymore

oneryalcin avatar Sep 19 '23 13:09 oneryalcin

Please disregard as this has been addressed in #10058 recently. No need anymore

Is there an example of how to use this async tool? I have used the arun and run but still it is calling run though I run this tool with the agent using await

sowsan avatar Dec 03 '23 16:12 sowsan

Also can we use async with tool decorator ? I am using latest version and when I use the code like below it says tool doesnt support async

@tool
async def profile_summary_data_retriever() -> str:
    """Use this for finding user specific portfolio summary and alters performance"""
    try:

        api_url = "http://api"  
       # response = requests.get(api_url)
        async with httpx.AsyncClient() as client:
            response = await client.get(api_url)
            alters_data = response.json()
        return alters_data
       
    except Exception as e: print(f"Error: {e}")

sowsan avatar Dec 03 '23 16:12 sowsan

I have the same problem. My agent chain needs to call the function several times for different parameters, and it doesn't seem to perform that asynchronously. I'm using llm_agent.ainvoke function, and defined my tool function to have _run and _arun properly. Doesn't seem to work. It is still calling tool sequencially

weijie7 avatar Jan 15 '24 06:01 weijie7

🤖

Thank you for your feature request and your willingness to contribute. It seems there might be a misunderstanding about the current async support in LangChain. The search tools in LangChain, specifically the TavilySearchAPIWrapper class, already have async support. This is evident from the raw_results_async and results_async methods in the class. These methods use the aiohttp library to make asynchronous HTTP requests to the Tavily Search API. Here is the code for these methods:

# raw_results_async method
async def raw_results_async(
        self,
        query: str,
        max_results: Optional[int] = 5,
        search_depth: Optional[str] = "advanced",
        include_domains: Optional[List[str]] = [],
        exclude_domains: Optional[List[str]] = [],
        include_answer: Optional[bool] = False,
        include_raw_content: Optional[bool] = False,
        include_images: Optional[bool] = False,
    ) -> Dict:
        """Get results from the Tavily Search API asynchronously."""

        # Function to perform the API call
        async def fetch() -> str:
            params = {
                "api_key": self.tavily_api_key.get_secret_value(),
                "query": query,
                "max_results": max_results,
                "search_depth": search_depth,
                "include_domains": include_domains,
                "exclude_domains": exclude_domains,
                "include_answer": include_answer,
                "include_raw_content": include_raw_content,
                "include_images": include_images,
            }
            async with aiohttp.ClientSession() as session:
                async with session.post(f"{TAVILY_API_URL}/search", json=params) as res:
                    if res.status == 200:
                        data = await res.text()
                        return data
                    else:
                        raise Exception(f"Error {res.status}: {res.reason}")

        results_json_str = await fetch()
        return json.loads(results_json_str)

# results_async method
async def results_async(
        self,
        query: str,
        max_results: Optional[int] = 5,
        search_depth: Optional[str] = "advanced",
        include_domains: Optional[List[str]] = [],
        exclude_domains: Optional[List[str]] = [],
        include_answer: Optional[bool] = False,
        include_raw_content: Optional[bool] = False,
        include_images: Optional[bool] = False,
    ) -> List[Dict]:
        results_json = await self.raw_results_async(
            query=query,
            max_results=max_results,
            search_depth=search_depth,
            include_domains=include_domains,
            exclude_domains=exclude_domains,
            include_answer=include_answer,
            include_raw_content=include_raw_content,
            include_images=include_images,
        )
        return self.clean_results(results_json["results"])

You can find these methods in the TavilySearchAPIWrapper class.

However, if you're referring to other search tools in the LangChain codebase, such as BingSearchRun, DuckDuckGoSearchRun, GoogleSearchResults, GoogleSearchRun, MetaphorSearchResults, GoogleSerperResults, GoogleSerperRun, SearchAPIResults, SearchAPIRun, SearxSearchResults, SearxSearchRun, or RedditSearchRun, could you please provide more specific details about the async support you're looking for? This will help us understand your request better and provide a more accurate response.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

dosubot[bot] avatar Jan 19 '24 16:01 dosubot[bot]