Shrijeeth-Suresh commited on
Commit
9d2647f
·
1 Parent(s): 5069259

refactor: implement base interface wrappers and add Serper search engine

Browse files
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
- import gradio as gr
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
- app.launch(
26
- mcp_server=True,
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
- str: The search results from DuckDuckGo Search Engine.
 
 
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
- duckduckgo_interface = gr.Interface(
34
- fn=duckduckgo_search,
35
- inputs=[
36
- gr.Textbox(label="Search Query"),
37
- gr.Slider(minimum=1, maximum=10, step=1, value=5, label="Number of Results"),
38
- ],
39
- outputs=gr.Dataframe(
40
- label="Search Results",
41
- headers=["title", "body", "link"],
42
- show_fullscreen_button=True,
43
- show_row_numbers=True,
44
- show_copy_button=True,
45
- wrap=True,
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
- str: The search results from SearxNG Search Engine.
 
 
22
  """
23
  searxng = SearxSearchWrapper(
24
  searx_host=searxng_host,
@@ -37,21 +41,16 @@ async def searxng_search(
37
  return results
38
 
39
 
40
- searxng_interface = gr.Interface(
41
- fn=searxng_search,
42
- inputs=[
43
- gr.Textbox(label="Search Query"),
44
- gr.Textbox(label="SearxNG Host URL"),
45
- gr.Slider(minimum=1, maximum=10, step=1, value=5, label="Number of Results"),
46
- ],
47
- outputs=gr.Dataframe(
48
- label="Search Results",
49
- headers=["title", "body", "link"],
50
- show_fullscreen_button=True,
51
- show_row_numbers=True,
52
- show_copy_button=True,
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
- str: The search results from SerpAPI Search Engine.
 
 
21
  """
22
  serpapi = SerpAPIWrapper(
23
  serpapi_api_key=api_key,
@@ -34,21 +38,17 @@ async def serpapi_search(
34
  return results
35
 
36
 
37
- serpapi_interface = gr.Interface(
38
- fn=serpapi_search,
39
- inputs=[
40
- gr.Textbox(label="Search Query"),
41
- gr.Textbox(label="API Key", type="password"),
42
- gr.Slider(minimum=1, maximum=10, step=1, value=5, label="Number of Results"),
43
- ],
44
- outputs=gr.Dataframe(
45
- label="Search Results",
46
- headers=["title", "body", "link"],
47
- show_fullscreen_button=True,
48
- show_row_numbers=True,
49
- show_copy_button=True,
50
- wrap=True,
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
- str: The search results from Tavily Search Engine.
 
 
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
- tavily_interface = gr.Interface(
35
- fn=tavily_search,
36
- inputs=[
37
- gr.Textbox(label="Search Query"),
38
- gr.Textbox(label="API Key", type="password"),
39
- gr.Slider(minimum=1, maximum=10, step=1, value=5, label="Number of Results"),
40
- ],
41
- outputs=gr.Dataframe(
42
- label="Search Results",
43
- headers=["title", "body", "link"],
44
- show_fullscreen_button=True,
45
- show_row_numbers=True,
46
- show_copy_button=True,
47
- wrap=True,
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