-
Notifications
You must be signed in to change notification settings - Fork 216
/
web.py
147 lines (125 loc) · 4.35 KB
/
web.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""Util that calls DuckDuckGo Search.
No setup required. Free.
https://pypi.org/project/duckduckgo-search/
"""
from typing import Dict, List, Optional
from langchain_core.pydantic_v1 import BaseModel, Extra, root_validator
class DuckDuckGoSearchAPIWrapper(BaseModel):
"""Wrapper for DuckDuckGo Search API.
Free and does not require any setup.
"""
region: Optional[str] = "wt-wt"
"""
See https://pypi.org/project/duckduckgo-search/#regions
"""
safesearch: str = "moderate"
"""
Options: strict, moderate, off
"""
time: Optional[str] = "y"
"""
Options: d, w, m, y
"""
max_results: int = 5
backend: str = "api"
"""
Options: api, html, lite
"""
source: str = "text"
"""
Options: text, news
"""
class Config:
"""Configuration for this pydantic object."""
extra = Extra.forbid
@root_validator(pre=True)
def validate_environment(cls, values: Dict) -> Dict:
"""Validate that python package exists in environment."""
try:
from duckduckgo_search import DDGS # noqa: F401
except ImportError:
raise ImportError(
"Could not import duckduckgo-search python package. "
"Please install it with `pip install -U duckduckgo-search`."
)
return values
def _ddgs_text(
self, query: str, max_results: Optional[int] = None
) -> List[Dict[str, str]]:
"""Run query through DuckDuckGo text search and return results."""
from duckduckgo_search import DDGS
with DDGS() as ddgs:
ddgs_gen = ddgs.text(
query,
region=self.region,
safesearch=self.safesearch,
timelimit=self.time,
max_results=max_results or self.max_results,
backend=self.backend,
)
if ddgs_gen:
return [r for r in ddgs_gen]
return []
def _ddgs_news(
self, query: str, max_results: Optional[int] = None
) -> List[Dict[str, str]]:
"""Run query through DuckDuckGo news search and return results."""
from duckduckgo_search import DDGS
with DDGS() as ddgs:
ddgs_gen = ddgs.news(
query,
region=self.region,
safesearch=self.safesearch,
timelimit=self.time,
max_results=max_results or self.max_results,
)
if ddgs_gen:
return [r for r in ddgs_gen]
return []
def run(self, query: str) -> str:
"""Run query through DuckDuckGo and return concatenated results."""
if self.source == "text":
results = self._ddgs_text(query)
elif self.source == "news":
results = self._ddgs_news(query)
else:
results = []
if not results:
return "No good DuckDuckGo Search Result was found"
return " ".join(r["body"] for r in results)
def results(
self, query: str, max_results: int, source: Optional[str] = None
) -> List[Dict[str, str]]:
"""Run query through DuckDuckGo and return metadata.
Args:
query: The query to search for.
max_results: The number of results to return.
source: The source to look from.
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.
"""
source = source or self.source
if source == "text":
results = [
{"snippet": r["body"], "title": r["title"], "link": r["href"]}
for r in self._ddgs_text(query, max_results=max_results)
]
elif source == "news":
results = [
{
"snippet": r["body"],
"title": r["title"],
"link": r["url"],
"date": r["date"],
"source": r["source"],
}
for r in self._ddgs_news(query, max_results=max_results)
]
else:
results = []
if results is None:
results = [{"Result": "No good DuckDuckGo Search Result was found"}]
return results