forked from Archives/langchain
Harrison/ddg (#3206)
Co-authored-by: itai <itai.marks@gmail.com> Co-authored-by: Itai Marks <itaim@users.noreply.github.com> Co-authored-by: Tianyi Pan <60060750+tipani86@users.noreply.github.com> Co-authored-by: Tianyi Pan <tianyi.pan@clobotics.com> Co-authored-by: Adilzhan Ismailov <13088690+aismlv@users.noreply.github.com> Co-authored-by: Justin Flick <Justinjayflick@gmail.com> Co-authored-by: Justin Flick <jflick@homesite.com>fix_agent_callbacks
parent
36c10f8a52
commit
d2520a5f1e
@ -1,9 +1,17 @@
|
||||
"""Core toolkit implementations."""
|
||||
|
||||
from langchain.tools.base import BaseTool
|
||||
from langchain.tools.ddg_search.tool import DuckDuckGoSearchTool
|
||||
from langchain.tools.ifttt import IFTTTWebhook
|
||||
from langchain.tools.openapi.utils.api_models import APIOperation
|
||||
from langchain.tools.openapi.utils.openapi_utils import OpenAPISpec
|
||||
from langchain.tools.plugin import AIPluginTool
|
||||
|
||||
__all__ = ["BaseTool", "IFTTTWebhook", "AIPluginTool", "OpenAPISpec", "APIOperation"]
|
||||
__all__ = [
|
||||
"BaseTool",
|
||||
"IFTTTWebhook",
|
||||
"AIPluginTool",
|
||||
"OpenAPISpec",
|
||||
"APIOperation",
|
||||
"DuckDuckGoSearchTool",
|
||||
]
|
||||
|
@ -0,0 +1 @@
|
||||
"""DuckDuckGo Search API toolkit."""
|
@ -0,0 +1,28 @@
|
||||
"""Tool for the DuckDuckGo search API."""
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from langchain.tools.base import BaseTool
|
||||
from langchain.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper
|
||||
|
||||
|
||||
class DuckDuckGoSearchTool(BaseTool):
|
||||
"""Tool that adds the capability to query the DuckDuckGo search API."""
|
||||
|
||||
name = "DuckDuckGo Search"
|
||||
description = (
|
||||
"A wrapper around DuckDuckGo Search. "
|
||||
"Useful for when you need to answer questions about current events. "
|
||||
"Input should be a search query."
|
||||
)
|
||||
api_wrapper: DuckDuckGoSearchAPIWrapper = Field(
|
||||
default_factory=DuckDuckGoSearchAPIWrapper
|
||||
)
|
||||
|
||||
def _run(self, query: str) -> str:
|
||||
"""Use the tool."""
|
||||
return self.api_wrapper.run(query)
|
||||
|
||||
async def _arun(self, query: str) -> str:
|
||||
"""Use the tool asynchronously."""
|
||||
raise NotImplementedError("DuckDuckGoSearch does not support async")
|
@ -0,0 +1,90 @@
|
||||
"""Util that calls DuckDuckGo Search.
|
||||
|
||||
No setup required. Free.
|
||||
https://pypi.org/project/duckduckgo-search/
|
||||
"""
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
from pydantic.class_validators import root_validator
|
||||
|
||||
|
||||
class DuckDuckGoSearchAPIWrapper(BaseModel):
|
||||
"""Wrapper for DuckDuckGo Search API.
|
||||
|
||||
Free and does not require any setup
|
||||
"""
|
||||
|
||||
k: int = 10
|
||||
region: Optional[str] = "wt-wt"
|
||||
safesearch: str = "moderate"
|
||||
time: Optional[str] = "y"
|
||||
max_results: int = 5
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
extra = Extra.forbid
|
||||
|
||||
@root_validator()
|
||||
def validate_environment(cls, values: Dict) -> Dict:
|
||||
"""Validate that python package exists in environment."""
|
||||
try:
|
||||
from duckduckgo_search import ddg # noqa: F401
|
||||
except ImportError:
|
||||
raise ValueError(
|
||||
"Could not import duckduckgo-search python package. "
|
||||
"Please install it with `pip install duckduckgo-search`."
|
||||
)
|
||||
return values
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
from duckduckgo_search import ddg
|
||||
|
||||
"""Run query through DuckDuckGo and return results."""
|
||||
results = ddg(
|
||||
query,
|
||||
region=self.region,
|
||||
safesearch=self.safesearch,
|
||||
time=self.time,
|
||||
max_results=self.max_results,
|
||||
)
|
||||
if len(results) == 0:
|
||||
return "No good DuckDuckGo Search Result was found"
|
||||
snippets = [result["body"] for result in results]
|
||||
return " ".join(snippets)
|
||||
|
||||
def results(self, query: str, num_results: int) -> List[Dict]:
|
||||
"""Run query through DuckDuckGo and return metadata.
|
||||
|
||||
Args:
|
||||
query: The query to search for.
|
||||
num_results: The number of results to return.
|
||||
|
||||
Returns:
|
||||
A list of dictionaries with the following keys:
|
||||
snippet - The description of the result.
|
||||
title - The title of the result.
|
||||
link - The link to the result.
|
||||
"""
|
||||
from duckduckgo_search import ddg
|
||||
|
||||
results = ddg(
|
||||
query,
|
||||
region=self.region,
|
||||
safesearch=self.safesearch,
|
||||
time=self.time,
|
||||
max_results=num_results,
|
||||
)
|
||||
|
||||
if len(results) == 0:
|
||||
return [{"Result": "No good DuckDuckGo Search Result was found"}]
|
||||
|
||||
def to_metadata(result: Dict) -> Dict:
|
||||
return {
|
||||
"snippet": result["body"],
|
||||
"title": result["title"],
|
||||
"link": result["href"],
|
||||
}
|
||||
|
||||
return [to_metadata(result) for result in results]
|
@ -0,0 +1,22 @@
|
||||
import pytest
|
||||
|
||||
from langchain.tools.ddg_search.tool import DuckDuckGoSearchTool
|
||||
|
||||
|
||||
def ddg_installed() -> bool:
|
||||
try:
|
||||
from duckduckgo_search import ddg # noqa: F401
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"duckduckgo not installed, skipping test {e}")
|
||||
return False
|
||||
|
||||
|
||||
@pytest.mark.skipif(not ddg_installed(), reason="requires duckduckgo-search package")
|
||||
def test_ddg_search_tool() -> None:
|
||||
keywords = "Bella Ciao"
|
||||
tool = DuckDuckGoSearchTool()
|
||||
result = tool(keywords)
|
||||
print(result)
|
||||
assert len(result.split()) > 20
|
Loading…
Reference in New Issue