rusticai-serpapi 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
File without changes
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
import shortuuid
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
import serpapi
|
|
10
|
+
from rustic_ai.core.agents.commons.media import MediaLink
|
|
11
|
+
from rustic_ai.core.guild import Agent, AgentMode, AgentSpec, AgentType, agent
|
|
12
|
+
from rustic_ai.core.messaging.core import JsonDict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SearchEngines(Enum):
|
|
16
|
+
google = {"key": "q"}
|
|
17
|
+
bing = {"key": "q"}
|
|
18
|
+
baidu = {"key": "q"}
|
|
19
|
+
yahoo = {"key": "p"}
|
|
20
|
+
duckduckgo = {"key": "q"}
|
|
21
|
+
ebay = {"key": "_nkw"}
|
|
22
|
+
yandex = {"key": "text"}
|
|
23
|
+
home_depot = {"key": "keyword"}
|
|
24
|
+
google_scholar = {"key": "q"}
|
|
25
|
+
youtube = {"key": "search_query"}
|
|
26
|
+
walmart = {"key": "query"}
|
|
27
|
+
google_maps = {"key": "query"}
|
|
28
|
+
google_patents = {"key": "q"}
|
|
29
|
+
|
|
30
|
+
def get_query(self, query):
|
|
31
|
+
return {self.value["key"]: query}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SERPQuery(BaseModel):
|
|
35
|
+
"""
|
|
36
|
+
A class representing a search query.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
engine: str
|
|
40
|
+
query: str
|
|
41
|
+
id: str = Field(default="")
|
|
42
|
+
num: Optional[int] = Field(default=12)
|
|
43
|
+
start: Optional[int] = Field(default=0)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SERPResults(BaseModel):
|
|
47
|
+
"""
|
|
48
|
+
A class representing a search result.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
engine: str
|
|
52
|
+
query: str
|
|
53
|
+
count: int
|
|
54
|
+
id: str = Field(default="")
|
|
55
|
+
results: List[MediaLink]
|
|
56
|
+
total_results: Optional[int] = Field(default=None)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SearchError(BaseModel):
|
|
60
|
+
|
|
61
|
+
id: str = Field(default="")
|
|
62
|
+
response: JsonDict
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SERPAgent(Agent):
|
|
66
|
+
def __init__(self, agent_spec: AgentSpec):
|
|
67
|
+
super().__init__(agent_spec=agent_spec, agent_type=AgentType.BOT, agent_mode=AgentMode.LOCAL)
|
|
68
|
+
|
|
69
|
+
self.serp_api_key = os.getenv("SERP_API_KEY", "")
|
|
70
|
+
assert self.serp_api_key, "SERP_API_KEY environment variable not set"
|
|
71
|
+
self.client = serpapi.Client(api_key=self.serp_api_key)
|
|
72
|
+
|
|
73
|
+
@agent.processor(SERPQuery)
|
|
74
|
+
def search(self, ctx: agent.ProcessContext[SERPQuery]) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Handles the received message.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
message (Message): The received message.
|
|
80
|
+
"""
|
|
81
|
+
search_query = ctx.payload
|
|
82
|
+
|
|
83
|
+
# Use the message.payload as search parameters
|
|
84
|
+
search_params = SearchEngines[search_query.engine].get_query(search_query.query)
|
|
85
|
+
|
|
86
|
+
# Execute asynchronous search and add it to the queue
|
|
87
|
+
results = self.client.search(
|
|
88
|
+
params=search_params,
|
|
89
|
+
engine=search_query.engine,
|
|
90
|
+
num=search_query.num,
|
|
91
|
+
start=search_query.start,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
metadata = results["search_metadata"]
|
|
95
|
+
|
|
96
|
+
if metadata["status"] == "Success":
|
|
97
|
+
# Get the search results
|
|
98
|
+
search_results = results["organic_results"]
|
|
99
|
+
|
|
100
|
+
result_links = []
|
|
101
|
+
|
|
102
|
+
# Publish the message
|
|
103
|
+
for result in search_results:
|
|
104
|
+
result.update({"search_parameters": search_params})
|
|
105
|
+
|
|
106
|
+
filepath = urlparse(result["link"]).path
|
|
107
|
+
filename = os.path.basename(filepath)
|
|
108
|
+
|
|
109
|
+
if not filename:
|
|
110
|
+
filename = f"{shortuuid.uuid()}.html"
|
|
111
|
+
|
|
112
|
+
metadata = {
|
|
113
|
+
"title": result["title"],
|
|
114
|
+
"favicon": result.get("favicon", ""),
|
|
115
|
+
"search_position": result["position"],
|
|
116
|
+
"snippet": result["snippet"],
|
|
117
|
+
"date": result.get("date", ""),
|
|
118
|
+
"query_id": search_query.id,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
link = MediaLink(
|
|
122
|
+
url=result["link"],
|
|
123
|
+
name=filename,
|
|
124
|
+
metadata=metadata,
|
|
125
|
+
mimetype="text/html",
|
|
126
|
+
encoding="utf-8",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
result_links.append(link)
|
|
130
|
+
|
|
131
|
+
total_results = None
|
|
132
|
+
if "search_information" in results and "total_results" in results["search_information"]:
|
|
133
|
+
total_results = results["search_information"]["total_results"]
|
|
134
|
+
|
|
135
|
+
ctx.send(
|
|
136
|
+
SERPResults(
|
|
137
|
+
count=len(result_links),
|
|
138
|
+
results=result_links,
|
|
139
|
+
total_results=total_results,
|
|
140
|
+
id=search_query.id,
|
|
141
|
+
query=result["search_parameters"]["q"],
|
|
142
|
+
engine=result["search_parameters"]["engine"],
|
|
143
|
+
),
|
|
144
|
+
new_thread=True,
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
# Publish the error message
|
|
148
|
+
ctx.send(SearchError(response=results["search_metadata"], id=search_query.id))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: rusticai-serpapi
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Rustic AI module supporting Search through SerpApi
|
|
5
|
+
Home-page: https://www.rustic.ai/
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Author: Dragonscale Industries Inc.
|
|
8
|
+
Author-email: dev@dragonscale.ai
|
|
9
|
+
Requires-Python: >=3.12,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: rusticai-core (>=0.0.5,<0.1.0)
|
|
16
|
+
Requires-Dist: serpapi (>=0.1.5,<0.2.0)
|
|
17
|
+
Project-URL: Repository, https://github.com/rustic-ai/python-framework
|
|
18
|
+
Project-URL: Rustic AI Core, https://pypi.org/project/rusticai-core/
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Rustic AI SerpApi
|
|
22
|
+
|
|
23
|
+
This module provides an agent that can be used to Search the web using [SerpApi](https://serpapi.com/)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Installing
|
|
27
|
+
|
|
28
|
+
```shell
|
|
29
|
+
pip install rusticai-serpapi
|
|
30
|
+
```
|
|
31
|
+
**Note:** It depends on [rusticai-core](https://pypi.org/project/rusticai-core/)
|
|
32
|
+
|
|
33
|
+
## Building from Source
|
|
34
|
+
|
|
35
|
+
```shell
|
|
36
|
+
poetry install --with dev
|
|
37
|
+
poetry build
|
|
38
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
rustic_ai/serpapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
rustic_ai/serpapi/agent.py,sha256=YGlh49h5g3XC4nYZ6G4w-p5yqvAiBWHl6H3h9NwXNZk,4477
|
|
3
|
+
rusticai_serpapi-0.0.1.dist-info/METADATA,sha256=_J8diRF6hZMn9EuTYNrHB8VSoQ2DHmvpqLAKZ97ypxc,1115
|
|
4
|
+
rusticai_serpapi-0.0.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
5
|
+
rusticai_serpapi-0.0.1.dist-info/RECORD,,
|