ai-parrot 0.8.3__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.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.
Potentially problematic release.
This version of ai-parrot might be problematic. Click here for more details.
- ai_parrot-0.8.3.dist-info/LICENSE +21 -0
- ai_parrot-0.8.3.dist-info/METADATA +306 -0
- ai_parrot-0.8.3.dist-info/RECORD +128 -0
- ai_parrot-0.8.3.dist-info/WHEEL +6 -0
- ai_parrot-0.8.3.dist-info/top_level.txt +2 -0
- parrot/__init__.py +30 -0
- parrot/bots/__init__.py +5 -0
- parrot/bots/abstract.py +1115 -0
- parrot/bots/agent.py +492 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/bose.py +17 -0
- parrot/bots/chatbot.py +271 -0
- parrot/bots/cody.py +17 -0
- parrot/bots/copilot.py +117 -0
- parrot/bots/data.py +730 -0
- parrot/bots/dataframe.py +103 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/interfaces/__init__.py +1 -0
- parrot/bots/interfaces/retrievers.py +12 -0
- parrot/bots/notebook.py +619 -0
- parrot/bots/odoo.py +17 -0
- parrot/bots/prompts/__init__.py +41 -0
- parrot/bots/prompts/agents.py +91 -0
- parrot/bots/prompts/data.py +214 -0
- parrot/bots/retrievals/__init__.py +1 -0
- parrot/bots/retrievals/constitutional.py +19 -0
- parrot/bots/retrievals/multi.py +122 -0
- parrot/bots/retrievals/retrieval.py +610 -0
- parrot/bots/tools/__init__.py +7 -0
- parrot/bots/tools/eda.py +325 -0
- parrot/bots/tools/pdf.py +50 -0
- parrot/bots/tools/plot.py +48 -0
- parrot/bots/troc.py +16 -0
- parrot/conf.py +170 -0
- parrot/crew/__init__.py +3 -0
- parrot/crew/tools/__init__.py +22 -0
- parrot/crew/tools/bing.py +13 -0
- parrot/crew/tools/config.py +43 -0
- parrot/crew/tools/duckgo.py +62 -0
- parrot/crew/tools/file.py +24 -0
- parrot/crew/tools/google.py +168 -0
- parrot/crew/tools/gtrends.py +16 -0
- parrot/crew/tools/md2pdf.py +25 -0
- parrot/crew/tools/rag.py +42 -0
- parrot/crew/tools/search.py +32 -0
- parrot/crew/tools/url.py +21 -0
- parrot/exceptions.cpython-311-x86_64-linux-gnu.so +0 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agents.py +292 -0
- parrot/handlers/bots.py +196 -0
- parrot/handlers/chat.py +192 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/http.py +805 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +18 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/exif.py +709 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/llms/__init__.py +1 -0
- parrot/llms/abstract.py +69 -0
- parrot/llms/anthropic.py +58 -0
- parrot/llms/gemma.py +15 -0
- parrot/llms/google.py +44 -0
- parrot/llms/groq.py +67 -0
- parrot/llms/hf.py +45 -0
- parrot/llms/openai.py +61 -0
- parrot/llms/pipes.py +114 -0
- parrot/llms/vertex.py +89 -0
- parrot/loaders/__init__.py +9 -0
- parrot/loaders/abstract.py +628 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/txt.py +26 -0
- parrot/manager.py +333 -0
- parrot/models.py +504 -0
- parrot/py.typed +0 -0
- parrot/stores/__init__.py +11 -0
- parrot/stores/abstract.py +248 -0
- parrot/stores/chroma.py +188 -0
- parrot/stores/duck.py +162 -0
- parrot/stores/embeddings/__init__.py +10 -0
- parrot/stores/embeddings/abstract.py +46 -0
- parrot/stores/embeddings/base.py +52 -0
- parrot/stores/embeddings/bge.py +20 -0
- parrot/stores/embeddings/fastembed.py +17 -0
- parrot/stores/embeddings/google.py +18 -0
- parrot/stores/embeddings/huggingface.py +20 -0
- parrot/stores/embeddings/ollama.py +14 -0
- parrot/stores/embeddings/openai.py +26 -0
- parrot/stores/embeddings/transformers.py +21 -0
- parrot/stores/embeddings/vertexai.py +17 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss.py +160 -0
- parrot/stores/milvus.py +397 -0
- parrot/stores/postgres.py +653 -0
- parrot/stores/qdrant.py +170 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +68 -0
- parrot/tools/asknews.py +33 -0
- parrot/tools/basic.py +51 -0
- parrot/tools/bby.py +359 -0
- parrot/tools/bing.py +13 -0
- parrot/tools/docx.py +343 -0
- parrot/tools/duck.py +62 -0
- parrot/tools/execute.py +56 -0
- parrot/tools/gamma.py +28 -0
- parrot/tools/google.py +170 -0
- parrot/tools/gvoice.py +301 -0
- parrot/tools/results.py +278 -0
- parrot/tools/stack.py +27 -0
- parrot/tools/weather.py +70 -0
- parrot/tools/wikipedia.py +58 -0
- parrot/tools/zipcode.py +198 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.cpython-311-x86_64-linux-gnu.so +0 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpython-311-x86_64-linux-gnu.so +0 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- resources/users/__init__.py +5 -0
- resources/users/handlers.py +13 -0
- resources/users/models.py +205 -0
parrot/tools/bby.py
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
from typing import List, Dict, Any, Union
|
|
2
|
+
import random
|
|
3
|
+
from orjson import JSONDecodeError
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
# from pydantic.v1 import BaseModel, Field
|
|
6
|
+
from langchain_core.tools import BaseTool, BaseToolkit, StructuredTool, ToolException, Tool
|
|
7
|
+
from datamodel.parsers.json import json_decoder, json_encoder # pylint: disable=E0611
|
|
8
|
+
from datamodel.exceptions import ParserError # pylint: disable=E0611
|
|
9
|
+
from navconfig import config
|
|
10
|
+
from ..interfaces.http import HTTPService, ua
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
ctt_list: list = [
|
|
14
|
+
"f3dbf688e45146555bb2b8604a993601",
|
|
15
|
+
"06f4dfe367e87866397ef32302f5042e",
|
|
16
|
+
"4e07e03ff03f5debc4e09ac4db9239ac"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
sid_list: list = [
|
|
20
|
+
"d4fa1142-2998-4b68-af78-46d821bb3e1f",
|
|
21
|
+
"9627390e-b423-459f-83ee-7964dd05c9a8"
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
class BestBuyProductAvailabilityInput(BaseModel):
|
|
25
|
+
"""Input for the BestBuy product availability tool."""
|
|
26
|
+
zipcode: str = Field(..., description="The ZIP code to check availability in")
|
|
27
|
+
sku: str = Field(..., description="The SKU of the product to check")
|
|
28
|
+
location_id: str = Field(
|
|
29
|
+
..., description="Optional specific location ID to check"
|
|
30
|
+
)
|
|
31
|
+
show_only_in_stock: bool = Field(
|
|
32
|
+
False, description="Whether to only show stores with product in stock"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
model_config = {
|
|
36
|
+
"arbitrary_types_allowed": True,
|
|
37
|
+
"extra": "forbid", # Helps with compatibility
|
|
38
|
+
"json_schema_extra": {
|
|
39
|
+
"required": ["zipcode", "sku", "location_id"]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class BestBuyToolkit(BaseToolkit):
|
|
44
|
+
"""Toolkit for interacting with BestBuy's API."""
|
|
45
|
+
|
|
46
|
+
api_key: str = Field(default=config.get('BESTBUY_APIKEY'))
|
|
47
|
+
|
|
48
|
+
http_service: HTTPService = Field(default_factory=lambda: HTTPService(
|
|
49
|
+
use_proxy=True,
|
|
50
|
+
cookies={
|
|
51
|
+
"CTT": random.choice(ctt_list),
|
|
52
|
+
"SID": random.choice(sid_list),
|
|
53
|
+
"bby_rdp": "l",
|
|
54
|
+
"bm_sz": "9F5ED0110AF18594E2347A89BB4AB998~YAAQxm1lX6EqYHGSAQAAw+apmhkhXIeGYEc4KnzUMsjeac3xEoQmTNz5+of62i3RXQL6fUI+0FvCb/jgSjiVQOcfaSF+LdLkOXP1F4urgeIcqp/dBAhu5MvZXaCQsT06bwr7j21ozhFfTTWhjz1HmZN8wecsE6WGbK6wXp/33ODKlLaGWkTutqHbkzvMiiHXBCs9hT8jVny0REfita4AfqTK85Y6/M6Uq4IaDLPBLnTtJ0cTlPHk1HmkG5EsnI46llghcx1KZnCGnvZfHdb2ME9YZJ2GmC2b7dNmAgyL/gSVpoNdCJOj5Jk6z/MCVhZ81OZfX4S01E2F1mBGq4uV5/1oK2KR4YgZP4dsTN8izEEPybUKGY3CyM1gOUc=~3556420~4277810",
|
|
55
|
+
"bby_cbc_lb": "p-browse-e",
|
|
56
|
+
"intl_splash": "false"
|
|
57
|
+
},
|
|
58
|
+
headers={
|
|
59
|
+
"Host": "www.bestbuy.com",
|
|
60
|
+
"Referer": "https://www.bestbuy.com/",
|
|
61
|
+
"TE": "trailers",
|
|
62
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
63
|
+
}
|
|
64
|
+
))
|
|
65
|
+
|
|
66
|
+
# Add this model_config to allow arbitrary types
|
|
67
|
+
model_config = {
|
|
68
|
+
"arbitrary_types_allowed": True
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def get_tools(self) -> List[BaseTool]:
|
|
72
|
+
"""Get the tools in the toolkit."""
|
|
73
|
+
return [
|
|
74
|
+
self._get_availability_tool(),
|
|
75
|
+
self._get_product_information()
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
def _get_product_information(self) -> StructuredTool:
|
|
79
|
+
"""Create a tool for getting product information from BestBuy."""
|
|
80
|
+
|
|
81
|
+
async def _product_information(tool_input: Union[str, dict]) -> List[dict]:
|
|
82
|
+
"""Get product information from BestBuy based on SKU, name, or search terms."""
|
|
83
|
+
# https://api.bestbuy.com/v1/products(name={product_name})?format=json&show=sku,name,salePrice,customerReviewAverage,customerReviewCount,manufacturer,modelNumber&apiKey={api_key}
|
|
84
|
+
if isinstance(tool_input, dict):
|
|
85
|
+
# Direct call with dict
|
|
86
|
+
input_data = tool_input
|
|
87
|
+
elif isinstance(tool_input, str):
|
|
88
|
+
# Might be a JSON string from LLM
|
|
89
|
+
try:
|
|
90
|
+
# input_data = json.loads(tool_input)
|
|
91
|
+
input_data = json_decoder(tool_input)
|
|
92
|
+
except (ParserError, JSONDecodeError):
|
|
93
|
+
# Not valid JSON, treat as zipcode with missing other params
|
|
94
|
+
input_data = {"product_name": tool_input}
|
|
95
|
+
else:
|
|
96
|
+
# Some other type that we don't expect
|
|
97
|
+
input_data = {"product_name": str(tool_input)}
|
|
98
|
+
|
|
99
|
+
# Product Name to be used in the search:
|
|
100
|
+
print('PRODUCT > ', input_data)
|
|
101
|
+
|
|
102
|
+
product_name = input_data.get("product_name", None)
|
|
103
|
+
search_terms = input_data.get("search_terms", None)
|
|
104
|
+
print(search_terms)
|
|
105
|
+
if search_terms:
|
|
106
|
+
search_terms = search_terms.split(',')
|
|
107
|
+
# I need in format: search=oven&search=stainless&search=steel
|
|
108
|
+
search_terms = '&'.join([f"search={term.strip()}" for term in search_terms])
|
|
109
|
+
url = f"https://api.bestbuy.com/v1/products({search_terms})?format=json&show=sku,name,salePrice,customerReviewAverage,customerReviewCount,manufacturer,modelNumber&apiKey={self.api_key}"
|
|
110
|
+
elif product_name:
|
|
111
|
+
if ',' in product_name:
|
|
112
|
+
search_terms = product_name.split(',')
|
|
113
|
+
search_terms = '&'.join([f"search={term.strip()}" for term in search_terms])
|
|
114
|
+
else:
|
|
115
|
+
search_terms = f"name={product_name.strip()}"
|
|
116
|
+
url = f"https://api.bestbuy.com/v1/products({search_terms})?format=json&show=sku,name,salePrice,customerReviewAverage,customerReviewCount,manufacturer,modelNumber&apiKey={self.api_key}"
|
|
117
|
+
else:
|
|
118
|
+
raise ToolException(
|
|
119
|
+
"Either product_name or search_terms must be provided."
|
|
120
|
+
)
|
|
121
|
+
# URL for BestBuy's Product API
|
|
122
|
+
result, error = await self.http_service._request(
|
|
123
|
+
url=url,
|
|
124
|
+
method="GET",
|
|
125
|
+
use_json=True,
|
|
126
|
+
use_proxy=True,
|
|
127
|
+
headers={
|
|
128
|
+
"User-Agent": random.choice(ua)
|
|
129
|
+
},
|
|
130
|
+
use_ssl=True,
|
|
131
|
+
follow_redirects=True,
|
|
132
|
+
raise_for_status=True,
|
|
133
|
+
full_response=False
|
|
134
|
+
)
|
|
135
|
+
products = result.get('products', [])
|
|
136
|
+
if not products:
|
|
137
|
+
return "No products found."
|
|
138
|
+
|
|
139
|
+
return products
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
return StructuredTool.from_function(
|
|
143
|
+
name="product_information",
|
|
144
|
+
description=(
|
|
145
|
+
"Use this tool to search for product information. "
|
|
146
|
+
"Input must be the search terms: "
|
|
147
|
+
"- search terms: different words separated by commas "
|
|
148
|
+
" Example input: {{\"search_terms\": \"oven,stainless\"}}"
|
|
149
|
+
),
|
|
150
|
+
func=_product_information,
|
|
151
|
+
coroutine=_product_information,
|
|
152
|
+
infer_schema=False,
|
|
153
|
+
return_direct=False,
|
|
154
|
+
handle_tool_error=True
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def _get_availability_tool(self) -> StructuredTool:
|
|
158
|
+
"""Create a tool for checking product availability at BestBuy."""
|
|
159
|
+
|
|
160
|
+
async def _check_availability(tool_input: Union[str, dict]) -> str:
|
|
161
|
+
"""Check BestBuy product availability based on SKU and location."""
|
|
162
|
+
|
|
163
|
+
# Input validation
|
|
164
|
+
# tool_input: str
|
|
165
|
+
try:
|
|
166
|
+
if isinstance(tool_input, dict):
|
|
167
|
+
# Direct call with dict
|
|
168
|
+
input_data = tool_input
|
|
169
|
+
elif isinstance(tool_input, str):
|
|
170
|
+
# Might be a JSON string from LLM
|
|
171
|
+
try:
|
|
172
|
+
# input_data = json.loads(tool_input)
|
|
173
|
+
input_data = json_decoder(tool_input)
|
|
174
|
+
except (ParserError, JSONDecodeError):
|
|
175
|
+
# Not valid JSON, treat as zipcode with missing other params
|
|
176
|
+
input_data = {"zipcode": tool_input}
|
|
177
|
+
else:
|
|
178
|
+
# Some other type that we don't expect
|
|
179
|
+
input_data = {"zipcode": str(tool_input)}
|
|
180
|
+
# Extract fields
|
|
181
|
+
zipcode = input_data.get("zipcode")
|
|
182
|
+
sku = input_data.get("sku")
|
|
183
|
+
location_id = input_data.get("location_id")
|
|
184
|
+
show_only_in_stock = input_data.get("show_only_in_stock", False)
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
# Final fallback - treat the entire input as a zipcode
|
|
188
|
+
zipcode = str(tool_input)
|
|
189
|
+
sku = None
|
|
190
|
+
location_id = None
|
|
191
|
+
show_only_in_stock = False
|
|
192
|
+
|
|
193
|
+
# Input validation
|
|
194
|
+
if not zipcode:
|
|
195
|
+
raise ToolException("ZIP code is required to check product availability")
|
|
196
|
+
if not sku:
|
|
197
|
+
raise ToolException("Product SKU is required to check availability")
|
|
198
|
+
if not location_id:
|
|
199
|
+
raise ToolException("Store location ID is required to check availability")
|
|
200
|
+
|
|
201
|
+
# Prepare the payload for the API call
|
|
202
|
+
payload = {
|
|
203
|
+
"locationId": location_id,
|
|
204
|
+
"zipCode": zipcode,
|
|
205
|
+
"showOnShelf": True,
|
|
206
|
+
"lookupInStoreQuantity": True,
|
|
207
|
+
"xboxAllAccess": False,
|
|
208
|
+
"consolidated": True,
|
|
209
|
+
"showOnlyOnShelf": False,
|
|
210
|
+
"showInStore": True,
|
|
211
|
+
"pickupTypes": [
|
|
212
|
+
"UPS_ACCESS_POINT",
|
|
213
|
+
"FEDEX_HAL"
|
|
214
|
+
],
|
|
215
|
+
"onlyBestBuyLocations": True,
|
|
216
|
+
# TODO: add more products
|
|
217
|
+
"items": [
|
|
218
|
+
{
|
|
219
|
+
"sku": sku,
|
|
220
|
+
"condition": None,
|
|
221
|
+
"quantity": 1,
|
|
222
|
+
"itemSeqNumber": "1",
|
|
223
|
+
"reservationToken": None,
|
|
224
|
+
"selectedServices": [],
|
|
225
|
+
"requiredAccessories": [],
|
|
226
|
+
"isTradeIn": False,
|
|
227
|
+
"isLeased": False
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# Make the API call using the HTTP service
|
|
233
|
+
try:
|
|
234
|
+
# URL for BestBuy's availability API
|
|
235
|
+
url = "https://www.bestbuy.com/productfulfillment/c/api/2.0/storeAvailability"
|
|
236
|
+
|
|
237
|
+
# Make POST request with JSON payload
|
|
238
|
+
result, error = await self.http_service._request(
|
|
239
|
+
url=url,
|
|
240
|
+
method="POST",
|
|
241
|
+
data=payload,
|
|
242
|
+
use_json=True,
|
|
243
|
+
use_proxy=True,
|
|
244
|
+
headers={
|
|
245
|
+
"User-Agent": random.choice(ua)
|
|
246
|
+
},
|
|
247
|
+
use_ssl=True,
|
|
248
|
+
follow_redirects=True,
|
|
249
|
+
raise_for_status=True,
|
|
250
|
+
full_response=False
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if error:
|
|
254
|
+
raise ToolException(
|
|
255
|
+
f"Error checking BestBuy availability: {error}"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Process and format the response
|
|
259
|
+
if not result:
|
|
260
|
+
return "No data was returned from BestBuy. The service may be unavailable."
|
|
261
|
+
|
|
262
|
+
# Extract relevant information from the result
|
|
263
|
+
formatted_result = self._format_availability_response(result, location_id, sku, show_only_in_stock)
|
|
264
|
+
return formatted_result
|
|
265
|
+
|
|
266
|
+
except Exception as e:
|
|
267
|
+
raise ToolException(f"Failed to check BestBuy product availability: {str(e)}")
|
|
268
|
+
|
|
269
|
+
return StructuredTool.from_function(
|
|
270
|
+
name="availability",
|
|
271
|
+
description=(
|
|
272
|
+
"Use this tool to check product availability at a specific Best Buy store or zipcode. "
|
|
273
|
+
"Input must be a JSON object with the following fields: "
|
|
274
|
+
"- zipcode (required): The ZIP code to check availability in. "
|
|
275
|
+
"- sku (required): The SKU of the product to check. "
|
|
276
|
+
"- location_id (required): The specific store location ID to check. "
|
|
277
|
+
"- show_only_in_stock (optional): Whether to only show stores with product in stock. "
|
|
278
|
+
"Example input: {{\"zipcode\": \"33928\", \"sku\": \"6428376\", \"location_id\": \"767\", \"show_only_in_stock\": false}}"
|
|
279
|
+
),
|
|
280
|
+
func=_check_availability,
|
|
281
|
+
# args_schema=BestBuyProductAvailabilityInput,
|
|
282
|
+
coroutine=_check_availability,
|
|
283
|
+
infer_schema=False,
|
|
284
|
+
return_direct=False,
|
|
285
|
+
handle_tool_error=True
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _format_availability_response(
|
|
289
|
+
self, result: Dict[str, Any], location_id: str, sku: str, show_only_in_stock: bool = False
|
|
290
|
+
) -> str:
|
|
291
|
+
try:
|
|
292
|
+
# Extract store information from the "ispu" locations
|
|
293
|
+
locations = result.get("ispu", {}).get("locations", [])
|
|
294
|
+
# Match on the "id" key rather than "locationId"
|
|
295
|
+
store = next((loc for loc in locations if loc.get("id") == location_id), None)
|
|
296
|
+
if not store:
|
|
297
|
+
return "No matching store location found."
|
|
298
|
+
|
|
299
|
+
store_name = store.get("name", "N/A")
|
|
300
|
+
store_address = store.get("address", "N/A")
|
|
301
|
+
store_city = store.get("city", "N/A")
|
|
302
|
+
store_zip = store.get("zipCode", "N/A")
|
|
303
|
+
store_state = store.get("state", "N/A")
|
|
304
|
+
store_lat = store.get("latitude", "N/A")
|
|
305
|
+
store_long = store.get("longitude", "N/A")
|
|
306
|
+
|
|
307
|
+
# Format store hours if available
|
|
308
|
+
open_times = store.get("openTimesMap", {})
|
|
309
|
+
store_hours_str = (
|
|
310
|
+
"\n".join(f"{day}: {hours}" for day, hours in open_times.items())
|
|
311
|
+
if open_times else "N/A"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Extract product availability from "ispu" items
|
|
315
|
+
items = result.get("ispu", {}).get("items", [])
|
|
316
|
+
item = next((it for it in items if it.get("sku") == sku), None)
|
|
317
|
+
if not item:
|
|
318
|
+
return "No matching product found."
|
|
319
|
+
|
|
320
|
+
print('ITEM > ', item)
|
|
321
|
+
|
|
322
|
+
product_instore = item.get("inStoreAvailable", False)
|
|
323
|
+
product_pickup = item.get("pickupEligible", False)
|
|
324
|
+
|
|
325
|
+
# Extract the location-specific availability from the item's "locations" list
|
|
326
|
+
item_locations = item.get("locations", [])
|
|
327
|
+
availability = next(
|
|
328
|
+
(loc for loc in item_locations if loc.get("locationId") == location_id), None
|
|
329
|
+
)
|
|
330
|
+
if not availability:
|
|
331
|
+
return "No matching product availability found."
|
|
332
|
+
|
|
333
|
+
on_shelf = availability.get("onShelfDisplay", False)
|
|
334
|
+
in_store_quantity = availability.get("inStoreAvailability", {}).get("availableInStoreQuantity", 0)
|
|
335
|
+
available_from = availability.get("inStoreAvailability", {}).get("minDate")
|
|
336
|
+
|
|
337
|
+
# Build and return a formatted result string
|
|
338
|
+
result_str = (
|
|
339
|
+
f"Store Name: {store_name}\n"
|
|
340
|
+
f"Address: {store_address}\n"
|
|
341
|
+
f"City: {store_city}\n"
|
|
342
|
+
f"Zip Code: {store_zip}\n"
|
|
343
|
+
f"State: {store_state}\n"
|
|
344
|
+
f"Latitude: {store_lat}\n"
|
|
345
|
+
f"Longitude: {store_long}\n"
|
|
346
|
+
f"Hours:\n{store_hours_str}\n"
|
|
347
|
+
"--------------------------------\n"
|
|
348
|
+
f"Product SKU: {sku}\n"
|
|
349
|
+
f"In-store Available: {product_instore}\n"
|
|
350
|
+
f"Pickup Eligible: {product_pickup}\n"
|
|
351
|
+
f"On Shelf Display: {on_shelf}\n"
|
|
352
|
+
f"Available Quantity: {in_store_quantity}\n"
|
|
353
|
+
f"Available From: {available_from}\n"
|
|
354
|
+
"--------------------------------\n"
|
|
355
|
+
)
|
|
356
|
+
return result_str
|
|
357
|
+
|
|
358
|
+
except Exception as e:
|
|
359
|
+
return f"Error formatting availability response: {str(e)}"
|
parrot/tools/bing.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from langchain_community.utilities.bing_search import BingSearchAPIWrapper
|
|
2
|
+
from langchain.tools import BaseTool
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BingSearchTool(BaseTool):
|
|
6
|
+
"""Microsoft Bing Search Tool."""
|
|
7
|
+
name: str = "Bing Search"
|
|
8
|
+
description: str = "Search the web using Microsoft Bing Search, conduct searches using Bing for alternative perspectives and sources."
|
|
9
|
+
|
|
10
|
+
def _run(self, query: str) -> dict:
|
|
11
|
+
"""Run the Bing Search Tool."""
|
|
12
|
+
bing = BingSearchAPIWrapper(k=5)
|
|
13
|
+
return bing.results(query=query, num_results=5)
|