Shrijeeth-Suresh
commited on
Commit
·
9d2647f
1
Parent(s):
5069259
refactor: implement base interface wrappers and add Serper search engine
Browse files- Makefile +4 -1
- app.py +6 -25
- app_wrapper.py +26 -0
- register_interfaces.py +16 -0
- requirements.txt +2 -1
- search_engines/base.py +21 -0
- search_engines/duckduckgo.py +18 -18
- search_engines/searxng.py +18 -19
- search_engines/serpapi.py +19 -19
- search_engines/serper.py +52 -0
- search_engines/tavily.py +19 -19
- utils/constants.py +0 -0
Makefile
CHANGED
@@ -8,4 +8,7 @@ lint:
|
|
8 |
python -m ruff check --select I,RUF022 --fix .
|
9 |
|
10 |
check:
|
11 |
-
python -m ruff check .
|
|
|
|
|
|
|
|
8 |
python -m ruff check --select I,RUF022 --fix .
|
9 |
|
10 |
check:
|
11 |
+
python -m ruff check .
|
12 |
+
|
13 |
+
run:
|
14 |
+
python app.py
|
app.py
CHANGED
@@ -1,27 +1,8 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
from search_engines.duckduckgo import duckduckgo_interface
|
4 |
-
from search_engines.searxng import searxng_interface
|
5 |
-
from search_engines.serpapi import serpapi_interface
|
6 |
-
from search_engines.tavily import tavily_interface
|
7 |
-
|
8 |
-
app = gr.TabbedInterface(
|
9 |
-
interface_list=[
|
10 |
-
duckduckgo_interface,
|
11 |
-
tavily_interface,
|
12 |
-
searxng_interface,
|
13 |
-
serpapi_interface,
|
14 |
-
],
|
15 |
-
tab_names=[
|
16 |
-
"DuckDuckGo Search",
|
17 |
-
"Tavily Search",
|
18 |
-
"SearxNG Search",
|
19 |
-
"SerpAPI Search",
|
20 |
-
],
|
21 |
-
)
|
22 |
-
|
23 |
|
24 |
if __name__ == "__main__":
|
25 |
-
|
26 |
-
|
27 |
-
)
|
|
|
|
1 |
+
from app_wrapper import AppWrapper
|
2 |
+
from register_interfaces import register_all_interfaces
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
if __name__ == "__main__":
|
5 |
+
wrapper = AppWrapper()
|
6 |
+
register_all_interfaces(wrapper)
|
7 |
+
wrapper.build_app()
|
8 |
+
wrapper.launch(mcp_server=True)
|
app_wrapper.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
|
4 |
+
class AppWrapper:
|
5 |
+
def __init__(self):
|
6 |
+
self._interfaces = [] # List of tuples: (interface_obj, tab_name)
|
7 |
+
|
8 |
+
def register_interface(self, interface_obj, tab_name):
|
9 |
+
"""
|
10 |
+
Register an interface object with a corresponding tab name.
|
11 |
+
"""
|
12 |
+
self._interfaces.append((interface_obj, tab_name))
|
13 |
+
|
14 |
+
def build_app(self):
|
15 |
+
"""
|
16 |
+
Build the Gradio TabbedInterface from registered interfaces.
|
17 |
+
"""
|
18 |
+
interface_list = [iface.interface for iface, _ in self._interfaces]
|
19 |
+
tab_names = [tab for _, tab in self._interfaces]
|
20 |
+
self.app = gr.TabbedInterface(
|
21 |
+
interface_list=interface_list,
|
22 |
+
tab_names=tab_names,
|
23 |
+
)
|
24 |
+
|
25 |
+
def launch(self, **kwargs):
|
26 |
+
self.app.launch(**kwargs)
|
register_interfaces.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from search_engines.duckduckgo import DuckDuckGoInterfaceWrapper
|
2 |
+
from search_engines.searxng import SearxNGInterfaceWrapper
|
3 |
+
from search_engines.serpapi import SerpAPIInterfaceWrapper
|
4 |
+
from search_engines.serper import SerperInterfaceWrapper
|
5 |
+
from search_engines.tavily import TavilyInterfaceWrapper
|
6 |
+
|
7 |
+
|
8 |
+
def register_all_interfaces(wrapper):
|
9 |
+
"""
|
10 |
+
Register all search engine interfaces with the provided AppWrapper instance.
|
11 |
+
"""
|
12 |
+
wrapper.register_interface(DuckDuckGoInterfaceWrapper(), "DuckDuckGo Search")
|
13 |
+
wrapper.register_interface(TavilyInterfaceWrapper(), "Tavily Search")
|
14 |
+
wrapper.register_interface(SearxNGInterfaceWrapper(), "SearxNG Search")
|
15 |
+
wrapper.register_interface(SerpAPIInterfaceWrapper(), "SerpAPI Search")
|
16 |
+
wrapper.register_interface(SerperInterfaceWrapper(), "Serper Search")
|
requirements.txt
CHANGED
@@ -6,4 +6,5 @@ langchain-community==0.3.24
|
|
6 |
duckduckgo-search==8.0.2
|
7 |
pandas==2.2.3
|
8 |
ruff==0.11.12
|
9 |
-
langchain-tavily==0.2.1
|
|
|
|
6 |
duckduckgo-search==8.0.2
|
7 |
pandas==2.2.3
|
8 |
ruff==0.11.12
|
9 |
+
langchain-tavily==0.2.1
|
10 |
+
wikipedia==1.4.0
|
search_engines/base.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
|
4 |
+
class BaseInterfaceWrapper:
|
5 |
+
def __init__(self, fn, inputs, outputs=None, title=None, description=None):
|
6 |
+
if outputs is None:
|
7 |
+
outputs = gr.Dataframe(
|
8 |
+
label="Search Results",
|
9 |
+
headers=["title", "body", "link"],
|
10 |
+
show_fullscreen_button=True,
|
11 |
+
show_row_numbers=True,
|
12 |
+
show_copy_button=True,
|
13 |
+
wrap=True,
|
14 |
+
)
|
15 |
+
self.interface = gr.Interface(
|
16 |
+
fn=fn,
|
17 |
+
inputs=inputs,
|
18 |
+
outputs=outputs,
|
19 |
+
title=title,
|
20 |
+
description=description,
|
21 |
+
)
|
search_engines/duckduckgo.py
CHANGED
@@ -4,6 +4,8 @@ from langchain_community.tools import DuckDuckGoSearchResults
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
|
|
|
|
7 |
|
8 |
async def duckduckgo_search(query: str, max_results: int = 5) -> pd.DataFrame:
|
9 |
"""
|
@@ -14,7 +16,9 @@ async def duckduckgo_search(query: str, max_results: int = 5) -> pd.DataFrame:
|
|
14 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
15 |
|
16 |
Returns:
|
17 |
-
|
|
|
|
|
18 |
"""
|
19 |
ddg = DuckDuckGoSearchResults(
|
20 |
output_format="list",
|
@@ -30,20 +34,16 @@ async def duckduckgo_search(query: str, max_results: int = 5) -> pd.DataFrame:
|
|
30 |
return results
|
31 |
|
32 |
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
),
|
47 |
-
title="DuckDuckGo Search",
|
48 |
-
description="Search the web using DuckDuckGo Search Engine.",
|
49 |
-
)
|
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
7 |
+
from .base import BaseInterfaceWrapper
|
8 |
+
|
9 |
|
10 |
async def duckduckgo_search(query: str, max_results: int = 5) -> pd.DataFrame:
|
11 |
"""
|
|
|
16 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
17 |
|
18 |
Returns:
|
19 |
+
dict: The search results from DuckDuckGo Search Engine. Fields:
|
20 |
+
headers (list): The headers of the columns in the search results. Contains "title", "link", "body".
|
21 |
+
data (list): The data in the search results. Each row contains the title, link, and body of the search result.
|
22 |
"""
|
23 |
ddg = DuckDuckGoSearchResults(
|
24 |
output_format="list",
|
|
|
34 |
return results
|
35 |
|
36 |
|
37 |
+
class DuckDuckGoInterfaceWrapper(BaseInterfaceWrapper):
|
38 |
+
def __init__(self):
|
39 |
+
super().__init__(
|
40 |
+
fn=duckduckgo_search,
|
41 |
+
inputs=[
|
42 |
+
gr.Textbox(label="Search Query"),
|
43 |
+
gr.Slider(
|
44 |
+
minimum=1, maximum=10, step=1, value=5, label="Number of Results"
|
45 |
+
),
|
46 |
+
],
|
47 |
+
title="DuckDuckGo Search",
|
48 |
+
description="Search the web using DuckDuckGo Search Engine.",
|
49 |
+
)
|
|
|
|
|
|
|
|
search_engines/searxng.py
CHANGED
@@ -4,6 +4,8 @@ from langchain_community.utilities import SearxSearchWrapper
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
|
|
|
|
7 |
|
8 |
async def searxng_search(
|
9 |
query: str, searxng_host: str, max_results: int = 5
|
@@ -18,7 +20,9 @@ async def searxng_search(
|
|
18 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
19 |
|
20 |
Returns:
|
21 |
-
|
|
|
|
|
22 |
"""
|
23 |
searxng = SearxSearchWrapper(
|
24 |
searx_host=searxng_host,
|
@@ -37,21 +41,16 @@ async def searxng_search(
|
|
37 |
return results
|
38 |
|
39 |
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
wrap=True,
|
54 |
-
),
|
55 |
-
title="SearxNG Search",
|
56 |
-
description="Search the web using SearxNG Search Engine.",
|
57 |
-
)
|
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
7 |
+
from .base import BaseInterfaceWrapper
|
8 |
+
|
9 |
|
10 |
async def searxng_search(
|
11 |
query: str, searxng_host: str, max_results: int = 5
|
|
|
20 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
21 |
|
22 |
Returns:
|
23 |
+
dict: The search results from SearxNG Search Engine. Fields:
|
24 |
+
headers (list): The headers of the columns in the search results. Contains "title", "link", "body".
|
25 |
+
data (list): The data in the search results. Each row contains the title, link, and body of the search result.
|
26 |
"""
|
27 |
searxng = SearxSearchWrapper(
|
28 |
searx_host=searxng_host,
|
|
|
41 |
return results
|
42 |
|
43 |
|
44 |
+
class SearxNGInterfaceWrapper(BaseInterfaceWrapper):
|
45 |
+
def __init__(self):
|
46 |
+
super().__init__(
|
47 |
+
fn=searxng_search,
|
48 |
+
inputs=[
|
49 |
+
gr.Textbox(label="Search Query"),
|
50 |
+
gr.Slider(
|
51 |
+
minimum=1, maximum=10, step=1, value=5, label="Number of Results"
|
52 |
+
),
|
53 |
+
],
|
54 |
+
title="SearxNG Search",
|
55 |
+
description="Search the web using SearxNG Search Engine.",
|
56 |
+
)
|
|
|
|
|
|
|
|
|
|
search_engines/serpapi.py
CHANGED
@@ -4,6 +4,8 @@ from langchain_community.utilities import SerpAPIWrapper
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
|
|
|
|
7 |
|
8 |
async def serpapi_search(
|
9 |
query: str, api_key: str, max_results: int = 5
|
@@ -17,7 +19,9 @@ async def serpapi_search(
|
|
17 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
18 |
|
19 |
Returns:
|
20 |
-
|
|
|
|
|
21 |
"""
|
22 |
serpapi = SerpAPIWrapper(
|
23 |
serpapi_api_key=api_key,
|
@@ -34,21 +38,17 @@ async def serpapi_search(
|
|
34 |
return results
|
35 |
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
),
|
52 |
-
title="SerpAPI Search",
|
53 |
-
description="Search the web using SerpAPI Search Engine.",
|
54 |
-
)
|
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
7 |
+
from .base import BaseInterfaceWrapper
|
8 |
+
|
9 |
|
10 |
async def serpapi_search(
|
11 |
query: str, api_key: str, max_results: int = 5
|
|
|
19 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
20 |
|
21 |
Returns:
|
22 |
+
dict: The search results from SerpAPI Search Engine. Fields:
|
23 |
+
headers (list): The headers of the columns in the search results. Contains "title", "link", "body".
|
24 |
+
data (list): The data in the search results. Each row contains the title, link, and body of the search result.
|
25 |
"""
|
26 |
serpapi = SerpAPIWrapper(
|
27 |
serpapi_api_key=api_key,
|
|
|
38 |
return results
|
39 |
|
40 |
|
41 |
+
class SerpAPIInterfaceWrapper(BaseInterfaceWrapper):
|
42 |
+
def __init__(self):
|
43 |
+
super().__init__(
|
44 |
+
fn=serpapi_search,
|
45 |
+
inputs=[
|
46 |
+
gr.Textbox(label="Search Query"),
|
47 |
+
gr.Textbox(label="API Key", type="password"),
|
48 |
+
gr.Slider(
|
49 |
+
minimum=1, maximum=10, step=1, value=5, label="Number of Results"
|
50 |
+
),
|
51 |
+
],
|
52 |
+
title="SerpAPI Search",
|
53 |
+
description="Search the web using SerpAPI Search Engine.",
|
54 |
+
)
|
|
|
|
|
|
|
|
search_engines/serper.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
from langchain_community.utilities import GoogleSerperAPIWrapper
|
4 |
+
|
5 |
+
from utils.helpers import map_results
|
6 |
+
|
7 |
+
from .base import BaseInterfaceWrapper
|
8 |
+
|
9 |
+
|
10 |
+
async def serper_search(query: str, api_key: str, max_results: int = 5) -> pd.DataFrame:
|
11 |
+
"""
|
12 |
+
Given a search query, returns the search results from Serper.
|
13 |
+
|
14 |
+
Args:
|
15 |
+
query (str): The search query.
|
16 |
+
api_key (str): The API key for Serper Search.
|
17 |
+
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
18 |
+
|
19 |
+
Returns:
|
20 |
+
dict: The search results from Serper Search Engine. Fields:
|
21 |
+
headers (list): The headers of the columns in the search results. Contains "title", "link", "body".
|
22 |
+
data (list): The data in the search results. Each row contains the title, link, and body of the search result.
|
23 |
+
"""
|
24 |
+
serper = GoogleSerperAPIWrapper(
|
25 |
+
k=max_results,
|
26 |
+
serper_api_key=api_key,
|
27 |
+
)
|
28 |
+
results = await serper.aresults(query)
|
29 |
+
results = results["organic"]
|
30 |
+
mapping = {
|
31 |
+
"title": "title",
|
32 |
+
"link": "link",
|
33 |
+
"snippet": "body",
|
34 |
+
}
|
35 |
+
results = await map_results(results, mapping)
|
36 |
+
return results
|
37 |
+
|
38 |
+
|
39 |
+
class SerperInterfaceWrapper(BaseInterfaceWrapper):
|
40 |
+
def __init__(self):
|
41 |
+
super().__init__(
|
42 |
+
fn=serper_search,
|
43 |
+
inputs=[
|
44 |
+
gr.Textbox(label="Search Query"),
|
45 |
+
gr.Textbox(label="API Key", type="password"),
|
46 |
+
gr.Slider(
|
47 |
+
minimum=1, maximum=10, step=1, value=5, label="Number of Results"
|
48 |
+
),
|
49 |
+
],
|
50 |
+
title="Serper Search",
|
51 |
+
description="Search the web using Serper Search Engine.",
|
52 |
+
)
|
search_engines/tavily.py
CHANGED
@@ -4,6 +4,8 @@ from langchain_tavily import TavilySearch
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
|
|
|
|
7 |
|
8 |
async def tavily_search(query: str, api_key: str, max_results: int = 5) -> pd.DataFrame:
|
9 |
"""
|
@@ -15,7 +17,9 @@ async def tavily_search(query: str, api_key: str, max_results: int = 5) -> pd.Da
|
|
15 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
16 |
|
17 |
Returns:
|
18 |
-
|
|
|
|
|
19 |
"""
|
20 |
tavily = TavilySearch(
|
21 |
tavily_api_key=api_key,
|
@@ -31,21 +35,17 @@ async def tavily_search(query: str, api_key: str, max_results: int = 5) -> pd.Da
|
|
31 |
return results
|
32 |
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
),
|
49 |
-
title="Tavily Search",
|
50 |
-
description="Search the web using Tavily Search Engine.",
|
51 |
-
)
|
|
|
4 |
|
5 |
from utils.helpers import map_results
|
6 |
|
7 |
+
from .base import BaseInterfaceWrapper
|
8 |
+
|
9 |
|
10 |
async def tavily_search(query: str, api_key: str, max_results: int = 5) -> pd.DataFrame:
|
11 |
"""
|
|
|
17 |
max_results (int, optional): The number of maximum results to return. Defaults to 5.
|
18 |
|
19 |
Returns:
|
20 |
+
dict: The search results from Tavily Search Engine. Fields:
|
21 |
+
headers (list): The headers of the columns in the search results. Contains "title", "link", "body".
|
22 |
+
data (list): The data in the search results. Each row contains the title, link, and body of the search result.
|
23 |
"""
|
24 |
tavily = TavilySearch(
|
25 |
tavily_api_key=api_key,
|
|
|
35 |
return results
|
36 |
|
37 |
|
38 |
+
class TavilyInterfaceWrapper(BaseInterfaceWrapper):
|
39 |
+
def __init__(self):
|
40 |
+
super().__init__(
|
41 |
+
fn=tavily_search,
|
42 |
+
inputs=[
|
43 |
+
gr.Textbox(label="Search Query"),
|
44 |
+
gr.Textbox(label="API Key", type="password"),
|
45 |
+
gr.Slider(
|
46 |
+
minimum=1, maximum=10, step=1, value=5, label="Number of Results"
|
47 |
+
),
|
48 |
+
],
|
49 |
+
title="Tavily Search",
|
50 |
+
description="Search the web using Tavily Search Engine.",
|
51 |
+
)
|
|
|
|
|
|
|
|
utils/constants.py
ADDED
File without changes
|