python_uml_class 0.2.0 → 0.2.1
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.
- checksums.yaml +4 -4
- data/.rspec_status +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +56 -41
- data/README.md +59 -2
- data/README_JA.md +99 -0
- data/img/class.png +0 -0
- data/lib/config/setting.json +23 -2
- data/lib/config.ru +0 -2
- data/lib/create_uml_class.rb +161 -34
- data/lib/css/index.css +6 -0
- data/lib/js/main.js +24 -10
- data/lib/python_uml_class/version.rb +1 -1
- data/lib/server.rb +12 -0
- data/lib/start.rb +28 -7
- data/lib/wsserver.rb +8 -1
- data/python_uml_class.gemspec +3 -0
- data/test_run.log +3617 -0
- data/test_run.rb +7 -0
- data/test_run_new.log +3083 -0
- data/test_script.py +1674 -0
- data/user_code.py +52 -0
- metadata +51 -2
data/test_run_new.log
ADDED
|
@@ -0,0 +1,3083 @@
|
|
|
1
|
+
in_dir = .
|
|
2
|
+
./lib/del_comment.py
|
|
3
|
+
|
|
4
|
+
|python3 lib/del_comment.py ./lib/del_comment.py > /tmp/pylint20260323-827-ocbr0w
|
|
5
|
+
block_count=0 cstruct_size=0 is_def=false import sys
|
|
6
|
+
block_count=0 cstruct_size=0 is_def=false import ast
|
|
7
|
+
block_count=0 cstruct_size=0 is_def=false import astor
|
|
8
|
+
block_count=0 cstruct_size=0 is_def=false import pprint
|
|
9
|
+
block_count=0 cstruct_size=0 is_def=false def remove_comments_from_code(code):
|
|
10
|
+
block_count=1 cstruct_size=0 is_def=true tree = ast.parse(code)
|
|
11
|
+
compo c_name=ast
|
|
12
|
+
block_count=1 cstruct_size=0 is_def=true for node in ast.walk(tree):
|
|
13
|
+
compo c_name=ast
|
|
14
|
+
block_count=2 cstruct_size=0 is_def=true if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str
|
|
15
|
+
block_count=3 cstruct_size=0 is_def=true ) and isinstance(node.value.s, str):
|
|
16
|
+
block_count=3 cstruct_size=0 is_def=true if node.value.s.startswith(('#', '"""', "'''")):
|
|
17
|
+
compo c_name=node
|
|
18
|
+
endf of ./lib/del_comment.py
|
|
19
|
+
./test/agent.py
|
|
20
|
+
|
|
21
|
+
|python3 lib/del_comment.py ./test/agent.py > /tmp/pylint20260323-827-hcxiok
|
|
22
|
+
block_count=0 cstruct_size=0 is_def=false import os
|
|
23
|
+
block_count=0 cstruct_size=0 is_def=false import sqlite3
|
|
24
|
+
block_count=0 cstruct_size=0 is_def=false import re
|
|
25
|
+
block_count=0 cstruct_size=0 is_def=false import json
|
|
26
|
+
block_count=0 cstruct_size=0 is_def=false import time
|
|
27
|
+
block_count=0 cstruct_size=0 is_def=false import shlex
|
|
28
|
+
block_count=0 cstruct_size=0 is_def=false import smtplib
|
|
29
|
+
block_count=0 cstruct_size=0 is_def=false from email.mime.text import MIMEText
|
|
30
|
+
compo c_name=email
|
|
31
|
+
block_count=0 cstruct_size=0 is_def=false from email.mime.multipart import MIMEMultipart
|
|
32
|
+
compo c_name=email
|
|
33
|
+
block_count=0 cstruct_size=0 is_def=false from typing import Optional, Type, Any, List, Dict
|
|
34
|
+
block_count=0 cstruct_size=0 is_def=false import urllib.request
|
|
35
|
+
compo c_name=urllib
|
|
36
|
+
block_count=0 cstruct_size=0 is_def=false import urllib.error
|
|
37
|
+
compo c_name=urllib
|
|
38
|
+
block_count=0 cstruct_size=0 is_def=false from dotenv import load_dotenv
|
|
39
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.tools import BaseTool, Tool
|
|
40
|
+
block_count=0 cstruct_size=0 is_def=false from pydantic import BaseModel, Field, model_validator
|
|
41
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
|
|
42
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_community.utilities import SerpAPIWrapper
|
|
43
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_community.tools.tavily_search import TavilySearchResults
|
|
44
|
+
block_count=0 cstruct_size=0 is_def=false from googleapiclient.discovery import build
|
|
45
|
+
compo c_name=googleapiclient
|
|
46
|
+
block_count=0 cstruct_size=0 is_def=false import asyncio
|
|
47
|
+
block_count=0 cstruct_size=0 is_def=false from browser_use import Agent, BrowserProfile
|
|
48
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
|
|
49
|
+
block_count=0 cstruct_size=0 is_def=false from browser_use.llm.google.chat import ChatGoogle
|
|
50
|
+
block_count=0 cstruct_size=0 is_def=false from langchain.agents import AgentExecutor, create_tool_calling_agent, create_react_agent
|
|
51
|
+
compo c_name=langchain
|
|
52
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, MessagesPlaceholder
|
|
53
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.output_parsers import JsonOutputParser
|
|
54
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.messages import HumanMessage, AIMessage
|
|
55
|
+
block_count=0 cstruct_size=0 is_def=false dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
|
|
56
|
+
compo c_name=os
|
|
57
|
+
block_count=0 cstruct_size=0 is_def=false load_dotenv(dotenv_path, override=True)
|
|
58
|
+
block_count=0 cstruct_size=0 is_def=false DB_NAME = 'products.db'
|
|
59
|
+
block_count=0 cstruct_size=0 is_def=false def init_db():
|
|
60
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
61
|
+
compo c_name=sqlite3
|
|
62
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
63
|
+
compo c_name=conn
|
|
64
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
65
|
+
compo c_name=cursor
|
|
66
|
+
block_count=2 cstruct_size=0 is_def=true """
|
|
67
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
68
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
69
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN model_number TEXT')
|
|
70
|
+
compo c_name=cursor
|
|
71
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
72
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
73
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
74
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN release_date TEXT')
|
|
75
|
+
compo c_name=cursor
|
|
76
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
77
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
78
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
79
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ram TEXT')
|
|
80
|
+
compo c_name=cursor
|
|
81
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
82
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
83
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
84
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ssd TEXT')
|
|
85
|
+
compo c_name=cursor
|
|
86
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
87
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
88
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
89
|
+
compo c_name=cursor
|
|
90
|
+
block_count=2 cstruct_size=0 is_def=true """
|
|
91
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
92
|
+
block_count=1 cstruct_size=0 is_def=true conn.commit()
|
|
93
|
+
compo c_name=conn
|
|
94
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
95
|
+
compo c_name=conn
|
|
96
|
+
block_count=0 cstruct_size=0 is_def=true init_db()
|
|
97
|
+
block_count=0 cstruct_size=0 is_def=false class TavilySearchWrapper:
|
|
98
|
+
class_name=agent.TavilySearchWrapper
|
|
99
|
+
base_name=
|
|
100
|
+
block_count=1 cstruct_size=1 is_def=false def __init__(self):
|
|
101
|
+
block_count=2 cstruct_size=1 is_def=true self.api_key = os.getenv('TAVILY_API_KEY')
|
|
102
|
+
compo c_name=self
|
|
103
|
+
block_count=2 cstruct_size=1 is_def=true if not self.api_key:
|
|
104
|
+
compo c_name=self
|
|
105
|
+
block_count=3 cstruct_size=1 is_def=true raise ValueError('TAVILY_API_KEY must be set for Tavily Search.')
|
|
106
|
+
compo c_name=ValueError
|
|
107
|
+
block_count=2 cstruct_size=1 is_def=true self.tool = TavilySearchResults(api_key=self.api_key)
|
|
108
|
+
compo c_name=self
|
|
109
|
+
compo c_name=TavilySearchResults
|
|
110
|
+
block_count=1 cstruct_size=1 is_def=true def run(self, query: str) ->str:
|
|
111
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
112
|
+
block_count=3 cstruct_size=1 is_def=true results = self.tool.invoke({'query': query})
|
|
113
|
+
compo c_name=self
|
|
114
|
+
block_count=3 cstruct_size=1 is_def=true formatted_results = []
|
|
115
|
+
block_count=3 cstruct_size=1 is_def=true for item in results:
|
|
116
|
+
block_count=4 cstruct_size=1 is_def=true content = item.get('content')
|
|
117
|
+
compo c_name=item
|
|
118
|
+
block_count=4 cstruct_size=1 is_def=true url = item.get('url')
|
|
119
|
+
compo c_name=item
|
|
120
|
+
block_count=4 cstruct_size=1 is_def=true formatted_results.append(f'Content: {content}\nURL: {url}\n')
|
|
121
|
+
block_count=3 cstruct_size=1 is_def=true return '\n'.join(formatted_results
|
|
122
|
+
block_count=4 cstruct_size=1 is_def=true ) if formatted_results else 'No results found.'
|
|
123
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
124
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error during Tavily Search: {e}'
|
|
125
|
+
block_count=0 cstruct_size=1 is_def=true class BrowserUseSearchWrapper:
|
|
126
|
+
end of agent.TavilySearchWrapper
|
|
127
|
+
class_name=agent.BrowserUseSearchWrapper
|
|
128
|
+
base_name=
|
|
129
|
+
block_count=1 cstruct_size=1 is_def=false def __init__(self):
|
|
130
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
131
|
+
compo c_name=os
|
|
132
|
+
block_count=2 cstruct_size=1 is_def=true self.llm = ChatGoogle(model=model_name, api_key=os.getenv(
|
|
133
|
+
compo c_name=self
|
|
134
|
+
compo c_name=ChatGoogle
|
|
135
|
+
block_count=3 cstruct_size=1 is_def=true 'GOOGLE_API_KEY'))
|
|
136
|
+
block_count=2 cstruct_size=1 is_def=true user_agent = (
|
|
137
|
+
block_count=3 cstruct_size=1 is_def=true 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
138
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
139
|
+
block_count=2 cstruct_size=1 is_def=true self.browser_profile = BrowserProfile(headless=False, user_agent=
|
|
140
|
+
compo c_name=self
|
|
141
|
+
compo c_name=BrowserProfile
|
|
142
|
+
block_count=3 cstruct_size=1 is_def=true user_agent)
|
|
143
|
+
block_count=1 cstruct_size=1 is_def=true async def _search_async(self, query: str) ->str:
|
|
144
|
+
block_count=2 cstruct_size=1 is_def=false task = f"""
|
|
145
|
+
block_count=2 cstruct_size=1 is_def=false agent = Agent(task=task, llm=self.llm, browser_profile=self.
|
|
146
|
+
compo c_name=Agent
|
|
147
|
+
block_count=3 cstruct_size=1 is_def=false browser_profile)
|
|
148
|
+
block_count=2 cstruct_size=1 is_def=false result = await agent.run()
|
|
149
|
+
compo c_name=agent
|
|
150
|
+
block_count=2 cstruct_size=1 is_def=false return result.final_result()
|
|
151
|
+
compo c_name=result
|
|
152
|
+
block_count=1 cstruct_size=1 is_def=false def run(self, query: str) ->str:
|
|
153
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
154
|
+
block_count=3 cstruct_size=1 is_def=true return asyncio.run(self._search_async(query))
|
|
155
|
+
compo c_name=asyncio
|
|
156
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
157
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error during Browser Use Search: {e}'
|
|
158
|
+
block_count=0 cstruct_size=1 is_def=true def get_search_tool_func():
|
|
159
|
+
end of agent.BrowserUseSearchWrapper
|
|
160
|
+
block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
|
|
161
|
+
compo c_name=os
|
|
162
|
+
block_count=1 cstruct_size=0 is_def=true if provider == 'tavily_api':
|
|
163
|
+
block_count=2 cstruct_size=0 is_def=true return TavilySearchWrapper()
|
|
164
|
+
compo c_name=TavilySearchWrapper
|
|
165
|
+
block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
|
|
166
|
+
block_count=2 cstruct_size=0 is_def=true return BrowserUseSearchWrapper()
|
|
167
|
+
compo c_name=BrowserUseSearchWrapper
|
|
168
|
+
block_count=1 cstruct_size=0 is_def=true else:
|
|
169
|
+
block_count=2 cstruct_size=0 is_def=true return SerpAPIWrapper()
|
|
170
|
+
compo c_name=SerpAPIWrapper
|
|
171
|
+
block_count=0 cstruct_size=0 is_def=true def get_all_products():
|
|
172
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
173
|
+
compo c_name=sqlite3
|
|
174
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
175
|
+
compo c_name=conn
|
|
176
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
177
|
+
compo c_name=cursor
|
|
178
|
+
block_count=2 cstruct_size=0 is_def=true 'SELECT id, name, store, price, url, description, model_number, release_date, ram, ssd, updated_at FROM products'
|
|
179
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
180
|
+
block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
|
|
181
|
+
compo c_name=cursor
|
|
182
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
183
|
+
compo c_name=conn
|
|
184
|
+
block_count=1 cstruct_size=0 is_def=true products = []
|
|
185
|
+
block_count=1 cstruct_size=0 is_def=true for r in rows:
|
|
186
|
+
block_count=2 cstruct_size=0 is_def=true products.append({'id': r[0], 'name': r[1], 'store': r[2], 'price':
|
|
187
|
+
compo c_name=products
|
|
188
|
+
block_count=3 cstruct_size=0 is_def=true r[3], 'url': r[4], 'description': r[5], 'model_number': r[6] if
|
|
189
|
+
block_count=3 cstruct_size=0 is_def=true len(r) > 6 else '', 'release_date': r[7] if len(r) > 7 else '',
|
|
190
|
+
block_count=3 cstruct_size=0 is_def=true 'ram': r[8] if len(r) > 8 else '', 'ssd': r[9] if len(r) > 9 else
|
|
191
|
+
block_count=3 cstruct_size=0 is_def=true '', 'updated_at': r[10] if len(r) > 10 else ''})
|
|
192
|
+
block_count=1 cstruct_size=0 is_def=true return products
|
|
193
|
+
block_count=0 cstruct_size=0 is_def=true def parse_price_val(p_str):
|
|
194
|
+
block_count=1 cstruct_size=0 is_def=true if not p_str:
|
|
195
|
+
block_count=2 cstruct_size=0 is_def=true return float('inf')
|
|
196
|
+
block_count=1 cstruct_size=0 is_def=true s = str(p_str).replace(',', '')
|
|
197
|
+
block_count=1 cstruct_size=0 is_def=true match_man = re.search('(\\d+(\\.\\d+)?)万', s)
|
|
198
|
+
compo c_name=re
|
|
199
|
+
block_count=1 cstruct_size=0 is_def=true if match_man:
|
|
200
|
+
block_count=2 cstruct_size=0 is_def=true try:
|
|
201
|
+
block_count=3 cstruct_size=0 is_def=true val = float(match_man.group(1)) * 10000
|
|
202
|
+
block_count=3 cstruct_size=0 is_def=true return int(val)
|
|
203
|
+
block_count=2 cstruct_size=0 is_def=true except:
|
|
204
|
+
block_count=3 cstruct_size=0 is_def=true pass
|
|
205
|
+
block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', s)
|
|
206
|
+
compo c_name=re
|
|
207
|
+
block_count=1 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
|
|
208
|
+
block_count=0 cstruct_size=0 is_def=true def extract_alphanumeric(s: str) ->str:
|
|
209
|
+
block_count=1 cstruct_size=0 is_def=true """Extracts only alphanumeric characters from a string and converts to lowercase for robust comparison."""
|
|
210
|
+
block_count=1 cstruct_size=0 is_def=true if not s:
|
|
211
|
+
block_count=2 cstruct_size=0 is_def=true return ''
|
|
212
|
+
block_count=1 cstruct_size=0 is_def=true return re.sub('[^a-zA-Z0-9]', '', str(s)).lower()
|
|
213
|
+
compo c_name=re
|
|
214
|
+
block_count=0 cstruct_size=0 is_def=true def parse_date_val(d_str: str) ->str:
|
|
215
|
+
block_count=1 cstruct_size=0 is_def=true """
|
|
216
|
+
block_count=1 cstruct_size=0 is_def=true if not d_str:
|
|
217
|
+
block_count=2 cstruct_size=0 is_def=true return ''
|
|
218
|
+
block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(d_str))
|
|
219
|
+
compo c_name=re
|
|
220
|
+
block_count=1 cstruct_size=0 is_def=true if len(nums) >= 2:
|
|
221
|
+
block_count=2 cstruct_size=0 is_def=true year = nums[0]
|
|
222
|
+
block_count=2 cstruct_size=0 is_def=true month = nums[1].zfill(2)
|
|
223
|
+
block_count=2 cstruct_size=0 is_def=true return f'{year}{month}'
|
|
224
|
+
block_count=1 cstruct_size=0 is_def=true elif len(nums) == 1:
|
|
225
|
+
block_count=2 cstruct_size=0 is_def=true return nums[0]
|
|
226
|
+
block_count=1 cstruct_size=0 is_def=true else:
|
|
227
|
+
block_count=2 cstruct_size=0 is_def=true return extract_alphanumeric(d_str)
|
|
228
|
+
block_count=0 cstruct_size=0 is_def=true def is_similar_model(m1: str, m2: str) ->bool:
|
|
229
|
+
block_count=1 cstruct_size=0 is_def=true """
|
|
230
|
+
block_count=1 cstruct_size=0 is_def=true am1 = extract_alphanumeric(m1)
|
|
231
|
+
block_count=1 cstruct_size=0 is_def=true am2 = extract_alphanumeric(m2)
|
|
232
|
+
block_count=1 cstruct_size=0 is_def=true if not am1 and not am2:
|
|
233
|
+
block_count=2 cstruct_size=0 is_def=true return True
|
|
234
|
+
block_count=1 cstruct_size=0 is_def=true if not am1 or not am2:
|
|
235
|
+
block_count=2 cstruct_size=0 is_def=true return False
|
|
236
|
+
block_count=1 cstruct_size=0 is_def=true return am1 in am2 or am2 in am1
|
|
237
|
+
block_count=0 cstruct_size=0 is_def=true def save_agent_log(query, steps):
|
|
238
|
+
block_count=1 cstruct_size=0 is_def=true """Saves the agent's scratchpad (intermediate steps) to the database."""
|
|
239
|
+
block_count=1 cstruct_size=0 is_def=true if not steps:
|
|
240
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
241
|
+
block_count=1 cstruct_size=0 is_def=true log_content = []
|
|
242
|
+
block_count=1 cstruct_size=0 is_def=true for action, observation in steps:
|
|
243
|
+
block_count=2 cstruct_size=0 is_def=true if isinstance(action, list):
|
|
244
|
+
block_count=3 cstruct_size=0 is_def=true for a in action:
|
|
245
|
+
block_count=4 cstruct_size=0 is_def=true log_content.append(f'Tool: {a.tool}')
|
|
246
|
+
block_count=4 cstruct_size=0 is_def=true log_content.append(f'Input: {a.tool_input}')
|
|
247
|
+
block_count=4 cstruct_size=0 is_def=true log_content.append(f'Log: {a.log}')
|
|
248
|
+
block_count=2 cstruct_size=0 is_def=true else:
|
|
249
|
+
block_count=3 cstruct_size=0 is_def=true log_content.append(f'Tool: {action.tool}')
|
|
250
|
+
block_count=3 cstruct_size=0 is_def=true log_content.append(f'Input: {action.tool_input}')
|
|
251
|
+
block_count=3 cstruct_size=0 is_def=true log_content.append(f'Log: {action.log}')
|
|
252
|
+
block_count=2 cstruct_size=0 is_def=true log_content.append(f'Observation: {observation}')
|
|
253
|
+
block_count=2 cstruct_size=0 is_def=true log_content.append('-' * 20)
|
|
254
|
+
block_count=1 cstruct_size=0 is_def=true scratchpad_text = '\n'.join(log_content)
|
|
255
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
256
|
+
block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
257
|
+
compo c_name=sqlite3
|
|
258
|
+
block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
259
|
+
compo c_name=conn
|
|
260
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute(
|
|
261
|
+
compo c_name=cursor
|
|
262
|
+
block_count=3 cstruct_size=0 is_def=true 'INSERT INTO agent_logs (query, scratchpad) VALUES (?, ?)', (
|
|
263
|
+
block_count=3 cstruct_size=0 is_def=true query, scratchpad_text))
|
|
264
|
+
block_count=2 cstruct_size=0 is_def=true conn.commit()
|
|
265
|
+
compo c_name=conn
|
|
266
|
+
block_count=2 cstruct_size=0 is_def=true conn.close()
|
|
267
|
+
compo c_name=conn
|
|
268
|
+
block_count=2 cstruct_size=0 is_def=true print(f' [Log saved to database]')
|
|
269
|
+
block_count=1 cstruct_size=0 is_def=true except Exception as e:
|
|
270
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Error saving log: {e}')
|
|
271
|
+
block_count=0 cstruct_size=0 is_def=true def get_all_agent_logs():
|
|
272
|
+
block_count=1 cstruct_size=0 is_def=true """Fetches all agent logs from the database."""
|
|
273
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
274
|
+
block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
275
|
+
compo c_name=sqlite3
|
|
276
|
+
block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
277
|
+
compo c_name=conn
|
|
278
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute(
|
|
279
|
+
compo c_name=cursor
|
|
280
|
+
block_count=3 cstruct_size=0 is_def=true 'SELECT query, scratchpad FROM agent_logs ORDER BY timestamp DESC')
|
|
281
|
+
block_count=2 cstruct_size=0 is_def=true rows = cursor.fetchall()
|
|
282
|
+
compo c_name=cursor
|
|
283
|
+
block_count=2 cstruct_size=0 is_def=true conn.close()
|
|
284
|
+
compo c_name=conn
|
|
285
|
+
block_count=2 cstruct_size=0 is_def=true logs = []
|
|
286
|
+
block_count=2 cstruct_size=0 is_def=true for r in rows:
|
|
287
|
+
block_count=3 cstruct_size=0 is_def=true logs.append(f'Query: {r[0]}\nLog:\n{r[1]}\n')
|
|
288
|
+
compo c_name=logs
|
|
289
|
+
block_count=2 cstruct_size=0 is_def=true return '\n'.join(logs)
|
|
290
|
+
block_count=1 cstruct_size=0 is_def=true except Exception as e:
|
|
291
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Error fetching logs: {e}')
|
|
292
|
+
block_count=2 cstruct_size=0 is_def=true return ''
|
|
293
|
+
block_count=0 cstruct_size=0 is_def=true def send_email_notification(subject: str, body: str):
|
|
294
|
+
block_count=1 cstruct_size=0 is_def=true """Sends an email notification."""
|
|
295
|
+
block_count=1 cstruct_size=0 is_def=true smtp_server = os.getenv('EMAIL_SMTP_SERVER')
|
|
296
|
+
compo c_name=os
|
|
297
|
+
block_count=1 cstruct_size=0 is_def=true smtp_port = os.getenv('EMAIL_SMTP_PORT')
|
|
298
|
+
compo c_name=os
|
|
299
|
+
block_count=1 cstruct_size=0 is_def=true sender_email = os.getenv('EMAIL_SENDER_ADDRESS')
|
|
300
|
+
compo c_name=os
|
|
301
|
+
block_count=1 cstruct_size=0 is_def=true sender_password = os.getenv('EMAIL_SENDER_PASSWORD')
|
|
302
|
+
compo c_name=os
|
|
303
|
+
block_count=1 cstruct_size=0 is_def=true receiver_email = os.getenv('EMAIL_RECEIVER_ADDRESS')
|
|
304
|
+
compo c_name=os
|
|
305
|
+
block_count=1 cstruct_size=0 is_def=true if not all([smtp_server, smtp_port, sender_email, receiver_email]):
|
|
306
|
+
block_count=2 cstruct_size=0 is_def=true print(
|
|
307
|
+
block_count=3 cstruct_size=0 is_def=true 'Email configuration missing (Server, Port, Sender, Receiver). Skipping notification.'
|
|
308
|
+
block_count=3 cstruct_size=0 is_def=true )
|
|
309
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
310
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
311
|
+
block_count=2 cstruct_size=0 is_def=true msg = MIMEMultipart()
|
|
312
|
+
compo c_name=MIMEMultipart
|
|
313
|
+
block_count=2 cstruct_size=0 is_def=true msg['From'] = sender_email
|
|
314
|
+
block_count=2 cstruct_size=0 is_def=true msg['To'] = receiver_email
|
|
315
|
+
block_count=2 cstruct_size=0 is_def=true msg['Subject'] = subject
|
|
316
|
+
block_count=2 cstruct_size=0 is_def=true msg.attach(MIMEText(body, 'plain'))
|
|
317
|
+
compo c_name=msg
|
|
318
|
+
compo c_name=MIMEText
|
|
319
|
+
block_count=2 cstruct_size=0 is_def=true server = smtplib.SMTP(smtp_server, int(smtp_port))
|
|
320
|
+
compo c_name=SMTP
|
|
321
|
+
block_count=2 cstruct_size=0 is_def=true server.starttls()
|
|
322
|
+
compo c_name=server
|
|
323
|
+
block_count=2 cstruct_size=0 is_def=true if sender_password:
|
|
324
|
+
block_count=3 cstruct_size=0 is_def=true server.login(sender_email, sender_password)
|
|
325
|
+
compo c_name=server
|
|
326
|
+
block_count=2 cstruct_size=0 is_def=true server.send_message(msg)
|
|
327
|
+
compo c_name=server
|
|
328
|
+
block_count=2 cstruct_size=0 is_def=true server.quit()
|
|
329
|
+
compo c_name=server
|
|
330
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Email notification sent: {subject}')
|
|
331
|
+
block_count=1 cstruct_size=0 is_def=true except Exception as e:
|
|
332
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Failed to send email: {e}')
|
|
333
|
+
block_count=0 cstruct_size=0 is_def=true class SaveProductInput(BaseModel):
|
|
334
|
+
class_name=agent.SaveProductInput
|
|
335
|
+
base_name=BaseModel
|
|
336
|
+
block_count=1 cstruct_size=1 is_def=false name: str = Field(description='Name of the product')
|
|
337
|
+
compo c_name=Field
|
|
338
|
+
block_count=1 cstruct_size=1 is_def=false store: str = Field(description='Name of the store selling the product')
|
|
339
|
+
compo c_name=Field
|
|
340
|
+
block_count=1 cstruct_size=1 is_def=false price: str = Field(description='Price of the product')
|
|
341
|
+
compo c_name=Field
|
|
342
|
+
block_count=1 cstruct_size=1 is_def=false url: Optional[str] = Field(description='URL of the product page',
|
|
343
|
+
compo c_name=Field
|
|
344
|
+
block_count=2 cstruct_size=1 is_def=false default='')
|
|
345
|
+
block_count=1 cstruct_size=1 is_def=false description: Optional[str] = Field(description=
|
|
346
|
+
compo c_name=Field
|
|
347
|
+
block_count=2 cstruct_size=1 is_def=false 'Brief description of the product', default='')
|
|
348
|
+
block_count=1 cstruct_size=1 is_def=false model_number: Optional[str] = Field(description=
|
|
349
|
+
compo c_name=Field
|
|
350
|
+
block_count=2 cstruct_size=1 is_def=false 'Model number (型番) of the product', default='')
|
|
351
|
+
block_count=1 cstruct_size=1 is_def=false release_date: Optional[str] = Field(description=
|
|
352
|
+
compo c_name=Field
|
|
353
|
+
block_count=2 cstruct_size=1 is_def=false 'Release date (発売日) of the product', default='')
|
|
354
|
+
block_count=1 cstruct_size=1 is_def=false ram: Optional[str] = Field(description='RAM size', default='')
|
|
355
|
+
compo c_name=Field
|
|
356
|
+
block_count=1 cstruct_size=1 is_def=false ssd: Optional[str] = Field(description='SSD size', default='')
|
|
357
|
+
compo c_name=Field
|
|
358
|
+
block_count=1 cstruct_size=1 is_def=false @model_validator(mode='before')
|
|
359
|
+
block_count=1 cstruct_size=1 is_def=false @classmethod
|
|
360
|
+
block_count=1 cstruct_size=1 is_def=false def parse_json_input(cls, data: Any) ->Any:
|
|
361
|
+
block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict):
|
|
362
|
+
block_count=3 cstruct_size=1 is_def=true if 'name' in data and ('store' not in data or 'price' not in data):
|
|
363
|
+
block_count=4 cstruct_size=1 is_def=true name_val = data['name']
|
|
364
|
+
block_count=4 cstruct_size=1 is_def=true if isinstance(name_val, str) and name_val.strip().startswith(
|
|
365
|
+
block_count=5 cstruct_size=1 is_def=true '{') and name_val.strip().endswith('}'):
|
|
366
|
+
block_count=5 cstruct_size=1 is_def=true try:
|
|
367
|
+
block_count=6 cstruct_size=1 is_def=true parsed = json.loads(name_val)
|
|
368
|
+
compo c_name=json
|
|
369
|
+
block_count=6 cstruct_size=1 is_def=true if isinstance(parsed, dict):
|
|
370
|
+
block_count=7 cstruct_size=1 is_def=true data = parsed
|
|
371
|
+
block_count=5 cstruct_size=1 is_def=true except json.JSONDecodeError:
|
|
372
|
+
block_count=6 cstruct_size=1 is_def=true pass
|
|
373
|
+
block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict) and 'price' in data:
|
|
374
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(data['price'], (int, float)):
|
|
375
|
+
block_count=4 cstruct_size=1 is_def=true data['price'] = str(data['price'])
|
|
376
|
+
block_count=2 cstruct_size=1 is_def=true return data
|
|
377
|
+
block_count=0 cstruct_size=1 is_def=true class SaveProductTool(BaseTool):
|
|
378
|
+
end of agent.SaveProductInput
|
|
379
|
+
class_name=agent.SaveProductTool
|
|
380
|
+
base_name=BaseTool
|
|
381
|
+
block_count=1 cstruct_size=1 is_def=false name = 'save_product'
|
|
382
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
383
|
+
block_count=2 cstruct_size=1 is_def=false 'Saves product information (name, store, price, url, description, model_number, release_date, ram, ssd) to the database.'
|
|
384
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
385
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SaveProductInput
|
|
386
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, name: str, store: str=None, price: str=None, url: str='',
|
|
387
|
+
block_count=2 cstruct_size=1 is_def=true description: str='', model_number: str='', release_date: str='',
|
|
388
|
+
block_count=2 cstruct_size=1 is_def=true ram: str='', ssd: str='', **kwargs):
|
|
389
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
390
|
+
block_count=3 cstruct_size=1 is_def=true parsed_data = {}
|
|
391
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(name, dict):
|
|
392
|
+
block_count=4 cstruct_size=1 is_def=true parsed_data = name
|
|
393
|
+
block_count=3 cstruct_size=1 is_def=true elif isinstance(name, str) and name.strip().startswith('{'
|
|
394
|
+
compo c_name=name
|
|
395
|
+
block_count=4 cstruct_size=1 is_def=true ) and name.strip().endswith('}'):
|
|
396
|
+
compo c_name=name
|
|
397
|
+
block_count=4 cstruct_size=1 is_def=true try:
|
|
398
|
+
block_count=5 cstruct_size=1 is_def=true parsed_data = json.loads(name)
|
|
399
|
+
compo c_name=json
|
|
400
|
+
block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError:
|
|
401
|
+
block_count=5 cstruct_size=1 is_def=true pass
|
|
402
|
+
block_count=3 cstruct_size=1 is_def=true if parsed_data:
|
|
403
|
+
block_count=4 cstruct_size=1 is_def=true if 'name' in parsed_data:
|
|
404
|
+
block_count=5 cstruct_size=1 is_def=true name = parsed_data['name']
|
|
405
|
+
block_count=4 cstruct_size=1 is_def=true if store is None:
|
|
406
|
+
block_count=5 cstruct_size=1 is_def=true store = parsed_data.get('store')
|
|
407
|
+
block_count=4 cstruct_size=1 is_def=true if price is None:
|
|
408
|
+
block_count=5 cstruct_size=1 is_def=true price = parsed_data.get('price')
|
|
409
|
+
block_count=4 cstruct_size=1 is_def=true if not url:
|
|
410
|
+
block_count=5 cstruct_size=1 is_def=true url = parsed_data.get('url', '')
|
|
411
|
+
block_count=4 cstruct_size=1 is_def=true if not description:
|
|
412
|
+
block_count=5 cstruct_size=1 is_def=true description = parsed_data.get('description', '')
|
|
413
|
+
block_count=4 cstruct_size=1 is_def=true if not model_number:
|
|
414
|
+
block_count=5 cstruct_size=1 is_def=true model_number = parsed_data.get('model_number', '')
|
|
415
|
+
block_count=4 cstruct_size=1 is_def=true if not release_date:
|
|
416
|
+
block_count=5 cstruct_size=1 is_def=true release_date = parsed_data.get('release_date', '')
|
|
417
|
+
block_count=4 cstruct_size=1 is_def=true if not ram:
|
|
418
|
+
block_count=5 cstruct_size=1 is_def=true ram = parsed_data.get('ram', '')
|
|
419
|
+
block_count=4 cstruct_size=1 is_def=true if not ssd:
|
|
420
|
+
block_count=5 cstruct_size=1 is_def=true ssd = parsed_data.get('ssd', '')
|
|
421
|
+
block_count=3 cstruct_size=1 is_def=true if store is None:
|
|
422
|
+
block_count=4 cstruct_size=1 is_def=true store = kwargs.get('store')
|
|
423
|
+
compo c_name=kwargs
|
|
424
|
+
block_count=3 cstruct_size=1 is_def=true if price is None:
|
|
425
|
+
block_count=4 cstruct_size=1 is_def=true price = kwargs.get('price')
|
|
426
|
+
compo c_name=kwargs
|
|
427
|
+
block_count=3 cstruct_size=1 is_def=true if not model_number:
|
|
428
|
+
block_count=4 cstruct_size=1 is_def=true model_number = kwargs.get('model_number', '')
|
|
429
|
+
compo c_name=kwargs
|
|
430
|
+
block_count=3 cstruct_size=1 is_def=true if not release_date:
|
|
431
|
+
block_count=4 cstruct_size=1 is_def=true release_date = kwargs.get('release_date', '')
|
|
432
|
+
compo c_name=kwargs
|
|
433
|
+
block_count=3 cstruct_size=1 is_def=true if not ram:
|
|
434
|
+
block_count=4 cstruct_size=1 is_def=true ram = kwargs.get('ram', '')
|
|
435
|
+
compo c_name=kwargs
|
|
436
|
+
block_count=3 cstruct_size=1 is_def=true if not ssd:
|
|
437
|
+
block_count=4 cstruct_size=1 is_def=true ssd = kwargs.get('ssd', '')
|
|
438
|
+
compo c_name=kwargs
|
|
439
|
+
block_count=3 cstruct_size=1 is_def=true if not name or not store or not price:
|
|
440
|
+
block_count=4 cstruct_size=1 is_def=true return (
|
|
441
|
+
block_count=5 cstruct_size=1 is_def=true f'Error: Required arguments missing. Name: {name}, Store: {store}, Price: {price}'
|
|
442
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
443
|
+
block_count=3 cstruct_size=1 is_def=true if not url or not description:
|
|
444
|
+
block_count=4 cstruct_size=1 is_def=true return (
|
|
445
|
+
block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL or description is missing. URL: '{url}', Description: '{description}'"
|
|
446
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
447
|
+
block_count=3 cstruct_size=1 is_def=true if not url.startswith('http://') and not url.startswith('https://'
|
|
448
|
+
compo c_name=url
|
|
449
|
+
block_count=4 cstruct_size=1 is_def=true ):
|
|
450
|
+
block_count=4 cstruct_size=1 is_def=true return (
|
|
451
|
+
block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL must start with 'http://' or 'https://'. URL: '{url}'"
|
|
452
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
453
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(price, (int, float)):
|
|
454
|
+
block_count=4 cstruct_size=1 is_def=true price = str(price)
|
|
455
|
+
block_count=3 cstruct_size=1 is_def=true price_val = parse_price_val(price)
|
|
456
|
+
block_count=3 cstruct_size=1 is_def=true if price_val == float('inf'):
|
|
457
|
+
block_count=4 cstruct_size=1 is_def=true if not re.search('\\d', str(price)):
|
|
458
|
+
compo c_name=re
|
|
459
|
+
block_count=5 cstruct_size=1 is_def=true return (
|
|
460
|
+
block_count=6 cstruct_size=1 is_def=true f"Skipped saving product '{name}': Price info is missing or invalid ('{price}')."
|
|
461
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
462
|
+
block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
463
|
+
compo c_name=sqlite3
|
|
464
|
+
block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
|
|
465
|
+
compo c_name=conn
|
|
466
|
+
block_count=3 cstruct_size=1 is_def=true cursor.execute(
|
|
467
|
+
compo c_name=cursor
|
|
468
|
+
block_count=4 cstruct_size=1 is_def=true 'SELECT id, store, price, url FROM products WHERE name = ?',
|
|
469
|
+
block_count=4 cstruct_size=1 is_def=true (name,))
|
|
470
|
+
block_count=3 cstruct_size=1 is_def=true rows = cursor.fetchall()
|
|
471
|
+
compo c_name=cursor
|
|
472
|
+
block_count=3 cstruct_size=1 is_def=true items = []
|
|
473
|
+
block_count=3 cstruct_size=1 is_def=true for r in rows:
|
|
474
|
+
block_count=4 cstruct_size=1 is_def=true items.append({'id': r[0], 'store': r[1], 'price_str': r[2],
|
|
475
|
+
compo c_name=items
|
|
476
|
+
block_count=5 cstruct_size=1 is_def=true 'price_val': parse_price_val(r[2]), 'url': r[3]})
|
|
477
|
+
block_count=3 cstruct_size=1 is_def=true new_price_val = parse_price_val(price)
|
|
478
|
+
block_count=3 cstruct_size=1 is_def=true msg = ''
|
|
479
|
+
block_count=3 cstruct_size=1 is_def=true items.sort(key=lambda x: x['price_val'])
|
|
480
|
+
compo c_name=items
|
|
481
|
+
block_count=3 cstruct_size=1 is_def=true current_cheapest = items[0] if items else None
|
|
482
|
+
block_count=3 cstruct_size=1 is_def=true should_save = False
|
|
483
|
+
block_count=3 cstruct_size=1 is_def=true should_update = False
|
|
484
|
+
block_count=3 cstruct_size=1 is_def=true if not current_cheapest:
|
|
485
|
+
block_count=4 cstruct_size=1 is_def=true should_save = True
|
|
486
|
+
block_count=3 cstruct_size=1 is_def=true elif new_price_val < current_cheapest['price_val']:
|
|
487
|
+
block_count=4 cstruct_size=1 is_def=true should_save = True
|
|
488
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (name,))
|
|
489
|
+
compo c_name=cursor
|
|
490
|
+
block_count=4 cstruct_size=1 is_def=true msg_prefix = (
|
|
491
|
+
block_count=5 cstruct_size=1 is_def=true f"Found cheaper price! Updated {name} from {current_cheapest['store']} ({current_cheapest['price_str']}) to {store} ({price})."
|
|
492
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
493
|
+
block_count=3 cstruct_size=1 is_def=true elif new_price_val == current_cheapest['price_val']:
|
|
494
|
+
block_count=4 cstruct_size=1 is_def=true if store == current_cheapest['store']:
|
|
495
|
+
block_count=5 cstruct_size=1 is_def=true should_update = True
|
|
496
|
+
block_count=5 cstruct_size=1 is_def=true msg_prefix = f'Updated info for {name} at {store}.'
|
|
497
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
498
|
+
block_count=5 cstruct_size=1 is_def=true msg = (
|
|
499
|
+
block_count=6 cstruct_size=1 is_def=true f"Product {name} exists with same price at {current_cheapest['store']}. Keeping existing."
|
|
500
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
501
|
+
block_count=3 cstruct_size=1 is_def=true elif store == current_cheapest['store']:
|
|
502
|
+
block_count=4 cstruct_size=1 is_def=true should_update = True
|
|
503
|
+
block_count=4 cstruct_size=1 is_def=true msg_prefix = (
|
|
504
|
+
block_count=5 cstruct_size=1 is_def=true f"Price increased for {name} at {store}: {current_cheapest['price_str']} -> {price}."
|
|
505
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
506
|
+
block_count=3 cstruct_size=1 is_def=true else:
|
|
507
|
+
block_count=4 cstruct_size=1 is_def=true msg = (
|
|
508
|
+
block_count=5 cstruct_size=1 is_def=true f"Product {name} exists cheaper at {current_cheapest['store']} ({current_cheapest['price_str']}). Ignoring {store} ({price})."
|
|
509
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
510
|
+
block_count=3 cstruct_size=1 is_def=true if should_save:
|
|
511
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute(
|
|
512
|
+
compo c_name=cursor
|
|
513
|
+
block_count=5 cstruct_size=1 is_def=true """
|
|
514
|
+
block_count=5 cstruct_size=1 is_def=true , (name, store, price, url, description, model_number,
|
|
515
|
+
block_count=5 cstruct_size=1 is_def=true release_date, ram, ssd))
|
|
516
|
+
block_count=4 cstruct_size=1 is_def=true if not msg:
|
|
517
|
+
block_count=5 cstruct_size=1 is_def=true msg = f'Saved product: {name} from {store} for {price}.'
|
|
518
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
519
|
+
block_count=5 cstruct_size=1 is_def=true msg = msg_prefix
|
|
520
|
+
block_count=4 cstruct_size=1 is_def=true email_subject = f'Product Saved: {name}'
|
|
521
|
+
block_count=4 cstruct_size=1 is_def=true email_body = f"""Action: Saved (New or Cheaper)
|
|
522
|
+
block_count=4 cstruct_size=1 is_def=true send_email_notification(email_subject, email_body)
|
|
523
|
+
block_count=3 cstruct_size=1 is_def=true if should_update:
|
|
524
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute(
|
|
525
|
+
compo c_name=cursor
|
|
526
|
+
block_count=5 cstruct_size=1 is_def=true 'SELECT price, url, description, model_number, release_date, ram, ssd FROM products WHERE id = ?'
|
|
527
|
+
block_count=5 cstruct_size=1 is_def=true , (current_cheapest['id'],))
|
|
528
|
+
block_count=4 cstruct_size=1 is_def=true curr_row = cursor.fetchone()
|
|
529
|
+
compo c_name=cursor
|
|
530
|
+
block_count=4 cstruct_size=1 is_def=true curr_price_str = curr_row[0]
|
|
531
|
+
block_count=4 cstruct_size=1 is_def=true curr_url = curr_row[1]
|
|
532
|
+
block_count=4 cstruct_size=1 is_def=true curr_desc = curr_row[2]
|
|
533
|
+
block_count=4 cstruct_size=1 is_def=true curr_model = curr_row[3]
|
|
534
|
+
block_count=4 cstruct_size=1 is_def=true curr_release = curr_row[4]
|
|
535
|
+
block_count=4 cstruct_size=1 is_def=true curr_ram = curr_row[5] if len(curr_row) > 5 else ''
|
|
536
|
+
block_count=4 cstruct_size=1 is_def=true curr_ssd = curr_row[6] if len(curr_row) > 6 else ''
|
|
537
|
+
block_count=4 cstruct_size=1 is_def=true final_model = model_number if model_number else curr_model
|
|
538
|
+
block_count=4 cstruct_size=1 is_def=true final_release = release_date if release_date else curr_release
|
|
539
|
+
block_count=4 cstruct_size=1 is_def=true final_ram = ram if ram else curr_ram
|
|
540
|
+
block_count=4 cstruct_size=1 is_def=true final_ssd = ssd if ssd else curr_ssd
|
|
541
|
+
block_count=4 cstruct_size=1 is_def=true if (price != curr_price_str or url != curr_url or
|
|
542
|
+
block_count=5 cstruct_size=1 is_def=true description != curr_desc or final_model != curr_model or
|
|
543
|
+
block_count=5 cstruct_size=1 is_def=true final_release != curr_release or final_ram != curr_ram or
|
|
544
|
+
block_count=5 cstruct_size=1 is_def=true final_ssd != curr_ssd):
|
|
545
|
+
block_count=5 cstruct_size=1 is_def=true cursor.execute(
|
|
546
|
+
compo c_name=cursor
|
|
547
|
+
block_count=6 cstruct_size=1 is_def=true """
|
|
548
|
+
block_count=6 cstruct_size=1 is_def=true , (price, url, description, final_model,
|
|
549
|
+
block_count=6 cstruct_size=1 is_def=true final_release, final_ram, final_ssd, store,
|
|
550
|
+
block_count=6 cstruct_size=1 is_def=true current_cheapest['id']))
|
|
551
|
+
block_count=5 cstruct_size=1 is_def=true if not msg:
|
|
552
|
+
block_count=6 cstruct_size=1 is_def=true msg = f'Updated product {name} info.'
|
|
553
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
554
|
+
block_count=6 cstruct_size=1 is_def=true msg = msg_prefix
|
|
555
|
+
block_count=5 cstruct_size=1 is_def=true email_subject = f'Product Updated: {name}'
|
|
556
|
+
block_count=5 cstruct_size=1 is_def=true email_body = f"""Action: Updated Info
|
|
557
|
+
block_count=5 cstruct_size=1 is_def=true send_email_notification(email_subject, email_body)
|
|
558
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
559
|
+
block_count=5 cstruct_size=1 is_def=true msg = f'No changes for {name} at {store}.'
|
|
560
|
+
block_count=3 cstruct_size=1 is_def=true if should_save or should_update:
|
|
561
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute('SELECT id, price FROM products WHERE name = ?',
|
|
562
|
+
compo c_name=cursor
|
|
563
|
+
block_count=5 cstruct_size=1 is_def=true (name,))
|
|
564
|
+
block_count=4 cstruct_size=1 is_def=true rows = cursor.fetchall()
|
|
565
|
+
compo c_name=cursor
|
|
566
|
+
block_count=4 cstruct_size=1 is_def=true if len(rows) > 1:
|
|
567
|
+
block_count=5 cstruct_size=1 is_def=true rows_parsed = []
|
|
568
|
+
block_count=5 cstruct_size=1 is_def=true for r in rows:
|
|
569
|
+
block_count=6 cstruct_size=1 is_def=true rows_parsed.append({'id': r[0], 'val':
|
|
570
|
+
block_count=7 cstruct_size=1 is_def=true parse_price_val(r[1])})
|
|
571
|
+
block_count=5 cstruct_size=1 is_def=true rows_parsed.sort(key=lambda x: x['val'])
|
|
572
|
+
block_count=5 cstruct_size=1 is_def=true winner = rows_parsed[0]
|
|
573
|
+
block_count=5 cstruct_size=1 is_def=true for loser in rows_parsed[1:]:
|
|
574
|
+
block_count=6 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?',
|
|
575
|
+
compo c_name=cursor
|
|
576
|
+
block_count=7 cstruct_size=1 is_def=true (loser['id'],))
|
|
577
|
+
block_count=5 cstruct_size=1 is_def=true msg += ' (Cleaned up duplicate records)'
|
|
578
|
+
block_count=3 cstruct_size=1 is_def=true conn.commit()
|
|
579
|
+
compo c_name=conn
|
|
580
|
+
block_count=3 cstruct_size=1 is_def=true conn.close()
|
|
581
|
+
compo c_name=conn
|
|
582
|
+
block_count=3 cstruct_size=1 is_def=true return msg
|
|
583
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
584
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error saving product: {str(e)}'
|
|
585
|
+
block_count=0 cstruct_size=1 is_def=true class UpdatePricesInput(BaseModel):
|
|
586
|
+
end of agent.SaveProductTool
|
|
587
|
+
class_name=agent.UpdatePricesInput
|
|
588
|
+
base_name=BaseModel
|
|
589
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
|
|
590
|
+
compo c_name=Field
|
|
591
|
+
block_count=0 cstruct_size=1 is_def=false class UpdatePricesTool(BaseTool):
|
|
592
|
+
end of agent.UpdatePricesInput
|
|
593
|
+
class_name=agent.UpdatePricesTool
|
|
594
|
+
base_name=BaseTool
|
|
595
|
+
block_count=1 cstruct_size=1 is_def=false name = 'update_prices'
|
|
596
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
597
|
+
block_count=2 cstruct_size=1 is_def=false 'Accesses the registered URL for each product in the database directly to check stock, price, and specs. Updates info or deletes if unavailable.'
|
|
598
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
599
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = UpdatePricesInput
|
|
600
|
+
block_count=1 cstruct_size=1 is_def=false def _fetch_page_content(self, url: str) ->(bool, str, str):
|
|
601
|
+
block_count=2 cstruct_size=1 is_def=true """
|
|
602
|
+
block_count=2 cstruct_size=1 is_def=true if not url or not url.startswith('http'):
|
|
603
|
+
compo c_name=url
|
|
604
|
+
block_count=3 cstruct_size=1 is_def=true return False, 'Invalid URL', ''
|
|
605
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
606
|
+
block_count=3 cstruct_size=1 is_def=true req = urllib.request.Request(url, headers={'User-Agent':
|
|
607
|
+
compo c_name=urllib
|
|
608
|
+
compo c_name=Request
|
|
609
|
+
block_count=4 cstruct_size=1 is_def=true 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
610
|
+
block_count=4 cstruct_size=1 is_def=true , 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8'})
|
|
611
|
+
block_count=3 cstruct_size=1 is_def=true with urllib.request.urlopen(req, timeout=15) as response:
|
|
612
|
+
compo c_name=urllib
|
|
613
|
+
block_count=4 cstruct_size=1 is_def=true html_content = response.read().decode('utf-8', errors='ignore')
|
|
614
|
+
compo c_name=response
|
|
615
|
+
block_count=4 cstruct_size=1 is_def=true bot_keywords = ['ロボットではありません', 'アクセスが制限されています', 'キャプチャ',
|
|
616
|
+
block_count=5 cstruct_size=1 is_def=true 'CAPTCHA', 'Are you a human?',
|
|
617
|
+
block_count=5 cstruct_size=1 is_def=true 'Please verify you are a human', 'Incapsula', 'Cloudflare']
|
|
618
|
+
block_count=4 cstruct_size=1 is_def=true html_lower = html_content.lower()
|
|
619
|
+
block_count=4 cstruct_size=1 is_def=true for kw in bot_keywords:
|
|
620
|
+
block_count=5 cstruct_size=1 is_def=true if kw.lower() in html_lower:
|
|
621
|
+
compo c_name=kw
|
|
622
|
+
block_count=6 cstruct_size=1 is_def=true return False, 'Bot Challenge Detected', ''
|
|
623
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<script.*?>.*?</script>', '',
|
|
624
|
+
compo c_name=re
|
|
625
|
+
block_count=5 cstruct_size=1 is_def=true html_content, flags=re.DOTALL | re.IGNORECASE)
|
|
626
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<style.*?>.*?</style>', '', clean_text,
|
|
627
|
+
compo c_name=re
|
|
628
|
+
block_count=5 cstruct_size=1 is_def=true flags=re.DOTALL | re.IGNORECASE)
|
|
629
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<img[^>]+alt="([^"]*)"[^>]*>', ' \\1 ',
|
|
630
|
+
compo c_name=re
|
|
631
|
+
block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
|
|
632
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub("<img[^>]+alt='([^']*)'[^>]*>", ' \\1 ',
|
|
633
|
+
compo c_name=re
|
|
634
|
+
block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
|
|
635
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<.*?>', ' ', clean_text)
|
|
636
|
+
compo c_name=re
|
|
637
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('\\s+', ' ', clean_text).strip()
|
|
638
|
+
compo c_name=re
|
|
639
|
+
block_count=4 cstruct_size=1 is_def=true if len(clean_text) > 10000:
|
|
640
|
+
block_count=5 cstruct_size=1 is_def=true clean_text = clean_text[:10000]
|
|
641
|
+
block_count=4 cstruct_size=1 is_def=true return True, 'Success', clean_text
|
|
642
|
+
block_count=2 cstruct_size=1 is_def=true except urllib.error.HTTPError as e:
|
|
643
|
+
compo c_name=urllib
|
|
644
|
+
block_count=3 cstruct_size=1 is_def=true if e.code == 404:
|
|
645
|
+
block_count=4 cstruct_size=1 is_def=true return False, '404 Not Found', ''
|
|
646
|
+
block_count=3 cstruct_size=1 is_def=true elif e.code == 410:
|
|
647
|
+
block_count=4 cstruct_size=1 is_def=true return False, '410 Gone', ''
|
|
648
|
+
block_count=3 cstruct_size=1 is_def=true elif e.code in [500, 502, 503, 504]:
|
|
649
|
+
block_count=4 cstruct_size=1 is_def=true return False, f'Retryable Server Error ({e.code})', ''
|
|
650
|
+
block_count=3 cstruct_size=1 is_def=true elif e.code == 403:
|
|
651
|
+
block_count=4 cstruct_size=1 is_def=true return False, '403 Forbidden (Possible Bot Block)', ''
|
|
652
|
+
block_count=3 cstruct_size=1 is_def=true else:
|
|
653
|
+
block_count=4 cstruct_size=1 is_def=true return False, f'HTTP Error {e.code}', ''
|
|
654
|
+
block_count=2 cstruct_size=1 is_def=true except urllib.error.URLError as e:
|
|
655
|
+
compo c_name=urllib
|
|
656
|
+
block_count=3 cstruct_size=1 is_def=true return False, f'URL Error: {e.reason}', ''
|
|
657
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
658
|
+
block_count=3 cstruct_size=1 is_def=true return False, f'Connection Error: {e}', ''
|
|
659
|
+
block_count=1 cstruct_size=1 is_def=true def _delete_product(self, product: dict, reason: str):
|
|
660
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
661
|
+
block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
662
|
+
compo c_name=sqlite3
|
|
663
|
+
block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
|
|
664
|
+
compo c_name=conn
|
|
665
|
+
block_count=3 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (product[
|
|
666
|
+
compo c_name=cursor
|
|
667
|
+
block_count=4 cstruct_size=1 is_def=true 'id'],))
|
|
668
|
+
block_count=3 cstruct_size=1 is_def=true conn.commit()
|
|
669
|
+
compo c_name=conn
|
|
670
|
+
block_count=3 cstruct_size=1 is_def=true conn.close()
|
|
671
|
+
compo c_name=conn
|
|
672
|
+
block_count=3 cstruct_size=1 is_def=true msg = f"Deleted {product['name']} at {product['store']} ({reason})"
|
|
673
|
+
block_count=3 cstruct_size=1 is_def=true print(f' {msg}')
|
|
674
|
+
block_count=3 cstruct_size=1 is_def=true email_subject = f"Product Deleted: {product['name']}"
|
|
675
|
+
block_count=3 cstruct_size=1 is_def=true email_body = f"""Action: Deleted (Unavailable/Not Found)
|
|
676
|
+
block_count=3 cstruct_size=1 is_def=true send_email_notification(email_subject, email_body)
|
|
677
|
+
block_count=3 cstruct_size=1 is_def=true return True
|
|
678
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
679
|
+
block_count=3 cstruct_size=1 is_def=true print(f' Error deleting product: {e}')
|
|
680
|
+
block_count=3 cstruct_size=1 is_def=true return False
|
|
681
|
+
block_count=1 cstruct_size=1 is_def=true def _run(self, query: str='', **kwargs):
|
|
682
|
+
block_count=2 cstruct_size=1 is_def=true print('\n--- Starting Price Update (Direct URL Access) ---')
|
|
683
|
+
block_count=2 cstruct_size=1 is_def=true products = get_all_products()
|
|
684
|
+
block_count=2 cstruct_size=1 is_def=true if not products:
|
|
685
|
+
block_count=3 cstruct_size=1 is_def=true return 'No products in database to update.'
|
|
686
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
687
|
+
compo c_name=os
|
|
688
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
|
|
689
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
690
|
+
block_count=2 cstruct_size=1 is_def=true updated_count = 0
|
|
691
|
+
block_count=2 cstruct_size=1 is_def=true deleted_count = 0
|
|
692
|
+
block_count=2 cstruct_size=1 is_def=true for p in products:
|
|
693
|
+
block_count=3 cstruct_size=1 is_def=true name = p['name']
|
|
694
|
+
block_count=3 cstruct_size=1 is_def=true store = p['store']
|
|
695
|
+
block_count=3 cstruct_size=1 is_def=true url = p['url']
|
|
696
|
+
block_count=3 cstruct_size=1 is_def=true print(f"Checking: {name} at {store} (ID: {p['id']})")
|
|
697
|
+
block_count=3 cstruct_size=1 is_def=true if not url:
|
|
698
|
+
block_count=4 cstruct_size=1 is_def=true print(f' [Warning] No URL for this product. Skipping.')
|
|
699
|
+
block_count=4 cstruct_size=1 is_def=true continue
|
|
700
|
+
block_count=3 cstruct_size=1 is_def=true success, access_reason, page_text = self._fetch_page_content(url)
|
|
701
|
+
block_count=3 cstruct_size=1 is_def=true if not success:
|
|
702
|
+
block_count=4 cstruct_size=1 is_def=true if ('404 Not Found' in access_reason or '410 Gone' in
|
|
703
|
+
block_count=5 cstruct_size=1 is_def=true access_reason or 'Invalid URL' in access_reason):
|
|
704
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
705
|
+
block_count=6 cstruct_size=1 is_def=true f' [Info] URL is dead ({access_reason}). Deleting product.'
|
|
706
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
707
|
+
block_count=5 cstruct_size=1 is_def=true if self._delete_product(p, access_reason):
|
|
708
|
+
block_count=6 cstruct_size=1 is_def=true deleted_count += 1
|
|
709
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
710
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
711
|
+
block_count=6 cstruct_size=1 is_def=true f' [Warning] Skipping update due to temporary/access error: {access_reason}'
|
|
712
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
713
|
+
block_count=4 cstruct_size=1 is_def=true continue
|
|
714
|
+
block_count=3 cstruct_size=1 is_def=true prompt = f"""
|
|
715
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
716
|
+
block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt)
|
|
717
|
+
compo c_name=llm
|
|
718
|
+
block_count=4 cstruct_size=1 is_def=true content = response.content
|
|
719
|
+
compo c_name=response
|
|
720
|
+
block_count=4 cstruct_size=1 is_def=true if '```json' in content:
|
|
721
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
722
|
+
compo c_name=content
|
|
723
|
+
block_count=4 cstruct_size=1 is_def=true elif '```' in content:
|
|
724
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
725
|
+
compo c_name=content
|
|
726
|
+
block_count=4 cstruct_size=1 is_def=true content = content.strip()
|
|
727
|
+
compo c_name=content
|
|
728
|
+
block_count=4 cstruct_size=1 is_def=true content = re.sub('\\\\(?![/"\\\\bfnrtu])', '\\\\\\\\', content)
|
|
729
|
+
compo c_name=re
|
|
730
|
+
block_count=4 cstruct_size=1 is_def=true try:
|
|
731
|
+
block_count=5 cstruct_size=1 is_def=true result_data = json.loads(content, strict=False)
|
|
732
|
+
compo c_name=json
|
|
733
|
+
block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
|
|
734
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
735
|
+
block_count=6 cstruct_size=1 is_def=true f' [Warning] Failed to parse JSON: {e}. Attempting further cleanup.'
|
|
736
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
737
|
+
block_count=5 cstruct_size=1 is_def=true content = content.replace('\\', '')
|
|
738
|
+
compo c_name=content
|
|
739
|
+
block_count=5 cstruct_size=1 is_def=true result_data = json.loads(content, strict=False)
|
|
740
|
+
compo c_name=json
|
|
741
|
+
block_count=4 cstruct_size=1 is_def=true is_unavailable = result_data.get('is_unavailable', False)
|
|
742
|
+
block_count=4 cstruct_size=1 is_def=true unavailability_reason = result_data.get('unavailability_reason'
|
|
743
|
+
block_count=5 cstruct_size=1 is_def=true , 'ページ内に在庫なし・販売終了の記載あり')
|
|
744
|
+
block_count=4 cstruct_size=1 is_def=true if is_unavailable:
|
|
745
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
746
|
+
block_count=6 cstruct_size=1 is_def=true f' [Info] LLM determined product is unavailable. Reason: {unavailability_reason}. Deleting product.'
|
|
747
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
748
|
+
block_count=5 cstruct_size=1 is_def=true if self._delete_product(p, unavailability_reason):
|
|
749
|
+
block_count=6 cstruct_size=1 is_def=true deleted_count += 1
|
|
750
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
751
|
+
block_count=5 cstruct_size=1 is_def=true new_price = result_data.get('price', '')
|
|
752
|
+
block_count=5 cstruct_size=1 is_def=true new_desc = result_data.get('description', '')
|
|
753
|
+
block_count=5 cstruct_size=1 is_def=true new_model = result_data.get('model_number', '')
|
|
754
|
+
block_count=5 cstruct_size=1 is_def=true new_release = result_data.get('release_date', '')
|
|
755
|
+
block_count=5 cstruct_size=1 is_def=true new_ram = result_data.get('ram', '')
|
|
756
|
+
block_count=5 cstruct_size=1 is_def=true new_ssd = result_data.get('ssd', '')
|
|
757
|
+
block_count=5 cstruct_size=1 is_def=true if not new_price:
|
|
758
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
759
|
+
block_count=7 cstruct_size=1 is_def=true f' [Warning] Could not extract price from page. Skipping update.'
|
|
760
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
761
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
762
|
+
block_count=5 cstruct_size=1 is_def=true final_desc = new_desc if new_desc else p['description']
|
|
763
|
+
block_count=5 cstruct_size=1 is_def=true final_model = new_model if new_model else p.get(
|
|
764
|
+
block_count=6 cstruct_size=1 is_def=true 'model_number', '')
|
|
765
|
+
block_count=5 cstruct_size=1 is_def=true final_release = new_release if new_release else p.get(
|
|
766
|
+
block_count=6 cstruct_size=1 is_def=true 'release_date', '')
|
|
767
|
+
block_count=5 cstruct_size=1 is_def=true final_ram = new_ram if new_ram else p.get('ram', '')
|
|
768
|
+
block_count=5 cstruct_size=1 is_def=true final_ssd = new_ssd if new_ssd else p.get('ssd', '')
|
|
769
|
+
block_count=5 cstruct_size=1 is_def=true changes = []
|
|
770
|
+
block_count=5 cstruct_size=1 is_def=true new_price_val = parse_price_val(new_price)
|
|
771
|
+
block_count=5 cstruct_size=1 is_def=true old_price_val = parse_price_val(p['price'])
|
|
772
|
+
block_count=5 cstruct_size=1 is_def=true if new_price_val != old_price_val:
|
|
773
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f"Price ({p['price']} -> {new_price})")
|
|
774
|
+
compo c_name=changes
|
|
775
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
776
|
+
block_count=6 cstruct_size=1 is_def=true new_price = p['price']
|
|
777
|
+
block_count=5 cstruct_size=1 is_def=true old_model = p.get('model_number', '')
|
|
778
|
+
block_count=5 cstruct_size=1 is_def=true if not is_similar_model(final_model, old_model):
|
|
779
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f'Model ({old_model} -> {final_model})')
|
|
780
|
+
compo c_name=changes
|
|
781
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
782
|
+
block_count=6 cstruct_size=1 is_def=true final_model = old_model
|
|
783
|
+
block_count=5 cstruct_size=1 is_def=true old_release = p.get('release_date', '')
|
|
784
|
+
block_count=5 cstruct_size=1 is_def=true if parse_date_val(final_release) != parse_date_val(
|
|
785
|
+
block_count=6 cstruct_size=1 is_def=true old_release):
|
|
786
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(
|
|
787
|
+
compo c_name=changes
|
|
788
|
+
block_count=7 cstruct_size=1 is_def=true f'Release Date ({old_release} -> {final_release})')
|
|
789
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
790
|
+
block_count=6 cstruct_size=1 is_def=true final_release = old_release
|
|
791
|
+
block_count=5 cstruct_size=1 is_def=true old_ram = p.get('ram', '')
|
|
792
|
+
block_count=5 cstruct_size=1 is_def=true if extract_alphanumeric(final_ram) != extract_alphanumeric(
|
|
793
|
+
block_count=6 cstruct_size=1 is_def=true old_ram):
|
|
794
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f'RAM ({old_ram} -> {final_ram})')
|
|
795
|
+
compo c_name=changes
|
|
796
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
797
|
+
block_count=6 cstruct_size=1 is_def=true final_ram = old_ram
|
|
798
|
+
block_count=5 cstruct_size=1 is_def=true old_ssd = p.get('ssd', '')
|
|
799
|
+
block_count=5 cstruct_size=1 is_def=true if extract_alphanumeric(final_ssd) != extract_alphanumeric(
|
|
800
|
+
block_count=6 cstruct_size=1 is_def=true old_ssd):
|
|
801
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f'SSD ({old_ssd} -> {final_ssd})')
|
|
802
|
+
compo c_name=changes
|
|
803
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
804
|
+
block_count=6 cstruct_size=1 is_def=true final_ssd = old_ssd
|
|
805
|
+
block_count=5 cstruct_size=1 is_def=true if changes:
|
|
806
|
+
block_count=6 cstruct_size=1 is_def=true try:
|
|
807
|
+
block_count=7 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
808
|
+
compo c_name=sqlite3
|
|
809
|
+
block_count=7 cstruct_size=1 is_def=true cursor = conn.cursor()
|
|
810
|
+
compo c_name=conn
|
|
811
|
+
block_count=7 cstruct_size=1 is_def=true cursor.execute(
|
|
812
|
+
compo c_name=cursor
|
|
813
|
+
block_count=8 cstruct_size=1 is_def=true """
|
|
814
|
+
block_count=8 cstruct_size=1 is_def=true , (new_price, final_desc, final_model,
|
|
815
|
+
block_count=8 cstruct_size=1 is_def=true final_release, final_ram, final_ssd, p['id']))
|
|
816
|
+
block_count=7 cstruct_size=1 is_def=true conn.commit()
|
|
817
|
+
compo c_name=conn
|
|
818
|
+
block_count=7 cstruct_size=1 is_def=true if cursor.rowcount > 0:
|
|
819
|
+
compo c_name=cursor
|
|
820
|
+
block_count=8 cstruct_size=1 is_def=true updated_count += 1
|
|
821
|
+
block_count=8 cstruct_size=1 is_def=true changes_str = ', '.join(changes)
|
|
822
|
+
block_count=8 cstruct_size=1 is_def=true msg = (
|
|
823
|
+
block_count=9 cstruct_size=1 is_def=true f'Updated {name} at {store}. Changes: {changes_str}'
|
|
824
|
+
block_count=9 cstruct_size=1 is_def=true )
|
|
825
|
+
block_count=8 cstruct_size=1 is_def=true print(f' {msg}')
|
|
826
|
+
block_count=8 cstruct_size=1 is_def=true email_subject = f'Product Updated: {name}'
|
|
827
|
+
block_count=8 cstruct_size=1 is_def=true email_body = f"""Action: Updated Info (Direct URL Check)
|
|
828
|
+
block_count=8 cstruct_size=1 is_def=true send_email_notification(email_subject,
|
|
829
|
+
block_count=9 cstruct_size=1 is_def=true email_body)
|
|
830
|
+
block_count=7 cstruct_size=1 is_def=true conn.close()
|
|
831
|
+
compo c_name=conn
|
|
832
|
+
block_count=6 cstruct_size=1 is_def=true except Exception as e:
|
|
833
|
+
block_count=7 cstruct_size=1 is_def=true print(f' Error updating {name} at {store}: {e}')
|
|
834
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
835
|
+
block_count=6 cstruct_size=1 is_def=true print(f' No spec/price changes for {name} at {store}.'
|
|
836
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
837
|
+
block_count=3 cstruct_size=1 is_def=true except Exception as e:
|
|
838
|
+
block_count=4 cstruct_size=1 is_def=true print(f' Error processing LLM response for {name}: {e}')
|
|
839
|
+
block_count=3 cstruct_size=1 is_def=true time.sleep(1)
|
|
840
|
+
compo c_name=time
|
|
841
|
+
block_count=2 cstruct_size=1 is_def=true return (
|
|
842
|
+
block_count=3 cstruct_size=1 is_def=true f'Price update complete. Updated {updated_count} items, Deleted {deleted_count} unavailable items.'
|
|
843
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
844
|
+
block_count=0 cstruct_size=1 is_def=true class SearchProductsInput(BaseModel):
|
|
845
|
+
end of agent.UpdatePricesTool
|
|
846
|
+
class_name=agent.SearchProductsInput
|
|
847
|
+
base_name=BaseModel
|
|
848
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
|
|
849
|
+
compo c_name=Field
|
|
850
|
+
block_count=2 cstruct_size=1 is_def=false 'Natural language query to search products in the database')
|
|
851
|
+
block_count=0 cstruct_size=1 is_def=false class SearchProductsTool(BaseTool):
|
|
852
|
+
end of agent.SearchProductsInput
|
|
853
|
+
class_name=agent.SearchProductsTool
|
|
854
|
+
base_name=BaseTool
|
|
855
|
+
block_count=1 cstruct_size=1 is_def=false name = 'search_products'
|
|
856
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
857
|
+
block_count=2 cstruct_size=1 is_def=false "Searches for products in the database using natural language queries (e.g., 'cheapest products', 'items with 16GB memory')."
|
|
858
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
859
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SearchProductsInput
|
|
860
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, query: str, **kwargs):
|
|
861
|
+
block_count=2 cstruct_size=1 is_def=true print(f'\n--- Searching Database: {query} ---')
|
|
862
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
863
|
+
compo c_name=os
|
|
864
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
|
|
865
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
866
|
+
block_count=2 cstruct_size=1 is_def=true prompt = f"""
|
|
867
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
868
|
+
block_count=3 cstruct_size=1 is_def=true response = llm.invoke(prompt)
|
|
869
|
+
compo c_name=llm
|
|
870
|
+
block_count=3 cstruct_size=1 is_def=true content = response.content.strip()
|
|
871
|
+
compo c_name=response
|
|
872
|
+
block_count=3 cstruct_size=1 is_def=true if '```json' in content:
|
|
873
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
874
|
+
compo c_name=content
|
|
875
|
+
block_count=3 cstruct_size=1 is_def=true elif '```' in content:
|
|
876
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
877
|
+
compo c_name=content
|
|
878
|
+
block_count=3 cstruct_size=1 is_def=true criteria = json.loads(content)
|
|
879
|
+
compo c_name=json
|
|
880
|
+
block_count=3 cstruct_size=1 is_def=true print(f' Search Criteria: {criteria}')
|
|
881
|
+
block_count=3 cstruct_size=1 is_def=true all_products = get_all_products()
|
|
882
|
+
block_count=3 cstruct_size=1 is_def=true filtered_products = []
|
|
883
|
+
block_count=3 cstruct_size=1 is_def=true keyword_groups = criteria.get('keyword_groups', [])
|
|
884
|
+
compo c_name=criteria
|
|
885
|
+
block_count=3 cstruct_size=1 is_def=true exclude_keywords = criteria.get('exclude_keywords', [])
|
|
886
|
+
compo c_name=criteria
|
|
887
|
+
block_count=3 cstruct_size=1 is_def=true empty_fields = criteria.get('empty_fields', [])
|
|
888
|
+
compo c_name=criteria
|
|
889
|
+
block_count=3 cstruct_size=1 is_def=true sort_by = criteria.get('sort_by')
|
|
890
|
+
compo c_name=criteria
|
|
891
|
+
block_count=3 cstruct_size=1 is_def=true max_p = criteria.get('max_price')
|
|
892
|
+
compo c_name=criteria
|
|
893
|
+
block_count=3 cstruct_size=1 is_def=true min_p = criteria.get('min_price')
|
|
894
|
+
compo c_name=criteria
|
|
895
|
+
block_count=3 cstruct_size=1 is_def=true for p in all_products:
|
|
896
|
+
block_count=4 cstruct_size=1 is_def=true text_to_search = (p['name'] + ' ' + (p['description'] or ''
|
|
897
|
+
block_count=5 cstruct_size=1 is_def=true ) + ' ' + (p['url'] or '')).lower()
|
|
898
|
+
block_count=4 cstruct_size=1 is_def=true if empty_fields:
|
|
899
|
+
block_count=5 cstruct_size=1 is_def=true is_empty_match = True
|
|
900
|
+
block_count=5 cstruct_size=1 is_def=true for field in empty_fields:
|
|
901
|
+
block_count=6 cstruct_size=1 is_def=true val = p.get(field)
|
|
902
|
+
block_count=6 cstruct_size=1 is_def=true if val and str(val).strip():
|
|
903
|
+
block_count=7 cstruct_size=1 is_def=true is_empty_match = False
|
|
904
|
+
block_count=7 cstruct_size=1 is_def=true break
|
|
905
|
+
block_count=5 cstruct_size=1 is_def=true if not is_empty_match:
|
|
906
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
907
|
+
block_count=4 cstruct_size=1 is_def=true if exclude_keywords:
|
|
908
|
+
block_count=5 cstruct_size=1 is_def=true should_exclude = False
|
|
909
|
+
block_count=5 cstruct_size=1 is_def=true for k in exclude_keywords:
|
|
910
|
+
block_count=6 cstruct_size=1 is_def=true if k.lower() in text_to_search:
|
|
911
|
+
block_count=7 cstruct_size=1 is_def=true should_exclude = True
|
|
912
|
+
block_count=7 cstruct_size=1 is_def=true break
|
|
913
|
+
block_count=5 cstruct_size=1 is_def=true if should_exclude:
|
|
914
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
915
|
+
block_count=4 cstruct_size=1 is_def=true if keyword_groups:
|
|
916
|
+
block_count=5 cstruct_size=1 is_def=true all_groups_match = True
|
|
917
|
+
block_count=5 cstruct_size=1 is_def=true for group in keyword_groups:
|
|
918
|
+
block_count=6 cstruct_size=1 is_def=true group_match = False
|
|
919
|
+
block_count=6 cstruct_size=1 is_def=true for k in group:
|
|
920
|
+
block_count=7 cstruct_size=1 is_def=true if k.lower() in text_to_search:
|
|
921
|
+
block_count=8 cstruct_size=1 is_def=true group_match = True
|
|
922
|
+
block_count=8 cstruct_size=1 is_def=true break
|
|
923
|
+
block_count=6 cstruct_size=1 is_def=true if not group_match:
|
|
924
|
+
block_count=7 cstruct_size=1 is_def=true all_groups_match = False
|
|
925
|
+
block_count=7 cstruct_size=1 is_def=true break
|
|
926
|
+
block_count=5 cstruct_size=1 is_def=true if not all_groups_match:
|
|
927
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
928
|
+
block_count=4 cstruct_size=1 is_def=true price_val = parse_price_val(p['price'])
|
|
929
|
+
block_count=4 cstruct_size=1 is_def=true if max_p is not None and price_val > max_p:
|
|
930
|
+
block_count=5 cstruct_size=1 is_def=true continue
|
|
931
|
+
block_count=4 cstruct_size=1 is_def=true if min_p is not None and price_val < min_p:
|
|
932
|
+
block_count=5 cstruct_size=1 is_def=true continue
|
|
933
|
+
block_count=4 cstruct_size=1 is_def=true p['price_val'] = price_val
|
|
934
|
+
block_count=4 cstruct_size=1 is_def=true filtered_products.append(p)
|
|
935
|
+
block_count=3 cstruct_size=1 is_def=true if sort_by == 'price_asc':
|
|
936
|
+
block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'])
|
|
937
|
+
block_count=3 cstruct_size=1 is_def=true elif sort_by == 'price_desc':
|
|
938
|
+
block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'],
|
|
939
|
+
block_count=5 cstruct_size=1 is_def=true reverse=True)
|
|
940
|
+
block_count=3 cstruct_size=1 is_def=true if not filtered_products:
|
|
941
|
+
block_count=4 cstruct_size=1 is_def=true return 'No products found matching your criteria.'
|
|
942
|
+
block_count=3 cstruct_size=1 is_def=true result_str = f'Found {len(filtered_products)} products:\n'
|
|
943
|
+
block_count=3 cstruct_size=1 is_def=true for p in filtered_products[:10]:
|
|
944
|
+
block_count=4 cstruct_size=1 is_def=true result_str += (
|
|
945
|
+
block_count=5 cstruct_size=1 is_def=true f"- [ID: {p['id']}] {p['name']} ({p['price']}) @ {p['store']}\n"
|
|
946
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
947
|
+
block_count=4 cstruct_size=1 is_def=true if p['description']:
|
|
948
|
+
block_count=5 cstruct_size=1 is_def=true result_str += f" Desc: {p['description'][:100]}...\n"
|
|
949
|
+
block_count=4 cstruct_size=1 is_def=true if p['url']:
|
|
950
|
+
block_count=5 cstruct_size=1 is_def=true result_str += f" URL: {p['url']}\n"
|
|
951
|
+
block_count=4 cstruct_size=1 is_def=true result_str += '\n'
|
|
952
|
+
block_count=3 cstruct_size=1 is_def=true return result_str
|
|
953
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
954
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error executing search: {e}'
|
|
955
|
+
block_count=0 cstruct_size=1 is_def=true class FindSimilarProductsInput(BaseModel):
|
|
956
|
+
end of agent.SearchProductsTool
|
|
957
|
+
class_name=agent.FindSimilarProductsInput
|
|
958
|
+
base_name=BaseModel
|
|
959
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
|
|
960
|
+
compo c_name=Field
|
|
961
|
+
block_count=0 cstruct_size=1 is_def=false class FindSimilarProductsTool(BaseTool):
|
|
962
|
+
end of agent.FindSimilarProductsInput
|
|
963
|
+
class_name=agent.FindSimilarProductsTool
|
|
964
|
+
base_name=BaseTool
|
|
965
|
+
block_count=1 cstruct_size=1 is_def=false name = 'find_similar_products'
|
|
966
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
967
|
+
block_count=2 cstruct_size=1 is_def=false 'Searches for similar products to those in the database and adds the best ones if found.'
|
|
968
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
969
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = FindSimilarProductsInput
|
|
970
|
+
block_count=1 cstruct_size=1 is_def=false agent_logs: str = ''
|
|
971
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
|
|
972
|
+
block_count=2 cstruct_size=1 is_def=true print('\n--- Starting Similar Product Search ---')
|
|
973
|
+
block_count=2 cstruct_size=1 is_def=true products = get_all_products()
|
|
974
|
+
block_count=2 cstruct_size=1 is_def=true if not products:
|
|
975
|
+
block_count=3 cstruct_size=1 is_def=true return 'No products in database to base search on.'
|
|
976
|
+
block_count=2 cstruct_size=1 is_def=true target_products = []
|
|
977
|
+
block_count=2 cstruct_size=1 is_def=true if query:
|
|
978
|
+
block_count=3 cstruct_size=1 is_def=true print(f'Filtering products with query: {query}')
|
|
979
|
+
block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
|
|
980
|
+
compo c_name=query
|
|
981
|
+
block_count=3 cstruct_size=1 is_def=true for p in products:
|
|
982
|
+
block_count=4 cstruct_size=1 is_def=true if query_lower in p['name'].lower() or p['description'
|
|
983
|
+
block_count=5 cstruct_size=1 is_def=true ] and query_lower in p['description'].lower():
|
|
984
|
+
block_count=5 cstruct_size=1 is_def=true target_products.append(p)
|
|
985
|
+
block_count=2 cstruct_size=1 is_def=true else:
|
|
986
|
+
block_count=3 cstruct_size=1 is_def=true target_products = products
|
|
987
|
+
block_count=2 cstruct_size=1 is_def=true if not target_products:
|
|
988
|
+
block_count=3 cstruct_size=1 is_def=true return f"No products found matching query '{query}'."
|
|
989
|
+
block_count=2 cstruct_size=1 is_def=true unique_products = {}
|
|
990
|
+
block_count=2 cstruct_size=1 is_def=true for p in target_products:
|
|
991
|
+
block_count=3 cstruct_size=1 is_def=true if p['name'] not in unique_products:
|
|
992
|
+
block_count=4 cstruct_size=1 is_def=true unique_products[p['name']] = p
|
|
993
|
+
block_count=2 cstruct_size=1 is_def=true target_names = list(unique_products.keys())
|
|
994
|
+
block_count=2 cstruct_size=1 is_def=true print(
|
|
995
|
+
block_count=3 cstruct_size=1 is_def=true f'Found {len(target_names)} target products to find similar items for.'
|
|
996
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
997
|
+
block_count=2 cstruct_size=1 is_def=true logs_context = ''
|
|
998
|
+
block_count=2 cstruct_size=1 is_def=true if self.agent_logs:
|
|
999
|
+
compo c_name=self
|
|
1000
|
+
block_count=3 cstruct_size=1 is_def=true logs_context = f'\n参考情報 (過去の検索履歴):\n{self.agent_logs}\n'
|
|
1001
|
+
block_count=2 cstruct_size=1 is_def=true search = get_search_tool_func()
|
|
1002
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
1003
|
+
compo c_name=os
|
|
1004
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
|
|
1005
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
1006
|
+
block_count=2 cstruct_size=1 is_def=true save_tool = SaveProductTool()
|
|
1007
|
+
compo c_name=SaveProductTool
|
|
1008
|
+
block_count=2 cstruct_size=1 is_def=true cached_similar_items = []
|
|
1009
|
+
block_count=2 cstruct_size=1 is_def=true max_targets = 5
|
|
1010
|
+
block_count=2 cstruct_size=1 is_def=true if len(target_names) > max_targets:
|
|
1011
|
+
block_count=3 cstruct_size=1 is_def=true print(f'Limiting search to first {max_targets} products.')
|
|
1012
|
+
block_count=3 cstruct_size=1 is_def=true target_names = target_names[:max_targets]
|
|
1013
|
+
block_count=2 cstruct_size=1 is_def=true for name in target_names:
|
|
1014
|
+
block_count=3 cstruct_size=1 is_def=true product_data = unique_products[name]
|
|
1015
|
+
block_count=3 cstruct_size=1 is_def=true description = product_data.get('description', '')
|
|
1016
|
+
block_count=3 cstruct_size=1 is_def=true print(f'Searching for similar items to: {name}')
|
|
1017
|
+
block_count=3 cstruct_size=1 is_def=true search_query = f'{name} 類似商品 おすすめ 比較 スペック'
|
|
1018
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
1019
|
+
block_count=4 cstruct_size=1 is_def=true search_results = search.run(search_query)
|
|
1020
|
+
compo c_name=search
|
|
1021
|
+
block_count=3 cstruct_size=1 is_def=true except Exception as e:
|
|
1022
|
+
block_count=4 cstruct_size=1 is_def=true print(f'Search failed for {name}: {e}')
|
|
1023
|
+
block_count=4 cstruct_size=1 is_def=true continue
|
|
1024
|
+
block_count=3 cstruct_size=1 is_def=true prompt = f"""
|
|
1025
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
1026
|
+
block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt)
|
|
1027
|
+
compo c_name=llm
|
|
1028
|
+
block_count=4 cstruct_size=1 is_def=true content = response.content
|
|
1029
|
+
compo c_name=response
|
|
1030
|
+
block_count=4 cstruct_size=1 is_def=true if '```json' in content:
|
|
1031
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
1032
|
+
compo c_name=content
|
|
1033
|
+
block_count=4 cstruct_size=1 is_def=true elif '```' in content:
|
|
1034
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
1035
|
+
compo c_name=content
|
|
1036
|
+
block_count=4 cstruct_size=1 is_def=true items = json.loads(content)
|
|
1037
|
+
compo c_name=json
|
|
1038
|
+
block_count=4 cstruct_size=1 is_def=true if isinstance(items, list):
|
|
1039
|
+
block_count=5 cstruct_size=1 is_def=true for item in items:
|
|
1040
|
+
block_count=6 cstruct_size=1 is_def=true if item.get('name') == name:
|
|
1041
|
+
compo c_name=item
|
|
1042
|
+
block_count=7 cstruct_size=1 is_def=true continue
|
|
1043
|
+
block_count=6 cstruct_size=1 is_def=true cached_similar_items.append(item)
|
|
1044
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
1045
|
+
block_count=7 cstruct_size=1 is_def=true f" Cached: {item.get('name')} ({item.get('price')})"
|
|
1046
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
1047
|
+
block_count=3 cstruct_size=1 is_def=true except Exception as e:
|
|
1048
|
+
block_count=4 cstruct_size=1 is_def=true print(f'Error processing similar items for {name}: {e}')
|
|
1049
|
+
block_count=3 cstruct_size=1 is_def=true time.sleep(1)
|
|
1050
|
+
compo c_name=time
|
|
1051
|
+
block_count=2 cstruct_size=1 is_def=true if not cached_similar_items:
|
|
1052
|
+
block_count=3 cstruct_size=1 is_def=true return 'No similar products found.'
|
|
1053
|
+
block_count=2 cstruct_size=1 is_def=true print(
|
|
1054
|
+
block_count=3 cstruct_size=1 is_def=true f'\nCached {len(cached_similar_items)} items. Selecting top 3 recommendations...'
|
|
1055
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
1056
|
+
block_count=2 cstruct_size=1 is_def=true selection_prompt = f"""
|
|
1057
|
+
block_count=2 cstruct_size=1 is_def=true added_count = 0
|
|
1058
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
1059
|
+
block_count=3 cstruct_size=1 is_def=true response = llm.invoke(selection_prompt)
|
|
1060
|
+
compo c_name=llm
|
|
1061
|
+
block_count=3 cstruct_size=1 is_def=true content = response.content
|
|
1062
|
+
compo c_name=response
|
|
1063
|
+
block_count=3 cstruct_size=1 is_def=true if '```json' in content:
|
|
1064
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
1065
|
+
compo c_name=content
|
|
1066
|
+
block_count=3 cstruct_size=1 is_def=true elif '```' in content:
|
|
1067
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
1068
|
+
compo c_name=content
|
|
1069
|
+
block_count=3 cstruct_size=1 is_def=true top_picks = json.loads(content)
|
|
1070
|
+
compo c_name=json
|
|
1071
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(top_picks, list):
|
|
1072
|
+
block_count=4 cstruct_size=1 is_def=true for item in top_picks:
|
|
1073
|
+
block_count=5 cstruct_size=1 is_def=true print(f" Saving recommendation: {item.get('name')}")
|
|
1074
|
+
block_count=5 cstruct_size=1 is_def=true res = save_tool._run(name=item.get('name'), store=item.
|
|
1075
|
+
block_count=6 cstruct_size=1 is_def=true get('store', 'Unknown'), price=item.get('price'),
|
|
1076
|
+
block_count=6 cstruct_size=1 is_def=true url=item.get('url', ''), description=item.get(
|
|
1077
|
+
block_count=6 cstruct_size=1 is_def=true 'description', ''), model_number=item.get(
|
|
1078
|
+
block_count=6 cstruct_size=1 is_def=true 'model_number', ''), release_date=item.get(
|
|
1079
|
+
block_count=6 cstruct_size=1 is_def=true 'release_date', ''))
|
|
1080
|
+
block_count=5 cstruct_size=1 is_def=true print(f' -> {res}')
|
|
1081
|
+
block_count=5 cstruct_size=1 is_def=true added_count += 1
|
|
1082
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
1083
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error selecting top recommendations: {e}'
|
|
1084
|
+
block_count=2 cstruct_size=1 is_def=true return (
|
|
1085
|
+
block_count=3 cstruct_size=1 is_def=true f'Similar product search complete. Added {added_count} recommended items.'
|
|
1086
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
1087
|
+
block_count=0 cstruct_size=1 is_def=true class CompareProductsInput(BaseModel):
|
|
1088
|
+
end of agent.FindSimilarProductsTool
|
|
1089
|
+
class_name=agent.CompareProductsInput
|
|
1090
|
+
base_name=BaseModel
|
|
1091
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
|
|
1092
|
+
compo c_name=Field
|
|
1093
|
+
block_count=2 cstruct_size=1 is_def=false "Optional category or query to filter products for comparison (e.g., 'laptop', 'monitor')."
|
|
1094
|
+
block_count=2 cstruct_size=1 is_def=false , default='')
|
|
1095
|
+
block_count=0 cstruct_size=1 is_def=false class CompareProductsTool(BaseTool):
|
|
1096
|
+
end of agent.CompareProductsInput
|
|
1097
|
+
class_name=agent.CompareProductsTool
|
|
1098
|
+
base_name=BaseTool
|
|
1099
|
+
block_count=1 cstruct_size=1 is_def=false name = 'compare_products'
|
|
1100
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
1101
|
+
block_count=2 cstruct_size=1 is_def=false 'Generates a comparison table of products (e.g. RAM, SSD, Price) and ranks them by recommendation. Saves the result as JSON.'
|
|
1102
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
1103
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = CompareProductsInput
|
|
1104
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
|
|
1105
|
+
block_count=2 cstruct_size=1 is_def=true print(f'\n--- Generating Product Comparison: {query} ---')
|
|
1106
|
+
block_count=2 cstruct_size=1 is_def=true products = get_all_products()
|
|
1107
|
+
block_count=2 cstruct_size=1 is_def=true if not products:
|
|
1108
|
+
block_count=3 cstruct_size=1 is_def=true return 'No products found in database.'
|
|
1109
|
+
block_count=2 cstruct_size=1 is_def=true target_products = []
|
|
1110
|
+
block_count=2 cstruct_size=1 is_def=true if query:
|
|
1111
|
+
block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
|
|
1112
|
+
compo c_name=query
|
|
1113
|
+
block_count=3 cstruct_size=1 is_def=true for p in products:
|
|
1114
|
+
block_count=4 cstruct_size=1 is_def=true text = (p['name'] + ' ' + (p['description'] or '')).lower()
|
|
1115
|
+
block_count=4 cstruct_size=1 is_def=true if query_lower in text:
|
|
1116
|
+
block_count=5 cstruct_size=1 is_def=true target_products.append(p)
|
|
1117
|
+
block_count=2 cstruct_size=1 is_def=true else:
|
|
1118
|
+
block_count=3 cstruct_size=1 is_def=true target_products = products
|
|
1119
|
+
block_count=2 cstruct_size=1 is_def=true if not target_products:
|
|
1120
|
+
block_count=3 cstruct_size=1 is_def=true return f"No products found matching '{query}'."
|
|
1121
|
+
block_count=2 cstruct_size=1 is_def=true CHUNK_SIZE = 5
|
|
1122
|
+
block_count=2 cstruct_size=1 is_def=true print(
|
|
1123
|
+
block_count=3 cstruct_size=1 is_def=true f'Step 1: Extracting specs from {len(target_products)} products in chunks of {CHUNK_SIZE}...'
|
|
1124
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
1125
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
1126
|
+
compo c_name=os
|
|
1127
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
|
|
1128
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
1129
|
+
block_count=3 cstruct_size=1 is_def=true max_output_tokens=8192)
|
|
1130
|
+
block_count=2 cstruct_size=1 is_def=true extracted_specs = []
|
|
1131
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
1132
|
+
block_count=3 cstruct_size=1 is_def=true for i in range(0, len(target_products), CHUNK_SIZE):
|
|
1133
|
+
block_count=4 cstruct_size=1 is_def=true chunk = target_products[i:i + CHUNK_SIZE]
|
|
1134
|
+
block_count=4 cstruct_size=1 is_def=true print(
|
|
1135
|
+
block_count=5 cstruct_size=1 is_def=true f' Processing chunk {i // CHUNK_SIZE + 1} ({len(chunk)} items)...'
|
|
1136
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1137
|
+
block_count=4 cstruct_size=1 is_def=true prompt_extract = f"""
|
|
1138
|
+
block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt_extract)
|
|
1139
|
+
compo c_name=llm
|
|
1140
|
+
block_count=4 cstruct_size=1 is_def=true content = response.content
|
|
1141
|
+
compo c_name=response
|
|
1142
|
+
block_count=4 cstruct_size=1 is_def=true if '```json' in content:
|
|
1143
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
1144
|
+
compo c_name=content
|
|
1145
|
+
block_count=4 cstruct_size=1 is_def=true elif '```' in content:
|
|
1146
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
1147
|
+
compo c_name=content
|
|
1148
|
+
block_count=4 cstruct_size=1 is_def=true content = content.strip()
|
|
1149
|
+
compo c_name=content
|
|
1150
|
+
block_count=4 cstruct_size=1 is_def=true try:
|
|
1151
|
+
block_count=5 cstruct_size=1 is_def=true chunk_data = json.loads(content)
|
|
1152
|
+
compo c_name=json
|
|
1153
|
+
block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
|
|
1154
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
1155
|
+
block_count=6 cstruct_size=1 is_def=true f' [Warning] Failed to parse JSON in chunk {i // CHUNK_SIZE + 1}. Attempting aggressive recovery.'
|
|
1156
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
1157
|
+
block_count=5 cstruct_size=1 is_def=true print(f' Error detail: {e}')
|
|
1158
|
+
block_count=5 cstruct_size=1 is_def=true try:
|
|
1159
|
+
block_count=6 cstruct_size=1 is_def=true start_idx = content.find('[')
|
|
1160
|
+
compo c_name=content
|
|
1161
|
+
block_count=6 cstruct_size=1 is_def=true if start_idx != -1:
|
|
1162
|
+
block_count=7 cstruct_size=1 is_def=true clean_content = content[start_idx:]
|
|
1163
|
+
block_count=7 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
|
|
1164
|
+
block_count=8 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
|
|
1165
|
+
block_count=8 cstruct_size=1 is_def=true if last_brace != -1:
|
|
1166
|
+
block_count=9 cstruct_size=1 is_def=true clean_content = clean_content[:
|
|
1167
|
+
block_count=10 cstruct_size=1 is_def=true last_brace + 1] + ']'
|
|
1168
|
+
block_count=8 cstruct_size=1 is_def=true else:
|
|
1169
|
+
block_count=9 cstruct_size=1 is_def=true clean_content += ']'
|
|
1170
|
+
block_count=7 cstruct_size=1 is_def=true chunk_data = json.loads(clean_content)
|
|
1171
|
+
compo c_name=json
|
|
1172
|
+
block_count=6 cstruct_size=1 is_def=true else:
|
|
1173
|
+
block_count=7 cstruct_size=1 is_def=true raise ValueError('No JSON list found.')
|
|
1174
|
+
compo c_name=ValueError
|
|
1175
|
+
block_count=5 cstruct_size=1 is_def=true except Exception as e2:
|
|
1176
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
1177
|
+
block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover JSON even after aggressive repair: {e2}'
|
|
1178
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
1179
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
1180
|
+
block_count=4 cstruct_size=1 is_def=true if isinstance(chunk_data, list):
|
|
1181
|
+
block_count=5 cstruct_size=1 is_def=true extracted_specs.extend(chunk_data)
|
|
1182
|
+
block_count=4 cstruct_size=1 is_def=true time.sleep(1)
|
|
1183
|
+
compo c_name=time
|
|
1184
|
+
block_count=3 cstruct_size=1 is_def=true print(
|
|
1185
|
+
block_count=4 cstruct_size=1 is_def=true f'Step 2: Ranking {len(extracted_specs)} products based on extracted specs...'
|
|
1186
|
+
block_count=4 cstruct_size=1 is_def=true )
|
|
1187
|
+
block_count=3 cstruct_size=1 is_def=true prompt_rank = f"""
|
|
1188
|
+
block_count=3 cstruct_size=1 is_def=true response_rank = llm.invoke(prompt_rank)
|
|
1189
|
+
compo c_name=llm
|
|
1190
|
+
block_count=3 cstruct_size=1 is_def=true content_rank = response_rank.content
|
|
1191
|
+
block_count=3 cstruct_size=1 is_def=true if '```json' in content_rank:
|
|
1192
|
+
block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```json')[1].split('```')[0]
|
|
1193
|
+
block_count=3 cstruct_size=1 is_def=true elif '```' in content_rank:
|
|
1194
|
+
block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```')[1].split('```')[0]
|
|
1195
|
+
block_count=3 cstruct_size=1 is_def=true content_rank = content_rank.strip()
|
|
1196
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
1197
|
+
block_count=4 cstruct_size=1 is_def=true ranking_data = json.loads(content_rank)
|
|
1198
|
+
compo c_name=json
|
|
1199
|
+
block_count=3 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
|
|
1200
|
+
block_count=4 cstruct_size=1 is_def=true print(
|
|
1201
|
+
block_count=5 cstruct_size=1 is_def=true f' [Warning] Failed to parse ranking JSON. Error: {e}. Attempting recovery.'
|
|
1202
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1203
|
+
block_count=4 cstruct_size=1 is_def=true start_idx = content_rank.find('[')
|
|
1204
|
+
block_count=4 cstruct_size=1 is_def=true if start_idx != -1:
|
|
1205
|
+
block_count=5 cstruct_size=1 is_def=true clean_content = content_rank[start_idx:]
|
|
1206
|
+
block_count=5 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
|
|
1207
|
+
block_count=6 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
|
|
1208
|
+
block_count=6 cstruct_size=1 is_def=true if last_brace != -1:
|
|
1209
|
+
block_count=7 cstruct_size=1 is_def=true clean_content = clean_content[:last_brace + 1
|
|
1210
|
+
block_count=8 cstruct_size=1 is_def=true ] + ']'
|
|
1211
|
+
block_count=6 cstruct_size=1 is_def=true else:
|
|
1212
|
+
block_count=7 cstruct_size=1 is_def=true clean_content += ']'
|
|
1213
|
+
block_count=5 cstruct_size=1 is_def=true try:
|
|
1214
|
+
block_count=6 cstruct_size=1 is_def=true ranking_data = json.loads(clean_content)
|
|
1215
|
+
compo c_name=json
|
|
1216
|
+
block_count=5 cstruct_size=1 is_def=true except Exception as e2:
|
|
1217
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
1218
|
+
block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover ranking JSON: {e2}'
|
|
1219
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
1220
|
+
block_count=6 cstruct_size=1 is_def=true return (
|
|
1221
|
+
block_count=7 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
|
|
1222
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
1223
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
1224
|
+
block_count=5 cstruct_size=1 is_def=true return (
|
|
1225
|
+
block_count=6 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
|
|
1226
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
1227
|
+
block_count=3 cstruct_size=1 is_def=true spec_dict = {item['id']: item for item in extracted_specs}
|
|
1228
|
+
block_count=3 cstruct_size=1 is_def=true target_dict = {item['id']: item for item in target_products}
|
|
1229
|
+
block_count=3 cstruct_size=1 is_def=true final_comparison_data = []
|
|
1230
|
+
block_count=3 cstruct_size=1 is_def=true for rank_item in ranking_data:
|
|
1231
|
+
block_count=4 cstruct_size=1 is_def=true p_id = rank_item.get('id')
|
|
1232
|
+
block_count=4 cstruct_size=1 is_def=true if p_id in spec_dict:
|
|
1233
|
+
block_count=5 cstruct_size=1 is_def=true merged = spec_dict[p_id].copy()
|
|
1234
|
+
block_count=5 cstruct_size=1 is_def=true merged['rank'] = rank_item.get('rank')
|
|
1235
|
+
block_count=5 cstruct_size=1 is_def=true merged['note'] = rank_item.get('note', '')
|
|
1236
|
+
block_count=5 cstruct_size=1 is_def=true if p_id in target_dict:
|
|
1237
|
+
block_count=6 cstruct_size=1 is_def=true merged['updated_at'] = target_dict[p_id].get(
|
|
1238
|
+
block_count=7 cstruct_size=1 is_def=true 'updated_at', '')
|
|
1239
|
+
block_count=5 cstruct_size=1 is_def=true final_comparison_data.append(merged)
|
|
1240
|
+
block_count=3 cstruct_size=1 is_def=true final_comparison_data.sort(key=lambda x: x.get('rank', 9999))
|
|
1241
|
+
block_count=3 cstruct_size=1 is_def=true for item in final_comparison_data:
|
|
1242
|
+
block_count=4 cstruct_size=1 is_def=true if 'model_number' not in item or item['model_number'] is None:
|
|
1243
|
+
block_count=5 cstruct_size=1 is_def=true item['model_number'] = ''
|
|
1244
|
+
block_count=4 cstruct_size=1 is_def=true if 'release_date' not in item or item['release_date'] is None:
|
|
1245
|
+
block_count=5 cstruct_size=1 is_def=true item['release_date'] = ''
|
|
1246
|
+
block_count=3 cstruct_size=1 is_def=true from datetime import datetime
|
|
1247
|
+
block_count=3 cstruct_size=1 is_def=true output_data = {'updated_at': datetime.now().strftime(
|
|
1248
|
+
compo c_name=datetime
|
|
1249
|
+
block_count=4 cstruct_size=1 is_def=true '%Y-%m-%d %H:%M:%S'), 'products': final_comparison_data}
|
|
1250
|
+
block_count=3 cstruct_size=1 is_def=true output_file = 'product_comparison.json'
|
|
1251
|
+
block_count=3 cstruct_size=1 is_def=true with open(output_file, 'w', encoding='utf-8') as f:
|
|
1252
|
+
block_count=4 cstruct_size=1 is_def=true json.dump(output_data, f, ensure_ascii=False, indent=2)
|
|
1253
|
+
compo c_name=json
|
|
1254
|
+
block_count=3 cstruct_size=1 is_def=true return (
|
|
1255
|
+
block_count=4 cstruct_size=1 is_def=true f'Comparison table generated and saved to {output_file}. Included {len(final_comparison_data)} ranked items.'
|
|
1256
|
+
block_count=4 cstruct_size=1 is_def=true )
|
|
1257
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
1258
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error generating comparison: {e}'
|
|
1259
|
+
block_count=0 cstruct_size=1 is_def=true def display_products():
|
|
1260
|
+
end of agent.CompareProductsTool
|
|
1261
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1262
|
+
compo c_name=sqlite3
|
|
1263
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1264
|
+
compo c_name=conn
|
|
1265
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute('SELECT id, name, store, price FROM products')
|
|
1266
|
+
compo c_name=cursor
|
|
1267
|
+
block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
|
|
1268
|
+
compo c_name=cursor
|
|
1269
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
1270
|
+
compo c_name=conn
|
|
1271
|
+
block_count=1 cstruct_size=0 is_def=true if not rows:
|
|
1272
|
+
block_count=2 cstruct_size=0 is_def=true print('\nNo products saved yet.')
|
|
1273
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1274
|
+
block_count=1 cstruct_size=0 is_def=true def parse_price_sort(p_str):
|
|
1275
|
+
block_count=2 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(p_str).replace(',', ''))
|
|
1276
|
+
compo c_name=re
|
|
1277
|
+
block_count=2 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
|
|
1278
|
+
block_count=1 cstruct_size=0 is_def=true rows.sort(key=lambda x: (x[1], parse_price_sort(x[3])))
|
|
1279
|
+
compo c_name=rows
|
|
1280
|
+
block_count=1 cstruct_size=0 is_def=false print('\n--- All Saved Products ---')
|
|
1281
|
+
block_count=1 cstruct_size=0 is_def=false print(f"{'ID':<5} {'Name':<40} {'Store':<20} {'Price':<15}")
|
|
1282
|
+
block_count=1 cstruct_size=0 is_def=false print('-' * 85)
|
|
1283
|
+
block_count=1 cstruct_size=0 is_def=false for row in rows:
|
|
1284
|
+
block_count=2 cstruct_size=0 is_def=false name_disp = row[1][:37] + '..' if len(row[1]) > 39 else row[1]
|
|
1285
|
+
block_count=2 cstruct_size=0 is_def=false store_disp = row[2][:18] + '..' if len(row[2]) > 20 else row[2]
|
|
1286
|
+
block_count=2 cstruct_size=0 is_def=false print(f'{row[0]:<5} {name_disp:<40} {store_disp:<20} {row[3]:<15}')
|
|
1287
|
+
block_count=1 cstruct_size=0 is_def=false print('-' * 85)
|
|
1288
|
+
block_count=0 cstruct_size=0 is_def=false def show_product_details(product_id):
|
|
1289
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1290
|
+
compo c_name=sqlite3
|
|
1291
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1292
|
+
compo c_name=conn
|
|
1293
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
1294
|
+
compo c_name=cursor
|
|
1295
|
+
block_count=2 cstruct_size=0 is_def=true 'SELECT id, name, store, price, url, description, model_number, release_date FROM products WHERE id = ?'
|
|
1296
|
+
block_count=2 cstruct_size=0 is_def=true , (product_id,))
|
|
1297
|
+
block_count=1 cstruct_size=0 is_def=true row = cursor.fetchone()
|
|
1298
|
+
compo c_name=cursor
|
|
1299
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
1300
|
+
compo c_name=conn
|
|
1301
|
+
block_count=1 cstruct_size=0 is_def=true if not row:
|
|
1302
|
+
block_count=2 cstruct_size=0 is_def=true print(f'\nProduct with ID {product_id} not found.')
|
|
1303
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1304
|
+
block_count=1 cstruct_size=0 is_def=true print('\n--- Product Details ---')
|
|
1305
|
+
block_count=1 cstruct_size=0 is_def=true print(f'ID: {row[0]}')
|
|
1306
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Name: {row[1]}')
|
|
1307
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Store: {row[2]}')
|
|
1308
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Price: {row[3]}')
|
|
1309
|
+
block_count=1 cstruct_size=0 is_def=true print(f"Model: {row[6] if len(row) > 6 else ''}")
|
|
1310
|
+
block_count=1 cstruct_size=0 is_def=true print(f"Release: {row[7] if len(row) > 7 else ''}")
|
|
1311
|
+
block_count=1 cstruct_size=0 is_def=true print(f'URL: {row[4]}')
|
|
1312
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Description: {row[5]}')
|
|
1313
|
+
block_count=1 cstruct_size=0 is_def=true print('-' * 30)
|
|
1314
|
+
block_count=0 cstruct_size=0 is_def=true def delete_product_records(identifiers: List[str]):
|
|
1315
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1316
|
+
compo c_name=sqlite3
|
|
1317
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1318
|
+
compo c_name=conn
|
|
1319
|
+
block_count=1 cstruct_size=0 is_def=true deleted_count = 0
|
|
1320
|
+
block_count=1 cstruct_size=0 is_def=true errors = []
|
|
1321
|
+
block_count=1 cstruct_size=0 is_def=true print(f'\nAttempting to delete: {identifiers}')
|
|
1322
|
+
block_count=1 cstruct_size=0 is_def=true for identifier in identifiers:
|
|
1323
|
+
block_count=2 cstruct_size=0 is_def=true try:
|
|
1324
|
+
block_count=3 cstruct_size=0 is_def=true if identifier.isdigit():
|
|
1325
|
+
compo c_name=identifier
|
|
1326
|
+
block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (int(
|
|
1327
|
+
compo c_name=cursor
|
|
1328
|
+
block_count=5 cstruct_size=0 is_def=true identifier),))
|
|
1329
|
+
block_count=3 cstruct_size=0 is_def=true else:
|
|
1330
|
+
block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (
|
|
1331
|
+
compo c_name=cursor
|
|
1332
|
+
block_count=5 cstruct_size=0 is_def=true identifier,))
|
|
1333
|
+
block_count=3 cstruct_size=0 is_def=true if cursor.rowcount > 0:
|
|
1334
|
+
compo c_name=cursor
|
|
1335
|
+
block_count=4 cstruct_size=0 is_def=true deleted_count += cursor.rowcount
|
|
1336
|
+
compo c_name=cursor
|
|
1337
|
+
block_count=4 cstruct_size=0 is_def=true print(f' Deleted: {identifier}')
|
|
1338
|
+
block_count=3 cstruct_size=0 is_def=true else:
|
|
1339
|
+
block_count=4 cstruct_size=0 is_def=true errors.append(f'No product found with ID/Name: {identifier}')
|
|
1340
|
+
compo c_name=errors
|
|
1341
|
+
block_count=2 cstruct_size=0 is_def=true except Exception as e:
|
|
1342
|
+
block_count=3 cstruct_size=0 is_def=true errors.append(f'Error deleting {identifier}: {e}')
|
|
1343
|
+
compo c_name=errors
|
|
1344
|
+
block_count=1 cstruct_size=0 is_def=true conn.commit()
|
|
1345
|
+
compo c_name=conn
|
|
1346
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
1347
|
+
compo c_name=conn
|
|
1348
|
+
block_count=1 cstruct_size=0 is_def=true print(f'\nTotal deleted: {deleted_count}')
|
|
1349
|
+
block_count=1 cstruct_size=0 is_def=true if errors:
|
|
1350
|
+
block_count=2 cstruct_size=0 is_def=true print('Errors/Warnings:')
|
|
1351
|
+
block_count=2 cstruct_size=0 is_def=true for err in errors:
|
|
1352
|
+
block_count=3 cstruct_size=0 is_def=true print(f' - {err}')
|
|
1353
|
+
block_count=0 cstruct_size=0 is_def=true def main():
|
|
1354
|
+
block_count=1 cstruct_size=0 is_def=true if not os.getenv('GOOGLE_API_KEY'):
|
|
1355
|
+
compo c_name=os
|
|
1356
|
+
block_count=2 cstruct_size=0 is_def=true print('Error: GOOGLE_API_KEY not found.')
|
|
1357
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1358
|
+
block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
|
|
1359
|
+
compo c_name=os
|
|
1360
|
+
block_count=1 cstruct_size=0 is_def=true if provider == 'serpapi' and not os.getenv('SERPAPI_API_KEY'):
|
|
1361
|
+
compo c_name=os
|
|
1362
|
+
block_count=2 cstruct_size=0 is_def=true print('Error: SERPAPI_API_KEY not found.')
|
|
1363
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1364
|
+
block_count=1 cstruct_size=0 is_def=true elif provider == 'tavily_api' and not os.getenv('TAVILY_API_KEY'):
|
|
1365
|
+
compo c_name=os
|
|
1366
|
+
block_count=2 cstruct_size=0 is_def=true print('Error: TAVILY_API_KEY not found for Tavily search.')
|
|
1367
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1368
|
+
block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
|
|
1369
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
1370
|
+
block_count=1 cstruct_size=0 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
1371
|
+
compo c_name=os
|
|
1372
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Using model: {model_name}')
|
|
1373
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Using Search Provider: {provider}')
|
|
1374
|
+
block_count=1 cstruct_size=0 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
|
|
1375
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
1376
|
+
block_count=2 cstruct_size=0 is_def=true max_retries=10)
|
|
1377
|
+
block_count=1 cstruct_size=0 is_def=true search = get_search_tool_func()
|
|
1378
|
+
block_count=1 cstruct_size=0 is_def=true search_tool = Tool(name='google_search', description=
|
|
1379
|
+
compo c_name=Tool
|
|
1380
|
+
block_count=2 cstruct_size=0 is_def=true 'Search Google for recent results.', func=search.run)
|
|
1381
|
+
block_count=1 cstruct_size=0 is_def=true startup_logs = get_all_agent_logs()
|
|
1382
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Loaded {len(startup_logs)} characters of agent logs.')
|
|
1383
|
+
block_count=1 cstruct_size=0 is_def=true save_tool = SaveProductTool()
|
|
1384
|
+
compo c_name=SaveProductTool
|
|
1385
|
+
block_count=1 cstruct_size=0 is_def=true db_search_tool = SearchProductsTool()
|
|
1386
|
+
compo c_name=SearchProductsTool
|
|
1387
|
+
block_count=1 cstruct_size=0 is_def=true update_tool = UpdatePricesTool()
|
|
1388
|
+
compo c_name=UpdatePricesTool
|
|
1389
|
+
block_count=1 cstruct_size=0 is_def=true similar_tool = FindSimilarProductsTool(agent_logs=startup_logs)
|
|
1390
|
+
compo c_name=FindSimilarProductsTool
|
|
1391
|
+
block_count=1 cstruct_size=0 is_def=true compare_tool = CompareProductsTool()
|
|
1392
|
+
compo c_name=CompareProductsTool
|
|
1393
|
+
block_count=1 cstruct_size=0 is_def=true tools = [search_tool, save_tool, db_search_tool, update_tool,
|
|
1394
|
+
block_count=2 cstruct_size=0 is_def=true similar_tool, compare_tool]
|
|
1395
|
+
block_count=1 cstruct_size=0 is_def=true prompt = ChatPromptTemplate.from_messages([('system',
|
|
1396
|
+
compo c_name=ChatPromptTemplate
|
|
1397
|
+
block_count=2 cstruct_size=0 is_def=true """あなたは、商品の検索、保存、価格更新、類似商品検索、比較表作成を行う有能なアシスタントです。
|
|
1398
|
+
block_count=2 cstruct_size=0 is_def=true ), MessagesPlaceholder(variable_name='chat_history'), ('human',
|
|
1399
|
+
compo c_name=MessagesPlaceholder
|
|
1400
|
+
block_count=2 cstruct_size=0 is_def=true '{input}'), ('placeholder', '{agent_scratchpad}')])
|
|
1401
|
+
block_count=1 cstruct_size=0 is_def=true agent = create_tool_calling_agent(llm, tools, prompt)
|
|
1402
|
+
block_count=1 cstruct_size=0 is_def=true agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True,
|
|
1403
|
+
compo c_name=AgentExecutor
|
|
1404
|
+
block_count=2 cstruct_size=0 is_def=true return_intermediate_steps=True)
|
|
1405
|
+
block_count=1 cstruct_size=0 is_def=true print('Advanced AI Agent initialized.')
|
|
1406
|
+
block_count=1 cstruct_size=0 is_def=true print(
|
|
1407
|
+
block_count=2 cstruct_size=0 is_def=true "Commands: 'list', 'show <ID>', 'update', 'similar', 'delete <ID/Name> ...', 'quit'"
|
|
1408
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
1409
|
+
block_count=1 cstruct_size=0 is_def=true chat_history = []
|
|
1410
|
+
block_count=1 cstruct_size=0 is_def=true while True:
|
|
1411
|
+
block_count=2 cstruct_size=0 is_def=true try:
|
|
1412
|
+
block_count=3 cstruct_size=0 is_def=true try:
|
|
1413
|
+
block_count=4 cstruct_size=0 is_def=true user_input = input('\nEnter command or search query: ')
|
|
1414
|
+
block_count=3 cstruct_size=0 is_def=true except EOFError:
|
|
1415
|
+
block_count=4 cstruct_size=0 is_def=true print('\nEOF detected. Exiting...')
|
|
1416
|
+
block_count=4 cstruct_size=0 is_def=true break
|
|
1417
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() in ['quit', 'exit']:
|
|
1418
|
+
block_count=4 cstruct_size=0 is_def=true break
|
|
1419
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'list':
|
|
1420
|
+
block_count=4 cstruct_size=0 is_def=true display_products()
|
|
1421
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1422
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'update':
|
|
1423
|
+
block_count=4 cstruct_size=0 is_def=true result = update_tool._run()
|
|
1424
|
+
block_count=4 cstruct_size=0 is_def=true print(result)
|
|
1425
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1426
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'similar':
|
|
1427
|
+
block_count=4 cstruct_size=0 is_def=true result = similar_tool._run()
|
|
1428
|
+
block_count=4 cstruct_size=0 is_def=true print(result)
|
|
1429
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1430
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('compare'):
|
|
1431
|
+
block_count=4 cstruct_size=0 is_def=true parts = user_input.split(maxsplit=1)
|
|
1432
|
+
block_count=4 cstruct_size=0 is_def=true query = parts[1] if len(parts) > 1 else ''
|
|
1433
|
+
block_count=4 cstruct_size=0 is_def=true result = compare_tool._run(query)
|
|
1434
|
+
block_count=4 cstruct_size=0 is_def=true print(result)
|
|
1435
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1436
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('search_products '):
|
|
1437
|
+
block_count=4 cstruct_size=0 is_def=true query = user_input[16:].strip()
|
|
1438
|
+
block_count=4 cstruct_size=0 is_def=true if query:
|
|
1439
|
+
block_count=5 cstruct_size=0 is_def=true result = db_search_tool._run(query)
|
|
1440
|
+
block_count=5 cstruct_size=0 is_def=true print(result)
|
|
1441
|
+
block_count=4 cstruct_size=0 is_def=true else:
|
|
1442
|
+
block_count=5 cstruct_size=0 is_def=true print('Usage: search_products <query>')
|
|
1443
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1444
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('show '):
|
|
1445
|
+
block_count=4 cstruct_size=0 is_def=true parts = user_input.split()
|
|
1446
|
+
block_count=4 cstruct_size=0 is_def=true if len(parts) > 1 and parts[1].isdigit():
|
|
1447
|
+
block_count=5 cstruct_size=0 is_def=true show_product_details(int(parts[1]))
|
|
1448
|
+
block_count=4 cstruct_size=0 is_def=true else:
|
|
1449
|
+
block_count=5 cstruct_size=0 is_def=true print('Usage: show <product_id>')
|
|
1450
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1451
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('delete '):
|
|
1452
|
+
block_count=4 cstruct_size=0 is_def=true try:
|
|
1453
|
+
block_count=5 cstruct_size=0 is_def=true parts = shlex.split(user_input)
|
|
1454
|
+
compo c_name=shlex
|
|
1455
|
+
block_count=5 cstruct_size=0 is_def=true if len(parts) > 1:
|
|
1456
|
+
block_count=6 cstruct_size=0 is_def=true identifiers = parts[1:]
|
|
1457
|
+
block_count=6 cstruct_size=0 is_def=true delete_product_records(identifiers)
|
|
1458
|
+
block_count=5 cstruct_size=0 is_def=true else:
|
|
1459
|
+
block_count=6 cstruct_size=0 is_def=true print('Usage: delete <ID/Name> ...')
|
|
1460
|
+
block_count=4 cstruct_size=0 is_def=true except ValueError as e:
|
|
1461
|
+
block_count=5 cstruct_size=0 is_def=true print(f'Error parsing command: {e}')
|
|
1462
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
1463
|
+
block_count=3 cstruct_size=0 is_def=true if user_input:
|
|
1464
|
+
block_count=4 cstruct_size=0 is_def=true print(f'\nProcessing: {user_input}...\n')
|
|
1465
|
+
block_count=4 cstruct_size=0 is_def=true result = agent_executor.invoke({'input': user_input,
|
|
1466
|
+
block_count=5 cstruct_size=0 is_def=true 'chat_history': chat_history})
|
|
1467
|
+
block_count=4 cstruct_size=0 is_def=true chat_history.append(HumanMessage(content=user_input))
|
|
1468
|
+
compo c_name=HumanMessage
|
|
1469
|
+
block_count=4 cstruct_size=0 is_def=true if isinstance(result['output'], str):
|
|
1470
|
+
block_count=5 cstruct_size=0 is_def=true chat_history.append(AIMessage(content=result['output']))
|
|
1471
|
+
compo c_name=AIMessage
|
|
1472
|
+
block_count=4 cstruct_size=0 is_def=true if 'intermediate_steps' in result:
|
|
1473
|
+
block_count=5 cstruct_size=0 is_def=true save_agent_log(user_input, result['intermediate_steps'])
|
|
1474
|
+
block_count=4 cstruct_size=0 is_def=true display_products()
|
|
1475
|
+
block_count=2 cstruct_size=0 is_def=true except KeyboardInterrupt:
|
|
1476
|
+
block_count=3 cstruct_size=0 is_def=true print('\nExiting...')
|
|
1477
|
+
block_count=3 cstruct_size=0 is_def=true break
|
|
1478
|
+
block_count=2 cstruct_size=0 is_def=true except Exception as e:
|
|
1479
|
+
block_count=3 cstruct_size=0 is_def=true print(f'An error occurred: {e}')
|
|
1480
|
+
block_count=0 cstruct_size=0 is_def=true if __name__ == '__main__':
|
|
1481
|
+
block_count=1 cstruct_size=0 is_def=false main()
|
|
1482
|
+
endf of ./test/agent.py
|
|
1483
|
+
./test_script.py
|
|
1484
|
+
|
|
1485
|
+
|python3 lib/del_comment.py ./test_script.py > /tmp/pylint20260323-827-lminf3
|
|
1486
|
+
block_count=0 cstruct_size=0 is_def=false import os
|
|
1487
|
+
block_count=0 cstruct_size=0 is_def=false import sqlite3
|
|
1488
|
+
block_count=0 cstruct_size=0 is_def=false import re
|
|
1489
|
+
block_count=0 cstruct_size=0 is_def=false import json
|
|
1490
|
+
block_count=0 cstruct_size=0 is_def=false import time
|
|
1491
|
+
block_count=0 cstruct_size=0 is_def=false import shlex
|
|
1492
|
+
block_count=0 cstruct_size=0 is_def=false import smtplib
|
|
1493
|
+
block_count=0 cstruct_size=0 is_def=false from email.mime.text import MIMEText
|
|
1494
|
+
compo c_name=email
|
|
1495
|
+
block_count=0 cstruct_size=0 is_def=false from email.mime.multipart import MIMEMultipart
|
|
1496
|
+
compo c_name=email
|
|
1497
|
+
block_count=0 cstruct_size=0 is_def=false from typing import Optional, Type, Any, List, Dict
|
|
1498
|
+
block_count=0 cstruct_size=0 is_def=false import urllib.request
|
|
1499
|
+
compo c_name=urllib
|
|
1500
|
+
block_count=0 cstruct_size=0 is_def=false import urllib.error
|
|
1501
|
+
compo c_name=urllib
|
|
1502
|
+
block_count=0 cstruct_size=0 is_def=false from dotenv import load_dotenv
|
|
1503
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.tools import BaseTool, Tool
|
|
1504
|
+
block_count=0 cstruct_size=0 is_def=false from pydantic import BaseModel, Field, model_validator
|
|
1505
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
|
|
1506
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_community.utilities import SerpAPIWrapper
|
|
1507
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_community.tools.tavily_search import TavilySearchResults
|
|
1508
|
+
block_count=0 cstruct_size=0 is_def=false from googleapiclient.discovery import build
|
|
1509
|
+
compo c_name=googleapiclient
|
|
1510
|
+
block_count=0 cstruct_size=0 is_def=false import asyncio
|
|
1511
|
+
block_count=0 cstruct_size=0 is_def=false from browser_use import Agent, BrowserProfile
|
|
1512
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
|
|
1513
|
+
block_count=0 cstruct_size=0 is_def=false from browser_use.llm.google.chat import ChatGoogle
|
|
1514
|
+
block_count=0 cstruct_size=0 is_def=false from langchain.agents import AgentExecutor, create_tool_calling_agent, create_react_agent
|
|
1515
|
+
compo c_name=langchain
|
|
1516
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, MessagesPlaceholder
|
|
1517
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.output_parsers import JsonOutputParser
|
|
1518
|
+
block_count=0 cstruct_size=0 is_def=false from langchain_core.messages import HumanMessage, AIMessage
|
|
1519
|
+
block_count=0 cstruct_size=0 is_def=false dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
|
|
1520
|
+
compo c_name=os
|
|
1521
|
+
block_count=0 cstruct_size=0 is_def=false load_dotenv(dotenv_path, override=True)
|
|
1522
|
+
block_count=0 cstruct_size=0 is_def=false DB_NAME = 'products.db'
|
|
1523
|
+
block_count=0 cstruct_size=0 is_def=false def init_db():
|
|
1524
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1525
|
+
compo c_name=sqlite3
|
|
1526
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1527
|
+
compo c_name=conn
|
|
1528
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
1529
|
+
compo c_name=cursor
|
|
1530
|
+
block_count=2 cstruct_size=0 is_def=true """
|
|
1531
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
1532
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1533
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN model_number TEXT')
|
|
1534
|
+
compo c_name=cursor
|
|
1535
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
1536
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
1537
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1538
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN release_date TEXT')
|
|
1539
|
+
compo c_name=cursor
|
|
1540
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
1541
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
1542
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1543
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ram TEXT')
|
|
1544
|
+
compo c_name=cursor
|
|
1545
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
1546
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
1547
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1548
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ssd TEXT')
|
|
1549
|
+
compo c_name=cursor
|
|
1550
|
+
block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
|
|
1551
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
1552
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
1553
|
+
compo c_name=cursor
|
|
1554
|
+
block_count=2 cstruct_size=0 is_def=true """
|
|
1555
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
1556
|
+
block_count=1 cstruct_size=0 is_def=true conn.commit()
|
|
1557
|
+
compo c_name=conn
|
|
1558
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
1559
|
+
compo c_name=conn
|
|
1560
|
+
block_count=0 cstruct_size=0 is_def=true init_db()
|
|
1561
|
+
block_count=0 cstruct_size=0 is_def=false class TavilySearchWrapper:
|
|
1562
|
+
class_name=test_script.TavilySearchWrapper
|
|
1563
|
+
base_name=
|
|
1564
|
+
block_count=1 cstruct_size=1 is_def=false def __init__(self):
|
|
1565
|
+
block_count=2 cstruct_size=1 is_def=true self.api_key = os.getenv('TAVILY_API_KEY')
|
|
1566
|
+
compo c_name=self
|
|
1567
|
+
block_count=2 cstruct_size=1 is_def=true if not self.api_key:
|
|
1568
|
+
compo c_name=self
|
|
1569
|
+
block_count=3 cstruct_size=1 is_def=true raise ValueError('TAVILY_API_KEY must be set for Tavily Search.')
|
|
1570
|
+
compo c_name=ValueError
|
|
1571
|
+
block_count=2 cstruct_size=1 is_def=true self.tool = TavilySearchResults(api_key=self.api_key)
|
|
1572
|
+
compo c_name=self
|
|
1573
|
+
compo c_name=TavilySearchResults
|
|
1574
|
+
block_count=1 cstruct_size=1 is_def=true def run(self, query: str) ->str:
|
|
1575
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
1576
|
+
block_count=3 cstruct_size=1 is_def=true results = self.tool.invoke({'query': query})
|
|
1577
|
+
compo c_name=self
|
|
1578
|
+
block_count=3 cstruct_size=1 is_def=true formatted_results = []
|
|
1579
|
+
block_count=3 cstruct_size=1 is_def=true for item in results:
|
|
1580
|
+
block_count=4 cstruct_size=1 is_def=true content = item.get('content')
|
|
1581
|
+
compo c_name=item
|
|
1582
|
+
block_count=4 cstruct_size=1 is_def=true url = item.get('url')
|
|
1583
|
+
compo c_name=item
|
|
1584
|
+
block_count=4 cstruct_size=1 is_def=true formatted_results.append(f'Content: {content}\nURL: {url}\n')
|
|
1585
|
+
block_count=3 cstruct_size=1 is_def=true return '\n'.join(formatted_results
|
|
1586
|
+
block_count=4 cstruct_size=1 is_def=true ) if formatted_results else 'No results found.'
|
|
1587
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
1588
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error during Tavily Search: {e}'
|
|
1589
|
+
block_count=0 cstruct_size=1 is_def=true class BrowserUseSearchWrapper:
|
|
1590
|
+
end of test_script.TavilySearchWrapper
|
|
1591
|
+
class_name=test_script.BrowserUseSearchWrapper
|
|
1592
|
+
base_name=
|
|
1593
|
+
block_count=1 cstruct_size=1 is_def=false def __init__(self):
|
|
1594
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
1595
|
+
compo c_name=os
|
|
1596
|
+
block_count=2 cstruct_size=1 is_def=true self.llm = ChatGoogle(model=model_name, api_key=os.getenv(
|
|
1597
|
+
compo c_name=self
|
|
1598
|
+
compo c_name=ChatGoogle
|
|
1599
|
+
block_count=3 cstruct_size=1 is_def=true 'GOOGLE_API_KEY'))
|
|
1600
|
+
block_count=2 cstruct_size=1 is_def=true user_agent = (
|
|
1601
|
+
block_count=3 cstruct_size=1 is_def=true 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
1602
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
1603
|
+
block_count=2 cstruct_size=1 is_def=true self.browser_profile = BrowserProfile(headless=False, user_agent=
|
|
1604
|
+
compo c_name=self
|
|
1605
|
+
compo c_name=BrowserProfile
|
|
1606
|
+
block_count=3 cstruct_size=1 is_def=true user_agent)
|
|
1607
|
+
block_count=1 cstruct_size=1 is_def=true async def _search_async(self, query: str) ->str:
|
|
1608
|
+
block_count=2 cstruct_size=1 is_def=false task = f"""
|
|
1609
|
+
block_count=2 cstruct_size=1 is_def=false agent = Agent(task=task, llm=self.llm, browser_profile=self.
|
|
1610
|
+
compo c_name=Agent
|
|
1611
|
+
block_count=3 cstruct_size=1 is_def=false browser_profile)
|
|
1612
|
+
block_count=2 cstruct_size=1 is_def=false result = await agent.run()
|
|
1613
|
+
compo c_name=agent
|
|
1614
|
+
block_count=2 cstruct_size=1 is_def=false return result.final_result()
|
|
1615
|
+
compo c_name=result
|
|
1616
|
+
block_count=1 cstruct_size=1 is_def=false def run(self, query: str) ->str:
|
|
1617
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
1618
|
+
block_count=3 cstruct_size=1 is_def=true return asyncio.run(self._search_async(query))
|
|
1619
|
+
compo c_name=asyncio
|
|
1620
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
1621
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error during Browser Use Search: {e}'
|
|
1622
|
+
block_count=0 cstruct_size=1 is_def=true def get_search_tool_func():
|
|
1623
|
+
end of test_script.BrowserUseSearchWrapper
|
|
1624
|
+
block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
|
|
1625
|
+
compo c_name=os
|
|
1626
|
+
block_count=1 cstruct_size=0 is_def=true if provider == 'tavily_api':
|
|
1627
|
+
block_count=2 cstruct_size=0 is_def=true return TavilySearchWrapper()
|
|
1628
|
+
compo c_name=TavilySearchWrapper
|
|
1629
|
+
block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
|
|
1630
|
+
block_count=2 cstruct_size=0 is_def=true return BrowserUseSearchWrapper()
|
|
1631
|
+
compo c_name=BrowserUseSearchWrapper
|
|
1632
|
+
block_count=1 cstruct_size=0 is_def=true else:
|
|
1633
|
+
block_count=2 cstruct_size=0 is_def=true return SerpAPIWrapper()
|
|
1634
|
+
compo c_name=SerpAPIWrapper
|
|
1635
|
+
block_count=0 cstruct_size=0 is_def=true def get_all_products():
|
|
1636
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1637
|
+
compo c_name=sqlite3
|
|
1638
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1639
|
+
compo c_name=conn
|
|
1640
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
1641
|
+
compo c_name=cursor
|
|
1642
|
+
block_count=2 cstruct_size=0 is_def=true 'SELECT id, name, store, price, url, description, model_number, release_date, ram, ssd, updated_at FROM products'
|
|
1643
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
1644
|
+
block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
|
|
1645
|
+
compo c_name=cursor
|
|
1646
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
1647
|
+
compo c_name=conn
|
|
1648
|
+
block_count=1 cstruct_size=0 is_def=true products = []
|
|
1649
|
+
block_count=1 cstruct_size=0 is_def=true for r in rows:
|
|
1650
|
+
block_count=2 cstruct_size=0 is_def=true products.append({'id': r[0], 'name': r[1], 'store': r[2], 'price':
|
|
1651
|
+
compo c_name=products
|
|
1652
|
+
block_count=3 cstruct_size=0 is_def=true r[3], 'url': r[4], 'description': r[5], 'model_number': r[6] if
|
|
1653
|
+
block_count=3 cstruct_size=0 is_def=true len(r) > 6 else '', 'release_date': r[7] if len(r) > 7 else '',
|
|
1654
|
+
block_count=3 cstruct_size=0 is_def=true 'ram': r[8] if len(r) > 8 else '', 'ssd': r[9] if len(r) > 9 else
|
|
1655
|
+
block_count=3 cstruct_size=0 is_def=true '', 'updated_at': r[10] if len(r) > 10 else ''})
|
|
1656
|
+
block_count=1 cstruct_size=0 is_def=true return products
|
|
1657
|
+
block_count=0 cstruct_size=0 is_def=true def parse_price_val(p_str):
|
|
1658
|
+
block_count=1 cstruct_size=0 is_def=true if not p_str:
|
|
1659
|
+
block_count=2 cstruct_size=0 is_def=true return float('inf')
|
|
1660
|
+
block_count=1 cstruct_size=0 is_def=true s = str(p_str).replace(',', '')
|
|
1661
|
+
block_count=1 cstruct_size=0 is_def=true match_man = re.search('(\\d+(\\.\\d+)?)万', s)
|
|
1662
|
+
compo c_name=re
|
|
1663
|
+
block_count=1 cstruct_size=0 is_def=true if match_man:
|
|
1664
|
+
block_count=2 cstruct_size=0 is_def=true try:
|
|
1665
|
+
block_count=3 cstruct_size=0 is_def=true val = float(match_man.group(1)) * 10000
|
|
1666
|
+
block_count=3 cstruct_size=0 is_def=true return int(val)
|
|
1667
|
+
block_count=2 cstruct_size=0 is_def=true except:
|
|
1668
|
+
block_count=3 cstruct_size=0 is_def=true pass
|
|
1669
|
+
block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', s)
|
|
1670
|
+
compo c_name=re
|
|
1671
|
+
block_count=1 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
|
|
1672
|
+
block_count=0 cstruct_size=0 is_def=true def extract_alphanumeric(s: str) ->str:
|
|
1673
|
+
block_count=1 cstruct_size=0 is_def=true """Extracts only alphanumeric characters from a string and converts to lowercase for robust comparison."""
|
|
1674
|
+
block_count=1 cstruct_size=0 is_def=true if not s:
|
|
1675
|
+
block_count=2 cstruct_size=0 is_def=true return ''
|
|
1676
|
+
block_count=1 cstruct_size=0 is_def=true return re.sub('[^a-zA-Z0-9]', '', str(s)).lower()
|
|
1677
|
+
compo c_name=re
|
|
1678
|
+
block_count=0 cstruct_size=0 is_def=true def parse_date_val(d_str: str) ->str:
|
|
1679
|
+
block_count=1 cstruct_size=0 is_def=true """
|
|
1680
|
+
block_count=1 cstruct_size=0 is_def=true if not d_str:
|
|
1681
|
+
block_count=2 cstruct_size=0 is_def=true return ''
|
|
1682
|
+
block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(d_str))
|
|
1683
|
+
compo c_name=re
|
|
1684
|
+
block_count=1 cstruct_size=0 is_def=true if len(nums) >= 2:
|
|
1685
|
+
block_count=2 cstruct_size=0 is_def=true year = nums[0]
|
|
1686
|
+
block_count=2 cstruct_size=0 is_def=true month = nums[1].zfill(2)
|
|
1687
|
+
block_count=2 cstruct_size=0 is_def=true return f'{year}{month}'
|
|
1688
|
+
block_count=1 cstruct_size=0 is_def=true elif len(nums) == 1:
|
|
1689
|
+
block_count=2 cstruct_size=0 is_def=true return nums[0]
|
|
1690
|
+
block_count=1 cstruct_size=0 is_def=true else:
|
|
1691
|
+
block_count=2 cstruct_size=0 is_def=true return extract_alphanumeric(d_str)
|
|
1692
|
+
block_count=0 cstruct_size=0 is_def=true def is_similar_model(m1: str, m2: str) ->bool:
|
|
1693
|
+
block_count=1 cstruct_size=0 is_def=true """
|
|
1694
|
+
block_count=1 cstruct_size=0 is_def=true am1 = extract_alphanumeric(m1)
|
|
1695
|
+
block_count=1 cstruct_size=0 is_def=true am2 = extract_alphanumeric(m2)
|
|
1696
|
+
block_count=1 cstruct_size=0 is_def=true if not am1 and not am2:
|
|
1697
|
+
block_count=2 cstruct_size=0 is_def=true return True
|
|
1698
|
+
block_count=1 cstruct_size=0 is_def=true if not am1 or not am2:
|
|
1699
|
+
block_count=2 cstruct_size=0 is_def=true return False
|
|
1700
|
+
block_count=1 cstruct_size=0 is_def=true return am1 in am2 or am2 in am1
|
|
1701
|
+
block_count=0 cstruct_size=0 is_def=true def save_agent_log(query, steps):
|
|
1702
|
+
block_count=1 cstruct_size=0 is_def=true """Saves the agent's scratchpad (intermediate steps) to the database."""
|
|
1703
|
+
block_count=1 cstruct_size=0 is_def=true if not steps:
|
|
1704
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1705
|
+
block_count=1 cstruct_size=0 is_def=true log_content = []
|
|
1706
|
+
block_count=1 cstruct_size=0 is_def=true for action, observation in steps:
|
|
1707
|
+
block_count=2 cstruct_size=0 is_def=true if isinstance(action, list):
|
|
1708
|
+
block_count=3 cstruct_size=0 is_def=true for a in action:
|
|
1709
|
+
block_count=4 cstruct_size=0 is_def=true log_content.append(f'Tool: {a.tool}')
|
|
1710
|
+
block_count=4 cstruct_size=0 is_def=true log_content.append(f'Input: {a.tool_input}')
|
|
1711
|
+
block_count=4 cstruct_size=0 is_def=true log_content.append(f'Log: {a.log}')
|
|
1712
|
+
block_count=2 cstruct_size=0 is_def=true else:
|
|
1713
|
+
block_count=3 cstruct_size=0 is_def=true log_content.append(f'Tool: {action.tool}')
|
|
1714
|
+
block_count=3 cstruct_size=0 is_def=true log_content.append(f'Input: {action.tool_input}')
|
|
1715
|
+
block_count=3 cstruct_size=0 is_def=true log_content.append(f'Log: {action.log}')
|
|
1716
|
+
block_count=2 cstruct_size=0 is_def=true log_content.append(f'Observation: {observation}')
|
|
1717
|
+
block_count=2 cstruct_size=0 is_def=true log_content.append('-' * 20)
|
|
1718
|
+
block_count=1 cstruct_size=0 is_def=true scratchpad_text = '\n'.join(log_content)
|
|
1719
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1720
|
+
block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1721
|
+
compo c_name=sqlite3
|
|
1722
|
+
block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1723
|
+
compo c_name=conn
|
|
1724
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute(
|
|
1725
|
+
compo c_name=cursor
|
|
1726
|
+
block_count=3 cstruct_size=0 is_def=true 'INSERT INTO agent_logs (query, scratchpad) VALUES (?, ?)', (
|
|
1727
|
+
block_count=3 cstruct_size=0 is_def=true query, scratchpad_text))
|
|
1728
|
+
block_count=2 cstruct_size=0 is_def=true conn.commit()
|
|
1729
|
+
compo c_name=conn
|
|
1730
|
+
block_count=2 cstruct_size=0 is_def=true conn.close()
|
|
1731
|
+
compo c_name=conn
|
|
1732
|
+
block_count=2 cstruct_size=0 is_def=true print(f' [Log saved to database]')
|
|
1733
|
+
block_count=1 cstruct_size=0 is_def=true except Exception as e:
|
|
1734
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Error saving log: {e}')
|
|
1735
|
+
block_count=0 cstruct_size=0 is_def=true def get_all_agent_logs():
|
|
1736
|
+
block_count=1 cstruct_size=0 is_def=true """Fetches all agent logs from the database."""
|
|
1737
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1738
|
+
block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1739
|
+
compo c_name=sqlite3
|
|
1740
|
+
block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
1741
|
+
compo c_name=conn
|
|
1742
|
+
block_count=2 cstruct_size=0 is_def=true cursor.execute(
|
|
1743
|
+
compo c_name=cursor
|
|
1744
|
+
block_count=3 cstruct_size=0 is_def=true 'SELECT query, scratchpad FROM agent_logs ORDER BY timestamp DESC')
|
|
1745
|
+
block_count=2 cstruct_size=0 is_def=true rows = cursor.fetchall()
|
|
1746
|
+
compo c_name=cursor
|
|
1747
|
+
block_count=2 cstruct_size=0 is_def=true conn.close()
|
|
1748
|
+
compo c_name=conn
|
|
1749
|
+
block_count=2 cstruct_size=0 is_def=true logs = []
|
|
1750
|
+
block_count=2 cstruct_size=0 is_def=true for r in rows:
|
|
1751
|
+
block_count=3 cstruct_size=0 is_def=true logs.append(f'Query: {r[0]}\nLog:\n{r[1]}\n')
|
|
1752
|
+
compo c_name=logs
|
|
1753
|
+
block_count=2 cstruct_size=0 is_def=true return '\n'.join(logs)
|
|
1754
|
+
block_count=1 cstruct_size=0 is_def=true except Exception as e:
|
|
1755
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Error fetching logs: {e}')
|
|
1756
|
+
block_count=2 cstruct_size=0 is_def=true return ''
|
|
1757
|
+
block_count=0 cstruct_size=0 is_def=true def send_email_notification(subject: str, body: str):
|
|
1758
|
+
block_count=1 cstruct_size=0 is_def=true """Sends an email notification."""
|
|
1759
|
+
block_count=1 cstruct_size=0 is_def=true smtp_server = os.getenv('EMAIL_SMTP_SERVER')
|
|
1760
|
+
compo c_name=os
|
|
1761
|
+
block_count=1 cstruct_size=0 is_def=true smtp_port = os.getenv('EMAIL_SMTP_PORT')
|
|
1762
|
+
compo c_name=os
|
|
1763
|
+
block_count=1 cstruct_size=0 is_def=true sender_email = os.getenv('EMAIL_SENDER_ADDRESS')
|
|
1764
|
+
compo c_name=os
|
|
1765
|
+
block_count=1 cstruct_size=0 is_def=true sender_password = os.getenv('EMAIL_SENDER_PASSWORD')
|
|
1766
|
+
compo c_name=os
|
|
1767
|
+
block_count=1 cstruct_size=0 is_def=true receiver_email = os.getenv('EMAIL_RECEIVER_ADDRESS')
|
|
1768
|
+
compo c_name=os
|
|
1769
|
+
block_count=1 cstruct_size=0 is_def=true if not all([smtp_server, smtp_port, sender_email, receiver_email]):
|
|
1770
|
+
block_count=2 cstruct_size=0 is_def=true print(
|
|
1771
|
+
block_count=3 cstruct_size=0 is_def=true 'Email configuration missing (Server, Port, Sender, Receiver). Skipping notification.'
|
|
1772
|
+
block_count=3 cstruct_size=0 is_def=true )
|
|
1773
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
1774
|
+
block_count=1 cstruct_size=0 is_def=true try:
|
|
1775
|
+
block_count=2 cstruct_size=0 is_def=true msg = MIMEMultipart()
|
|
1776
|
+
compo c_name=MIMEMultipart
|
|
1777
|
+
block_count=2 cstruct_size=0 is_def=true msg['From'] = sender_email
|
|
1778
|
+
block_count=2 cstruct_size=0 is_def=true msg['To'] = receiver_email
|
|
1779
|
+
block_count=2 cstruct_size=0 is_def=true msg['Subject'] = subject
|
|
1780
|
+
block_count=2 cstruct_size=0 is_def=true msg.attach(MIMEText(body, 'plain'))
|
|
1781
|
+
compo c_name=msg
|
|
1782
|
+
compo c_name=MIMEText
|
|
1783
|
+
block_count=2 cstruct_size=0 is_def=true server = smtplib.SMTP(smtp_server, int(smtp_port))
|
|
1784
|
+
compo c_name=SMTP
|
|
1785
|
+
block_count=2 cstruct_size=0 is_def=true server.starttls()
|
|
1786
|
+
compo c_name=server
|
|
1787
|
+
block_count=2 cstruct_size=0 is_def=true if sender_password:
|
|
1788
|
+
block_count=3 cstruct_size=0 is_def=true server.login(sender_email, sender_password)
|
|
1789
|
+
compo c_name=server
|
|
1790
|
+
block_count=2 cstruct_size=0 is_def=true server.send_message(msg)
|
|
1791
|
+
compo c_name=server
|
|
1792
|
+
block_count=2 cstruct_size=0 is_def=true server.quit()
|
|
1793
|
+
compo c_name=server
|
|
1794
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Email notification sent: {subject}')
|
|
1795
|
+
block_count=1 cstruct_size=0 is_def=true except Exception as e:
|
|
1796
|
+
block_count=2 cstruct_size=0 is_def=true print(f'Failed to send email: {e}')
|
|
1797
|
+
block_count=0 cstruct_size=0 is_def=true class SaveProductInput(BaseModel):
|
|
1798
|
+
class_name=test_script.SaveProductInput
|
|
1799
|
+
base_name=BaseModel
|
|
1800
|
+
block_count=1 cstruct_size=1 is_def=false name: str = Field(description='Name of the product')
|
|
1801
|
+
compo c_name=Field
|
|
1802
|
+
block_count=1 cstruct_size=1 is_def=false store: str = Field(description='Name of the store selling the product')
|
|
1803
|
+
compo c_name=Field
|
|
1804
|
+
block_count=1 cstruct_size=1 is_def=false price: str = Field(description='Price of the product')
|
|
1805
|
+
compo c_name=Field
|
|
1806
|
+
block_count=1 cstruct_size=1 is_def=false url: Optional[str] = Field(description='URL of the product page',
|
|
1807
|
+
compo c_name=Field
|
|
1808
|
+
block_count=2 cstruct_size=1 is_def=false default='')
|
|
1809
|
+
block_count=1 cstruct_size=1 is_def=false description: Optional[str] = Field(description=
|
|
1810
|
+
compo c_name=Field
|
|
1811
|
+
block_count=2 cstruct_size=1 is_def=false 'Brief description of the product', default='')
|
|
1812
|
+
block_count=1 cstruct_size=1 is_def=false model_number: Optional[str] = Field(description=
|
|
1813
|
+
compo c_name=Field
|
|
1814
|
+
block_count=2 cstruct_size=1 is_def=false 'Model number (型番) of the product', default='')
|
|
1815
|
+
block_count=1 cstruct_size=1 is_def=false release_date: Optional[str] = Field(description=
|
|
1816
|
+
compo c_name=Field
|
|
1817
|
+
block_count=2 cstruct_size=1 is_def=false 'Release date (発売日) of the product', default='')
|
|
1818
|
+
block_count=1 cstruct_size=1 is_def=false ram: Optional[str] = Field(description='RAM size', default='')
|
|
1819
|
+
compo c_name=Field
|
|
1820
|
+
block_count=1 cstruct_size=1 is_def=false ssd: Optional[str] = Field(description='SSD size', default='')
|
|
1821
|
+
compo c_name=Field
|
|
1822
|
+
block_count=1 cstruct_size=1 is_def=false @model_validator(mode='before')
|
|
1823
|
+
block_count=1 cstruct_size=1 is_def=false @classmethod
|
|
1824
|
+
block_count=1 cstruct_size=1 is_def=false def parse_json_input(cls, data: Any) ->Any:
|
|
1825
|
+
block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict):
|
|
1826
|
+
block_count=3 cstruct_size=1 is_def=true if 'name' in data and ('store' not in data or 'price' not in data):
|
|
1827
|
+
block_count=4 cstruct_size=1 is_def=true name_val = data['name']
|
|
1828
|
+
block_count=4 cstruct_size=1 is_def=true if isinstance(name_val, str) and name_val.strip().startswith(
|
|
1829
|
+
block_count=5 cstruct_size=1 is_def=true '{') and name_val.strip().endswith('}'):
|
|
1830
|
+
block_count=5 cstruct_size=1 is_def=true try:
|
|
1831
|
+
block_count=6 cstruct_size=1 is_def=true parsed = json.loads(name_val)
|
|
1832
|
+
compo c_name=json
|
|
1833
|
+
block_count=6 cstruct_size=1 is_def=true if isinstance(parsed, dict):
|
|
1834
|
+
block_count=7 cstruct_size=1 is_def=true data = parsed
|
|
1835
|
+
block_count=5 cstruct_size=1 is_def=true except json.JSONDecodeError:
|
|
1836
|
+
block_count=6 cstruct_size=1 is_def=true pass
|
|
1837
|
+
block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict) and 'price' in data:
|
|
1838
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(data['price'], (int, float)):
|
|
1839
|
+
block_count=4 cstruct_size=1 is_def=true data['price'] = str(data['price'])
|
|
1840
|
+
block_count=2 cstruct_size=1 is_def=true return data
|
|
1841
|
+
block_count=0 cstruct_size=1 is_def=true class SaveProductTool(BaseTool):
|
|
1842
|
+
end of test_script.SaveProductInput
|
|
1843
|
+
class_name=test_script.SaveProductTool
|
|
1844
|
+
base_name=BaseTool
|
|
1845
|
+
block_count=1 cstruct_size=1 is_def=false name = 'save_product'
|
|
1846
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
1847
|
+
block_count=2 cstruct_size=1 is_def=false 'Saves product information (name, store, price, url, description, model_number, release_date, ram, ssd) to the database.'
|
|
1848
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
1849
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SaveProductInput
|
|
1850
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, name: str, store: str=None, price: str=None, url: str='',
|
|
1851
|
+
block_count=2 cstruct_size=1 is_def=true description: str='', model_number: str='', release_date: str='',
|
|
1852
|
+
block_count=2 cstruct_size=1 is_def=true ram: str='', ssd: str='', **kwargs):
|
|
1853
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
1854
|
+
block_count=3 cstruct_size=1 is_def=true parsed_data = {}
|
|
1855
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(name, dict):
|
|
1856
|
+
block_count=4 cstruct_size=1 is_def=true parsed_data = name
|
|
1857
|
+
block_count=3 cstruct_size=1 is_def=true elif isinstance(name, str) and name.strip().startswith('{'
|
|
1858
|
+
compo c_name=name
|
|
1859
|
+
block_count=4 cstruct_size=1 is_def=true ) and name.strip().endswith('}'):
|
|
1860
|
+
compo c_name=name
|
|
1861
|
+
block_count=4 cstruct_size=1 is_def=true try:
|
|
1862
|
+
block_count=5 cstruct_size=1 is_def=true parsed_data = json.loads(name)
|
|
1863
|
+
compo c_name=json
|
|
1864
|
+
block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError:
|
|
1865
|
+
block_count=5 cstruct_size=1 is_def=true pass
|
|
1866
|
+
block_count=3 cstruct_size=1 is_def=true if parsed_data:
|
|
1867
|
+
block_count=4 cstruct_size=1 is_def=true if 'name' in parsed_data:
|
|
1868
|
+
block_count=5 cstruct_size=1 is_def=true name = parsed_data['name']
|
|
1869
|
+
block_count=4 cstruct_size=1 is_def=true if store is None:
|
|
1870
|
+
block_count=5 cstruct_size=1 is_def=true store = parsed_data.get('store')
|
|
1871
|
+
block_count=4 cstruct_size=1 is_def=true if price is None:
|
|
1872
|
+
block_count=5 cstruct_size=1 is_def=true price = parsed_data.get('price')
|
|
1873
|
+
block_count=4 cstruct_size=1 is_def=true if not url:
|
|
1874
|
+
block_count=5 cstruct_size=1 is_def=true url = parsed_data.get('url', '')
|
|
1875
|
+
block_count=4 cstruct_size=1 is_def=true if not description:
|
|
1876
|
+
block_count=5 cstruct_size=1 is_def=true description = parsed_data.get('description', '')
|
|
1877
|
+
block_count=4 cstruct_size=1 is_def=true if not model_number:
|
|
1878
|
+
block_count=5 cstruct_size=1 is_def=true model_number = parsed_data.get('model_number', '')
|
|
1879
|
+
block_count=4 cstruct_size=1 is_def=true if not release_date:
|
|
1880
|
+
block_count=5 cstruct_size=1 is_def=true release_date = parsed_data.get('release_date', '')
|
|
1881
|
+
block_count=4 cstruct_size=1 is_def=true if not ram:
|
|
1882
|
+
block_count=5 cstruct_size=1 is_def=true ram = parsed_data.get('ram', '')
|
|
1883
|
+
block_count=4 cstruct_size=1 is_def=true if not ssd:
|
|
1884
|
+
block_count=5 cstruct_size=1 is_def=true ssd = parsed_data.get('ssd', '')
|
|
1885
|
+
block_count=3 cstruct_size=1 is_def=true if store is None:
|
|
1886
|
+
block_count=4 cstruct_size=1 is_def=true store = kwargs.get('store')
|
|
1887
|
+
compo c_name=kwargs
|
|
1888
|
+
block_count=3 cstruct_size=1 is_def=true if price is None:
|
|
1889
|
+
block_count=4 cstruct_size=1 is_def=true price = kwargs.get('price')
|
|
1890
|
+
compo c_name=kwargs
|
|
1891
|
+
block_count=3 cstruct_size=1 is_def=true if not model_number:
|
|
1892
|
+
block_count=4 cstruct_size=1 is_def=true model_number = kwargs.get('model_number', '')
|
|
1893
|
+
compo c_name=kwargs
|
|
1894
|
+
block_count=3 cstruct_size=1 is_def=true if not release_date:
|
|
1895
|
+
block_count=4 cstruct_size=1 is_def=true release_date = kwargs.get('release_date', '')
|
|
1896
|
+
compo c_name=kwargs
|
|
1897
|
+
block_count=3 cstruct_size=1 is_def=true if not ram:
|
|
1898
|
+
block_count=4 cstruct_size=1 is_def=true ram = kwargs.get('ram', '')
|
|
1899
|
+
compo c_name=kwargs
|
|
1900
|
+
block_count=3 cstruct_size=1 is_def=true if not ssd:
|
|
1901
|
+
block_count=4 cstruct_size=1 is_def=true ssd = kwargs.get('ssd', '')
|
|
1902
|
+
compo c_name=kwargs
|
|
1903
|
+
block_count=3 cstruct_size=1 is_def=true if not name or not store or not price:
|
|
1904
|
+
block_count=4 cstruct_size=1 is_def=true return (
|
|
1905
|
+
block_count=5 cstruct_size=1 is_def=true f'Error: Required arguments missing. Name: {name}, Store: {store}, Price: {price}'
|
|
1906
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1907
|
+
block_count=3 cstruct_size=1 is_def=true if not url or not description:
|
|
1908
|
+
block_count=4 cstruct_size=1 is_def=true return (
|
|
1909
|
+
block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL or description is missing. URL: '{url}', Description: '{description}'"
|
|
1910
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1911
|
+
block_count=3 cstruct_size=1 is_def=true if not url.startswith('http://') and not url.startswith('https://'
|
|
1912
|
+
compo c_name=url
|
|
1913
|
+
block_count=4 cstruct_size=1 is_def=true ):
|
|
1914
|
+
block_count=4 cstruct_size=1 is_def=true return (
|
|
1915
|
+
block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL must start with 'http://' or 'https://'. URL: '{url}'"
|
|
1916
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1917
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(price, (int, float)):
|
|
1918
|
+
block_count=4 cstruct_size=1 is_def=true price = str(price)
|
|
1919
|
+
block_count=3 cstruct_size=1 is_def=true price_val = parse_price_val(price)
|
|
1920
|
+
block_count=3 cstruct_size=1 is_def=true if price_val == float('inf'):
|
|
1921
|
+
block_count=4 cstruct_size=1 is_def=true if not re.search('\\d', str(price)):
|
|
1922
|
+
compo c_name=re
|
|
1923
|
+
block_count=5 cstruct_size=1 is_def=true return (
|
|
1924
|
+
block_count=6 cstruct_size=1 is_def=true f"Skipped saving product '{name}': Price info is missing or invalid ('{price}')."
|
|
1925
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
1926
|
+
block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
1927
|
+
compo c_name=sqlite3
|
|
1928
|
+
block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
|
|
1929
|
+
compo c_name=conn
|
|
1930
|
+
block_count=3 cstruct_size=1 is_def=true cursor.execute(
|
|
1931
|
+
compo c_name=cursor
|
|
1932
|
+
block_count=4 cstruct_size=1 is_def=true 'SELECT id, store, price, url FROM products WHERE name = ?',
|
|
1933
|
+
block_count=4 cstruct_size=1 is_def=true (name,))
|
|
1934
|
+
block_count=3 cstruct_size=1 is_def=true rows = cursor.fetchall()
|
|
1935
|
+
compo c_name=cursor
|
|
1936
|
+
block_count=3 cstruct_size=1 is_def=true items = []
|
|
1937
|
+
block_count=3 cstruct_size=1 is_def=true for r in rows:
|
|
1938
|
+
block_count=4 cstruct_size=1 is_def=true items.append({'id': r[0], 'store': r[1], 'price_str': r[2],
|
|
1939
|
+
compo c_name=items
|
|
1940
|
+
block_count=5 cstruct_size=1 is_def=true 'price_val': parse_price_val(r[2]), 'url': r[3]})
|
|
1941
|
+
block_count=3 cstruct_size=1 is_def=true new_price_val = parse_price_val(price)
|
|
1942
|
+
block_count=3 cstruct_size=1 is_def=true msg = ''
|
|
1943
|
+
block_count=3 cstruct_size=1 is_def=true items.sort(key=lambda x: x['price_val'])
|
|
1944
|
+
compo c_name=items
|
|
1945
|
+
block_count=3 cstruct_size=1 is_def=true current_cheapest = items[0] if items else None
|
|
1946
|
+
block_count=3 cstruct_size=1 is_def=true should_save = False
|
|
1947
|
+
block_count=3 cstruct_size=1 is_def=true should_update = False
|
|
1948
|
+
block_count=3 cstruct_size=1 is_def=true if not current_cheapest:
|
|
1949
|
+
block_count=4 cstruct_size=1 is_def=true should_save = True
|
|
1950
|
+
block_count=3 cstruct_size=1 is_def=true elif new_price_val < current_cheapest['price_val']:
|
|
1951
|
+
block_count=4 cstruct_size=1 is_def=true should_save = True
|
|
1952
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (name,))
|
|
1953
|
+
compo c_name=cursor
|
|
1954
|
+
block_count=4 cstruct_size=1 is_def=true msg_prefix = (
|
|
1955
|
+
block_count=5 cstruct_size=1 is_def=true f"Found cheaper price! Updated {name} from {current_cheapest['store']} ({current_cheapest['price_str']}) to {store} ({price})."
|
|
1956
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1957
|
+
block_count=3 cstruct_size=1 is_def=true elif new_price_val == current_cheapest['price_val']:
|
|
1958
|
+
block_count=4 cstruct_size=1 is_def=true if store == current_cheapest['store']:
|
|
1959
|
+
block_count=5 cstruct_size=1 is_def=true should_update = True
|
|
1960
|
+
block_count=5 cstruct_size=1 is_def=true msg_prefix = f'Updated info for {name} at {store}.'
|
|
1961
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
1962
|
+
block_count=5 cstruct_size=1 is_def=true msg = (
|
|
1963
|
+
block_count=6 cstruct_size=1 is_def=true f"Product {name} exists with same price at {current_cheapest['store']}. Keeping existing."
|
|
1964
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
1965
|
+
block_count=3 cstruct_size=1 is_def=true elif store == current_cheapest['store']:
|
|
1966
|
+
block_count=4 cstruct_size=1 is_def=true should_update = True
|
|
1967
|
+
block_count=4 cstruct_size=1 is_def=true msg_prefix = (
|
|
1968
|
+
block_count=5 cstruct_size=1 is_def=true f"Price increased for {name} at {store}: {current_cheapest['price_str']} -> {price}."
|
|
1969
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1970
|
+
block_count=3 cstruct_size=1 is_def=true else:
|
|
1971
|
+
block_count=4 cstruct_size=1 is_def=true msg = (
|
|
1972
|
+
block_count=5 cstruct_size=1 is_def=true f"Product {name} exists cheaper at {current_cheapest['store']} ({current_cheapest['price_str']}). Ignoring {store} ({price})."
|
|
1973
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
1974
|
+
block_count=3 cstruct_size=1 is_def=true if should_save:
|
|
1975
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute(
|
|
1976
|
+
compo c_name=cursor
|
|
1977
|
+
block_count=5 cstruct_size=1 is_def=true """
|
|
1978
|
+
block_count=5 cstruct_size=1 is_def=true , (name, store, price, url, description, model_number,
|
|
1979
|
+
block_count=5 cstruct_size=1 is_def=true release_date, ram, ssd))
|
|
1980
|
+
block_count=4 cstruct_size=1 is_def=true if not msg:
|
|
1981
|
+
block_count=5 cstruct_size=1 is_def=true msg = f'Saved product: {name} from {store} for {price}.'
|
|
1982
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
1983
|
+
block_count=5 cstruct_size=1 is_def=true msg = msg_prefix
|
|
1984
|
+
block_count=4 cstruct_size=1 is_def=true email_subject = f'Product Saved: {name}'
|
|
1985
|
+
block_count=4 cstruct_size=1 is_def=true email_body = f"""Action: Saved (New or Cheaper)
|
|
1986
|
+
block_count=4 cstruct_size=1 is_def=true send_email_notification(email_subject, email_body)
|
|
1987
|
+
block_count=3 cstruct_size=1 is_def=true if should_update:
|
|
1988
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute(
|
|
1989
|
+
compo c_name=cursor
|
|
1990
|
+
block_count=5 cstruct_size=1 is_def=true 'SELECT price, url, description, model_number, release_date, ram, ssd FROM products WHERE id = ?'
|
|
1991
|
+
block_count=5 cstruct_size=1 is_def=true , (current_cheapest['id'],))
|
|
1992
|
+
block_count=4 cstruct_size=1 is_def=true curr_row = cursor.fetchone()
|
|
1993
|
+
compo c_name=cursor
|
|
1994
|
+
block_count=4 cstruct_size=1 is_def=true curr_price_str = curr_row[0]
|
|
1995
|
+
block_count=4 cstruct_size=1 is_def=true curr_url = curr_row[1]
|
|
1996
|
+
block_count=4 cstruct_size=1 is_def=true curr_desc = curr_row[2]
|
|
1997
|
+
block_count=4 cstruct_size=1 is_def=true curr_model = curr_row[3]
|
|
1998
|
+
block_count=4 cstruct_size=1 is_def=true curr_release = curr_row[4]
|
|
1999
|
+
block_count=4 cstruct_size=1 is_def=true curr_ram = curr_row[5] if len(curr_row) > 5 else ''
|
|
2000
|
+
block_count=4 cstruct_size=1 is_def=true curr_ssd = curr_row[6] if len(curr_row) > 6 else ''
|
|
2001
|
+
block_count=4 cstruct_size=1 is_def=true final_model = model_number if model_number else curr_model
|
|
2002
|
+
block_count=4 cstruct_size=1 is_def=true final_release = release_date if release_date else curr_release
|
|
2003
|
+
block_count=4 cstruct_size=1 is_def=true final_ram = ram if ram else curr_ram
|
|
2004
|
+
block_count=4 cstruct_size=1 is_def=true final_ssd = ssd if ssd else curr_ssd
|
|
2005
|
+
block_count=4 cstruct_size=1 is_def=true if (price != curr_price_str or url != curr_url or
|
|
2006
|
+
block_count=5 cstruct_size=1 is_def=true description != curr_desc or final_model != curr_model or
|
|
2007
|
+
block_count=5 cstruct_size=1 is_def=true final_release != curr_release or final_ram != curr_ram or
|
|
2008
|
+
block_count=5 cstruct_size=1 is_def=true final_ssd != curr_ssd):
|
|
2009
|
+
block_count=5 cstruct_size=1 is_def=true cursor.execute(
|
|
2010
|
+
compo c_name=cursor
|
|
2011
|
+
block_count=6 cstruct_size=1 is_def=true """
|
|
2012
|
+
block_count=6 cstruct_size=1 is_def=true , (price, url, description, final_model,
|
|
2013
|
+
block_count=6 cstruct_size=1 is_def=true final_release, final_ram, final_ssd, store,
|
|
2014
|
+
block_count=6 cstruct_size=1 is_def=true current_cheapest['id']))
|
|
2015
|
+
block_count=5 cstruct_size=1 is_def=true if not msg:
|
|
2016
|
+
block_count=6 cstruct_size=1 is_def=true msg = f'Updated product {name} info.'
|
|
2017
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2018
|
+
block_count=6 cstruct_size=1 is_def=true msg = msg_prefix
|
|
2019
|
+
block_count=5 cstruct_size=1 is_def=true email_subject = f'Product Updated: {name}'
|
|
2020
|
+
block_count=5 cstruct_size=1 is_def=true email_body = f"""Action: Updated Info
|
|
2021
|
+
block_count=5 cstruct_size=1 is_def=true send_email_notification(email_subject, email_body)
|
|
2022
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
2023
|
+
block_count=5 cstruct_size=1 is_def=true msg = f'No changes for {name} at {store}.'
|
|
2024
|
+
block_count=3 cstruct_size=1 is_def=true if should_save or should_update:
|
|
2025
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute('SELECT id, price FROM products WHERE name = ?',
|
|
2026
|
+
compo c_name=cursor
|
|
2027
|
+
block_count=5 cstruct_size=1 is_def=true (name,))
|
|
2028
|
+
block_count=4 cstruct_size=1 is_def=true rows = cursor.fetchall()
|
|
2029
|
+
compo c_name=cursor
|
|
2030
|
+
block_count=4 cstruct_size=1 is_def=true if len(rows) > 1:
|
|
2031
|
+
block_count=5 cstruct_size=1 is_def=true rows_parsed = []
|
|
2032
|
+
block_count=5 cstruct_size=1 is_def=true for r in rows:
|
|
2033
|
+
block_count=6 cstruct_size=1 is_def=true rows_parsed.append({'id': r[0], 'val':
|
|
2034
|
+
block_count=7 cstruct_size=1 is_def=true parse_price_val(r[1])})
|
|
2035
|
+
block_count=5 cstruct_size=1 is_def=true rows_parsed.sort(key=lambda x: x['val'])
|
|
2036
|
+
block_count=5 cstruct_size=1 is_def=true winner = rows_parsed[0]
|
|
2037
|
+
block_count=5 cstruct_size=1 is_def=true for loser in rows_parsed[1:]:
|
|
2038
|
+
block_count=6 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?',
|
|
2039
|
+
compo c_name=cursor
|
|
2040
|
+
block_count=7 cstruct_size=1 is_def=true (loser['id'],))
|
|
2041
|
+
block_count=5 cstruct_size=1 is_def=true msg += ' (Cleaned up duplicate records)'
|
|
2042
|
+
block_count=3 cstruct_size=1 is_def=true conn.commit()
|
|
2043
|
+
compo c_name=conn
|
|
2044
|
+
block_count=3 cstruct_size=1 is_def=true conn.close()
|
|
2045
|
+
compo c_name=conn
|
|
2046
|
+
block_count=3 cstruct_size=1 is_def=true return msg
|
|
2047
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
2048
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error saving product: {str(e)}'
|
|
2049
|
+
block_count=0 cstruct_size=1 is_def=true class UpdatePricesInput(BaseModel):
|
|
2050
|
+
end of test_script.SaveProductTool
|
|
2051
|
+
class_name=test_script.UpdatePricesInput
|
|
2052
|
+
base_name=BaseModel
|
|
2053
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
|
|
2054
|
+
compo c_name=Field
|
|
2055
|
+
block_count=0 cstruct_size=1 is_def=false class UpdatePricesTool(BaseTool):
|
|
2056
|
+
end of test_script.UpdatePricesInput
|
|
2057
|
+
class_name=test_script.UpdatePricesTool
|
|
2058
|
+
base_name=BaseTool
|
|
2059
|
+
block_count=1 cstruct_size=1 is_def=false name = 'update_prices'
|
|
2060
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
2061
|
+
block_count=2 cstruct_size=1 is_def=false 'Accesses the registered URL for each product in the database directly to check stock, price, and specs. Updates info or deletes if unavailable.'
|
|
2062
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
2063
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = UpdatePricesInput
|
|
2064
|
+
block_count=1 cstruct_size=1 is_def=false def _fetch_page_content(self, url: str) ->(bool, str, str):
|
|
2065
|
+
block_count=2 cstruct_size=1 is_def=true """
|
|
2066
|
+
block_count=2 cstruct_size=1 is_def=true if not url or not url.startswith('http'):
|
|
2067
|
+
compo c_name=url
|
|
2068
|
+
block_count=3 cstruct_size=1 is_def=true return False, 'Invalid URL', ''
|
|
2069
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
2070
|
+
block_count=3 cstruct_size=1 is_def=true req = urllib.request.Request(url, headers={'User-Agent':
|
|
2071
|
+
compo c_name=urllib
|
|
2072
|
+
compo c_name=Request
|
|
2073
|
+
block_count=4 cstruct_size=1 is_def=true 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
2074
|
+
block_count=4 cstruct_size=1 is_def=true , 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8'})
|
|
2075
|
+
block_count=3 cstruct_size=1 is_def=true with urllib.request.urlopen(req, timeout=15) as response:
|
|
2076
|
+
compo c_name=urllib
|
|
2077
|
+
block_count=4 cstruct_size=1 is_def=true html_content = response.read().decode('utf-8', errors='ignore')
|
|
2078
|
+
compo c_name=response
|
|
2079
|
+
block_count=4 cstruct_size=1 is_def=true bot_keywords = ['ロボットではありません', 'アクセスが制限されています', 'キャプチャ',
|
|
2080
|
+
block_count=5 cstruct_size=1 is_def=true 'CAPTCHA', 'Are you a human?',
|
|
2081
|
+
block_count=5 cstruct_size=1 is_def=true 'Please verify you are a human', 'Incapsula', 'Cloudflare']
|
|
2082
|
+
block_count=4 cstruct_size=1 is_def=true html_lower = html_content.lower()
|
|
2083
|
+
block_count=4 cstruct_size=1 is_def=true for kw in bot_keywords:
|
|
2084
|
+
block_count=5 cstruct_size=1 is_def=true if kw.lower() in html_lower:
|
|
2085
|
+
compo c_name=kw
|
|
2086
|
+
block_count=6 cstruct_size=1 is_def=true return False, 'Bot Challenge Detected', ''
|
|
2087
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<script.*?>.*?</script>', '',
|
|
2088
|
+
compo c_name=re
|
|
2089
|
+
block_count=5 cstruct_size=1 is_def=true html_content, flags=re.DOTALL | re.IGNORECASE)
|
|
2090
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<style.*?>.*?</style>', '', clean_text,
|
|
2091
|
+
compo c_name=re
|
|
2092
|
+
block_count=5 cstruct_size=1 is_def=true flags=re.DOTALL | re.IGNORECASE)
|
|
2093
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<img[^>]+alt="([^"]*)"[^>]*>', ' \\1 ',
|
|
2094
|
+
compo c_name=re
|
|
2095
|
+
block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
|
|
2096
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub("<img[^>]+alt='([^']*)'[^>]*>", ' \\1 ',
|
|
2097
|
+
compo c_name=re
|
|
2098
|
+
block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
|
|
2099
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<.*?>', ' ', clean_text)
|
|
2100
|
+
compo c_name=re
|
|
2101
|
+
block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('\\s+', ' ', clean_text).strip()
|
|
2102
|
+
compo c_name=re
|
|
2103
|
+
block_count=4 cstruct_size=1 is_def=true if len(clean_text) > 10000:
|
|
2104
|
+
block_count=5 cstruct_size=1 is_def=true clean_text = clean_text[:10000]
|
|
2105
|
+
block_count=4 cstruct_size=1 is_def=true return True, 'Success', clean_text
|
|
2106
|
+
block_count=2 cstruct_size=1 is_def=true except urllib.error.HTTPError as e:
|
|
2107
|
+
compo c_name=urllib
|
|
2108
|
+
block_count=3 cstruct_size=1 is_def=true if e.code == 404:
|
|
2109
|
+
block_count=4 cstruct_size=1 is_def=true return False, '404 Not Found', ''
|
|
2110
|
+
block_count=3 cstruct_size=1 is_def=true elif e.code == 410:
|
|
2111
|
+
block_count=4 cstruct_size=1 is_def=true return False, '410 Gone', ''
|
|
2112
|
+
block_count=3 cstruct_size=1 is_def=true elif e.code in [500, 502, 503, 504]:
|
|
2113
|
+
block_count=4 cstruct_size=1 is_def=true return False, f'Retryable Server Error ({e.code})', ''
|
|
2114
|
+
block_count=3 cstruct_size=1 is_def=true elif e.code == 403:
|
|
2115
|
+
block_count=4 cstruct_size=1 is_def=true return False, '403 Forbidden (Possible Bot Block)', ''
|
|
2116
|
+
block_count=3 cstruct_size=1 is_def=true else:
|
|
2117
|
+
block_count=4 cstruct_size=1 is_def=true return False, f'HTTP Error {e.code}', ''
|
|
2118
|
+
block_count=2 cstruct_size=1 is_def=true except urllib.error.URLError as e:
|
|
2119
|
+
compo c_name=urllib
|
|
2120
|
+
block_count=3 cstruct_size=1 is_def=true return False, f'URL Error: {e.reason}', ''
|
|
2121
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
2122
|
+
block_count=3 cstruct_size=1 is_def=true return False, f'Connection Error: {e}', ''
|
|
2123
|
+
block_count=1 cstruct_size=1 is_def=true def _delete_product(self, product: dict, reason: str):
|
|
2124
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
2125
|
+
block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
2126
|
+
compo c_name=sqlite3
|
|
2127
|
+
block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
|
|
2128
|
+
compo c_name=conn
|
|
2129
|
+
block_count=3 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (product[
|
|
2130
|
+
compo c_name=cursor
|
|
2131
|
+
block_count=4 cstruct_size=1 is_def=true 'id'],))
|
|
2132
|
+
block_count=3 cstruct_size=1 is_def=true conn.commit()
|
|
2133
|
+
compo c_name=conn
|
|
2134
|
+
block_count=3 cstruct_size=1 is_def=true conn.close()
|
|
2135
|
+
compo c_name=conn
|
|
2136
|
+
block_count=3 cstruct_size=1 is_def=true msg = f"Deleted {product['name']} at {product['store']} ({reason})"
|
|
2137
|
+
block_count=3 cstruct_size=1 is_def=true print(f' {msg}')
|
|
2138
|
+
block_count=3 cstruct_size=1 is_def=true email_subject = f"Product Deleted: {product['name']}"
|
|
2139
|
+
block_count=3 cstruct_size=1 is_def=true email_body = f"""Action: Deleted (Unavailable/Not Found)
|
|
2140
|
+
block_count=3 cstruct_size=1 is_def=true send_email_notification(email_subject, email_body)
|
|
2141
|
+
block_count=3 cstruct_size=1 is_def=true return True
|
|
2142
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
2143
|
+
block_count=3 cstruct_size=1 is_def=true print(f' Error deleting product: {e}')
|
|
2144
|
+
block_count=3 cstruct_size=1 is_def=true return False
|
|
2145
|
+
block_count=1 cstruct_size=1 is_def=true def _run(self, query: str='', **kwargs):
|
|
2146
|
+
block_count=2 cstruct_size=1 is_def=true print('\n--- Starting Price Update (Direct URL Access) ---')
|
|
2147
|
+
block_count=2 cstruct_size=1 is_def=true products = get_all_products()
|
|
2148
|
+
block_count=2 cstruct_size=1 is_def=true if not products:
|
|
2149
|
+
block_count=3 cstruct_size=1 is_def=true return 'No products in database to update.'
|
|
2150
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
2151
|
+
compo c_name=os
|
|
2152
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
|
|
2153
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
2154
|
+
block_count=2 cstruct_size=1 is_def=true updated_count = 0
|
|
2155
|
+
block_count=2 cstruct_size=1 is_def=true deleted_count = 0
|
|
2156
|
+
block_count=2 cstruct_size=1 is_def=true for p in products:
|
|
2157
|
+
block_count=3 cstruct_size=1 is_def=true name = p['name']
|
|
2158
|
+
block_count=3 cstruct_size=1 is_def=true store = p['store']
|
|
2159
|
+
block_count=3 cstruct_size=1 is_def=true url = p['url']
|
|
2160
|
+
block_count=3 cstruct_size=1 is_def=true print(f"Checking: {name} at {store} (ID: {p['id']})")
|
|
2161
|
+
block_count=3 cstruct_size=1 is_def=true if not url:
|
|
2162
|
+
block_count=4 cstruct_size=1 is_def=true print(f' [Warning] No URL for this product. Skipping.')
|
|
2163
|
+
block_count=4 cstruct_size=1 is_def=true continue
|
|
2164
|
+
block_count=3 cstruct_size=1 is_def=true success, access_reason, page_text = self._fetch_page_content(url)
|
|
2165
|
+
block_count=3 cstruct_size=1 is_def=true if not success:
|
|
2166
|
+
block_count=4 cstruct_size=1 is_def=true if ('404 Not Found' in access_reason or '410 Gone' in
|
|
2167
|
+
block_count=5 cstruct_size=1 is_def=true access_reason or 'Invalid URL' in access_reason):
|
|
2168
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
2169
|
+
block_count=6 cstruct_size=1 is_def=true f' [Info] URL is dead ({access_reason}). Deleting product.'
|
|
2170
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
2171
|
+
block_count=5 cstruct_size=1 is_def=true if self._delete_product(p, access_reason):
|
|
2172
|
+
block_count=6 cstruct_size=1 is_def=true deleted_count += 1
|
|
2173
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
2174
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
2175
|
+
block_count=6 cstruct_size=1 is_def=true f' [Warning] Skipping update due to temporary/access error: {access_reason}'
|
|
2176
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
2177
|
+
block_count=4 cstruct_size=1 is_def=true continue
|
|
2178
|
+
block_count=3 cstruct_size=1 is_def=true prompt = f"""
|
|
2179
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
2180
|
+
block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt)
|
|
2181
|
+
compo c_name=llm
|
|
2182
|
+
block_count=4 cstruct_size=1 is_def=true content = response.content
|
|
2183
|
+
compo c_name=response
|
|
2184
|
+
block_count=4 cstruct_size=1 is_def=true if '```json' in content:
|
|
2185
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
2186
|
+
compo c_name=content
|
|
2187
|
+
block_count=4 cstruct_size=1 is_def=true elif '```' in content:
|
|
2188
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
2189
|
+
compo c_name=content
|
|
2190
|
+
block_count=4 cstruct_size=1 is_def=true content = content.strip()
|
|
2191
|
+
compo c_name=content
|
|
2192
|
+
block_count=4 cstruct_size=1 is_def=true content = re.sub('\\\\(?![/"\\\\bfnrtu])', '\\\\\\\\', content)
|
|
2193
|
+
compo c_name=re
|
|
2194
|
+
block_count=4 cstruct_size=1 is_def=true try:
|
|
2195
|
+
block_count=5 cstruct_size=1 is_def=true result_data = json.loads(content, strict=False)
|
|
2196
|
+
compo c_name=json
|
|
2197
|
+
block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
|
|
2198
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
2199
|
+
block_count=6 cstruct_size=1 is_def=true f' [Warning] Failed to parse JSON: {e}. Attempting further cleanup.'
|
|
2200
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
2201
|
+
block_count=5 cstruct_size=1 is_def=true content = content.replace('\\', '')
|
|
2202
|
+
compo c_name=content
|
|
2203
|
+
block_count=5 cstruct_size=1 is_def=true result_data = json.loads(content, strict=False)
|
|
2204
|
+
compo c_name=json
|
|
2205
|
+
block_count=4 cstruct_size=1 is_def=true is_unavailable = result_data.get('is_unavailable', False)
|
|
2206
|
+
block_count=4 cstruct_size=1 is_def=true unavailability_reason = result_data.get('unavailability_reason'
|
|
2207
|
+
block_count=5 cstruct_size=1 is_def=true , 'ページ内に在庫なし・販売終了の記載あり')
|
|
2208
|
+
block_count=4 cstruct_size=1 is_def=true if is_unavailable:
|
|
2209
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
2210
|
+
block_count=6 cstruct_size=1 is_def=true f' [Info] LLM determined product is unavailable. Reason: {unavailability_reason}. Deleting product.'
|
|
2211
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
2212
|
+
block_count=5 cstruct_size=1 is_def=true if self._delete_product(p, unavailability_reason):
|
|
2213
|
+
block_count=6 cstruct_size=1 is_def=true deleted_count += 1
|
|
2214
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
2215
|
+
block_count=5 cstruct_size=1 is_def=true new_price = result_data.get('price', '')
|
|
2216
|
+
block_count=5 cstruct_size=1 is_def=true new_desc = result_data.get('description', '')
|
|
2217
|
+
block_count=5 cstruct_size=1 is_def=true new_model = result_data.get('model_number', '')
|
|
2218
|
+
block_count=5 cstruct_size=1 is_def=true new_release = result_data.get('release_date', '')
|
|
2219
|
+
block_count=5 cstruct_size=1 is_def=true new_ram = result_data.get('ram', '')
|
|
2220
|
+
block_count=5 cstruct_size=1 is_def=true new_ssd = result_data.get('ssd', '')
|
|
2221
|
+
block_count=5 cstruct_size=1 is_def=true if not new_price:
|
|
2222
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
2223
|
+
block_count=7 cstruct_size=1 is_def=true f' [Warning] Could not extract price from page. Skipping update.'
|
|
2224
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
2225
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
2226
|
+
block_count=5 cstruct_size=1 is_def=true final_desc = new_desc if new_desc else p['description']
|
|
2227
|
+
block_count=5 cstruct_size=1 is_def=true final_model = new_model if new_model else p.get(
|
|
2228
|
+
block_count=6 cstruct_size=1 is_def=true 'model_number', '')
|
|
2229
|
+
block_count=5 cstruct_size=1 is_def=true final_release = new_release if new_release else p.get(
|
|
2230
|
+
block_count=6 cstruct_size=1 is_def=true 'release_date', '')
|
|
2231
|
+
block_count=5 cstruct_size=1 is_def=true final_ram = new_ram if new_ram else p.get('ram', '')
|
|
2232
|
+
block_count=5 cstruct_size=1 is_def=true final_ssd = new_ssd if new_ssd else p.get('ssd', '')
|
|
2233
|
+
block_count=5 cstruct_size=1 is_def=true changes = []
|
|
2234
|
+
block_count=5 cstruct_size=1 is_def=true new_price_val = parse_price_val(new_price)
|
|
2235
|
+
block_count=5 cstruct_size=1 is_def=true old_price_val = parse_price_val(p['price'])
|
|
2236
|
+
block_count=5 cstruct_size=1 is_def=true if new_price_val != old_price_val:
|
|
2237
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f"Price ({p['price']} -> {new_price})")
|
|
2238
|
+
compo c_name=changes
|
|
2239
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2240
|
+
block_count=6 cstruct_size=1 is_def=true new_price = p['price']
|
|
2241
|
+
block_count=5 cstruct_size=1 is_def=true old_model = p.get('model_number', '')
|
|
2242
|
+
block_count=5 cstruct_size=1 is_def=true if not is_similar_model(final_model, old_model):
|
|
2243
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f'Model ({old_model} -> {final_model})')
|
|
2244
|
+
compo c_name=changes
|
|
2245
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2246
|
+
block_count=6 cstruct_size=1 is_def=true final_model = old_model
|
|
2247
|
+
block_count=5 cstruct_size=1 is_def=true old_release = p.get('release_date', '')
|
|
2248
|
+
block_count=5 cstruct_size=1 is_def=true if parse_date_val(final_release) != parse_date_val(
|
|
2249
|
+
block_count=6 cstruct_size=1 is_def=true old_release):
|
|
2250
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(
|
|
2251
|
+
compo c_name=changes
|
|
2252
|
+
block_count=7 cstruct_size=1 is_def=true f'Release Date ({old_release} -> {final_release})')
|
|
2253
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2254
|
+
block_count=6 cstruct_size=1 is_def=true final_release = old_release
|
|
2255
|
+
block_count=5 cstruct_size=1 is_def=true old_ram = p.get('ram', '')
|
|
2256
|
+
block_count=5 cstruct_size=1 is_def=true if extract_alphanumeric(final_ram) != extract_alphanumeric(
|
|
2257
|
+
block_count=6 cstruct_size=1 is_def=true old_ram):
|
|
2258
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f'RAM ({old_ram} -> {final_ram})')
|
|
2259
|
+
compo c_name=changes
|
|
2260
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2261
|
+
block_count=6 cstruct_size=1 is_def=true final_ram = old_ram
|
|
2262
|
+
block_count=5 cstruct_size=1 is_def=true old_ssd = p.get('ssd', '')
|
|
2263
|
+
block_count=5 cstruct_size=1 is_def=true if extract_alphanumeric(final_ssd) != extract_alphanumeric(
|
|
2264
|
+
block_count=6 cstruct_size=1 is_def=true old_ssd):
|
|
2265
|
+
block_count=6 cstruct_size=1 is_def=true changes.append(f'SSD ({old_ssd} -> {final_ssd})')
|
|
2266
|
+
compo c_name=changes
|
|
2267
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2268
|
+
block_count=6 cstruct_size=1 is_def=true final_ssd = old_ssd
|
|
2269
|
+
block_count=5 cstruct_size=1 is_def=true if changes:
|
|
2270
|
+
block_count=6 cstruct_size=1 is_def=true try:
|
|
2271
|
+
block_count=7 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
2272
|
+
compo c_name=sqlite3
|
|
2273
|
+
block_count=7 cstruct_size=1 is_def=true cursor = conn.cursor()
|
|
2274
|
+
compo c_name=conn
|
|
2275
|
+
block_count=7 cstruct_size=1 is_def=true cursor.execute(
|
|
2276
|
+
compo c_name=cursor
|
|
2277
|
+
block_count=8 cstruct_size=1 is_def=true """
|
|
2278
|
+
block_count=8 cstruct_size=1 is_def=true , (new_price, final_desc, final_model,
|
|
2279
|
+
block_count=8 cstruct_size=1 is_def=true final_release, final_ram, final_ssd, p['id']))
|
|
2280
|
+
block_count=7 cstruct_size=1 is_def=true conn.commit()
|
|
2281
|
+
compo c_name=conn
|
|
2282
|
+
block_count=7 cstruct_size=1 is_def=true if cursor.rowcount > 0:
|
|
2283
|
+
compo c_name=cursor
|
|
2284
|
+
block_count=8 cstruct_size=1 is_def=true updated_count += 1
|
|
2285
|
+
block_count=8 cstruct_size=1 is_def=true changes_str = ', '.join(changes)
|
|
2286
|
+
block_count=8 cstruct_size=1 is_def=true msg = (
|
|
2287
|
+
block_count=9 cstruct_size=1 is_def=true f'Updated {name} at {store}. Changes: {changes_str}'
|
|
2288
|
+
block_count=9 cstruct_size=1 is_def=true )
|
|
2289
|
+
block_count=8 cstruct_size=1 is_def=true print(f' {msg}')
|
|
2290
|
+
block_count=8 cstruct_size=1 is_def=true email_subject = f'Product Updated: {name}'
|
|
2291
|
+
block_count=8 cstruct_size=1 is_def=true email_body = f"""Action: Updated Info (Direct URL Check)
|
|
2292
|
+
block_count=8 cstruct_size=1 is_def=true send_email_notification(email_subject,
|
|
2293
|
+
block_count=9 cstruct_size=1 is_def=true email_body)
|
|
2294
|
+
block_count=7 cstruct_size=1 is_def=true conn.close()
|
|
2295
|
+
compo c_name=conn
|
|
2296
|
+
block_count=6 cstruct_size=1 is_def=true except Exception as e:
|
|
2297
|
+
block_count=7 cstruct_size=1 is_def=true print(f' Error updating {name} at {store}: {e}')
|
|
2298
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2299
|
+
block_count=6 cstruct_size=1 is_def=true print(f' No spec/price changes for {name} at {store}.'
|
|
2300
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
2301
|
+
block_count=3 cstruct_size=1 is_def=true except Exception as e:
|
|
2302
|
+
block_count=4 cstruct_size=1 is_def=true print(f' Error processing LLM response for {name}: {e}')
|
|
2303
|
+
block_count=3 cstruct_size=1 is_def=true time.sleep(1)
|
|
2304
|
+
compo c_name=time
|
|
2305
|
+
block_count=2 cstruct_size=1 is_def=true return (
|
|
2306
|
+
block_count=3 cstruct_size=1 is_def=true f'Price update complete. Updated {updated_count} items, Deleted {deleted_count} unavailable items.'
|
|
2307
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
2308
|
+
block_count=0 cstruct_size=1 is_def=true class SearchProductsInput(BaseModel):
|
|
2309
|
+
end of test_script.UpdatePricesTool
|
|
2310
|
+
class_name=test_script.SearchProductsInput
|
|
2311
|
+
base_name=BaseModel
|
|
2312
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
|
|
2313
|
+
compo c_name=Field
|
|
2314
|
+
block_count=2 cstruct_size=1 is_def=false 'Natural language query to search products in the database')
|
|
2315
|
+
block_count=0 cstruct_size=1 is_def=false class SearchProductsTool(BaseTool):
|
|
2316
|
+
end of test_script.SearchProductsInput
|
|
2317
|
+
class_name=test_script.SearchProductsTool
|
|
2318
|
+
base_name=BaseTool
|
|
2319
|
+
block_count=1 cstruct_size=1 is_def=false name = 'search_products'
|
|
2320
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
2321
|
+
block_count=2 cstruct_size=1 is_def=false "Searches for products in the database using natural language queries (e.g., 'cheapest products', 'items with 16GB memory')."
|
|
2322
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
2323
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SearchProductsInput
|
|
2324
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, query: str, **kwargs):
|
|
2325
|
+
block_count=2 cstruct_size=1 is_def=true print(f'\n--- Searching Database: {query} ---')
|
|
2326
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
2327
|
+
compo c_name=os
|
|
2328
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
|
|
2329
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
2330
|
+
block_count=2 cstruct_size=1 is_def=true prompt = f"""
|
|
2331
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
2332
|
+
block_count=3 cstruct_size=1 is_def=true response = llm.invoke(prompt)
|
|
2333
|
+
compo c_name=llm
|
|
2334
|
+
block_count=3 cstruct_size=1 is_def=true content = response.content.strip()
|
|
2335
|
+
compo c_name=response
|
|
2336
|
+
block_count=3 cstruct_size=1 is_def=true if '```json' in content:
|
|
2337
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
2338
|
+
compo c_name=content
|
|
2339
|
+
block_count=3 cstruct_size=1 is_def=true elif '```' in content:
|
|
2340
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
2341
|
+
compo c_name=content
|
|
2342
|
+
block_count=3 cstruct_size=1 is_def=true criteria = json.loads(content)
|
|
2343
|
+
compo c_name=json
|
|
2344
|
+
block_count=3 cstruct_size=1 is_def=true print(f' Search Criteria: {criteria}')
|
|
2345
|
+
block_count=3 cstruct_size=1 is_def=true all_products = get_all_products()
|
|
2346
|
+
block_count=3 cstruct_size=1 is_def=true filtered_products = []
|
|
2347
|
+
block_count=3 cstruct_size=1 is_def=true keyword_groups = criteria.get('keyword_groups', [])
|
|
2348
|
+
compo c_name=criteria
|
|
2349
|
+
block_count=3 cstruct_size=1 is_def=true exclude_keywords = criteria.get('exclude_keywords', [])
|
|
2350
|
+
compo c_name=criteria
|
|
2351
|
+
block_count=3 cstruct_size=1 is_def=true empty_fields = criteria.get('empty_fields', [])
|
|
2352
|
+
compo c_name=criteria
|
|
2353
|
+
block_count=3 cstruct_size=1 is_def=true sort_by = criteria.get('sort_by')
|
|
2354
|
+
compo c_name=criteria
|
|
2355
|
+
block_count=3 cstruct_size=1 is_def=true max_p = criteria.get('max_price')
|
|
2356
|
+
compo c_name=criteria
|
|
2357
|
+
block_count=3 cstruct_size=1 is_def=true min_p = criteria.get('min_price')
|
|
2358
|
+
compo c_name=criteria
|
|
2359
|
+
block_count=3 cstruct_size=1 is_def=true for p in all_products:
|
|
2360
|
+
block_count=4 cstruct_size=1 is_def=true text_to_search = (p['name'] + ' ' + (p['description'] or ''
|
|
2361
|
+
block_count=5 cstruct_size=1 is_def=true ) + ' ' + (p['url'] or '')).lower()
|
|
2362
|
+
block_count=4 cstruct_size=1 is_def=true if empty_fields:
|
|
2363
|
+
block_count=5 cstruct_size=1 is_def=true is_empty_match = True
|
|
2364
|
+
block_count=5 cstruct_size=1 is_def=true for field in empty_fields:
|
|
2365
|
+
block_count=6 cstruct_size=1 is_def=true val = p.get(field)
|
|
2366
|
+
block_count=6 cstruct_size=1 is_def=true if val and str(val).strip():
|
|
2367
|
+
block_count=7 cstruct_size=1 is_def=true is_empty_match = False
|
|
2368
|
+
block_count=7 cstruct_size=1 is_def=true break
|
|
2369
|
+
block_count=5 cstruct_size=1 is_def=true if not is_empty_match:
|
|
2370
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
2371
|
+
block_count=4 cstruct_size=1 is_def=true if exclude_keywords:
|
|
2372
|
+
block_count=5 cstruct_size=1 is_def=true should_exclude = False
|
|
2373
|
+
block_count=5 cstruct_size=1 is_def=true for k in exclude_keywords:
|
|
2374
|
+
block_count=6 cstruct_size=1 is_def=true if k.lower() in text_to_search:
|
|
2375
|
+
block_count=7 cstruct_size=1 is_def=true should_exclude = True
|
|
2376
|
+
block_count=7 cstruct_size=1 is_def=true break
|
|
2377
|
+
block_count=5 cstruct_size=1 is_def=true if should_exclude:
|
|
2378
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
2379
|
+
block_count=4 cstruct_size=1 is_def=true if keyword_groups:
|
|
2380
|
+
block_count=5 cstruct_size=1 is_def=true all_groups_match = True
|
|
2381
|
+
block_count=5 cstruct_size=1 is_def=true for group in keyword_groups:
|
|
2382
|
+
block_count=6 cstruct_size=1 is_def=true group_match = False
|
|
2383
|
+
block_count=6 cstruct_size=1 is_def=true for k in group:
|
|
2384
|
+
block_count=7 cstruct_size=1 is_def=true if k.lower() in text_to_search:
|
|
2385
|
+
block_count=8 cstruct_size=1 is_def=true group_match = True
|
|
2386
|
+
block_count=8 cstruct_size=1 is_def=true break
|
|
2387
|
+
block_count=6 cstruct_size=1 is_def=true if not group_match:
|
|
2388
|
+
block_count=7 cstruct_size=1 is_def=true all_groups_match = False
|
|
2389
|
+
block_count=7 cstruct_size=1 is_def=true break
|
|
2390
|
+
block_count=5 cstruct_size=1 is_def=true if not all_groups_match:
|
|
2391
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
2392
|
+
block_count=4 cstruct_size=1 is_def=true price_val = parse_price_val(p['price'])
|
|
2393
|
+
block_count=4 cstruct_size=1 is_def=true if max_p is not None and price_val > max_p:
|
|
2394
|
+
block_count=5 cstruct_size=1 is_def=true continue
|
|
2395
|
+
block_count=4 cstruct_size=1 is_def=true if min_p is not None and price_val < min_p:
|
|
2396
|
+
block_count=5 cstruct_size=1 is_def=true continue
|
|
2397
|
+
block_count=4 cstruct_size=1 is_def=true p['price_val'] = price_val
|
|
2398
|
+
block_count=4 cstruct_size=1 is_def=true filtered_products.append(p)
|
|
2399
|
+
block_count=3 cstruct_size=1 is_def=true if sort_by == 'price_asc':
|
|
2400
|
+
block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'])
|
|
2401
|
+
block_count=3 cstruct_size=1 is_def=true elif sort_by == 'price_desc':
|
|
2402
|
+
block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'],
|
|
2403
|
+
block_count=5 cstruct_size=1 is_def=true reverse=True)
|
|
2404
|
+
block_count=3 cstruct_size=1 is_def=true if not filtered_products:
|
|
2405
|
+
block_count=4 cstruct_size=1 is_def=true return 'No products found matching your criteria.'
|
|
2406
|
+
block_count=3 cstruct_size=1 is_def=true result_str = f'Found {len(filtered_products)} products:\n'
|
|
2407
|
+
block_count=3 cstruct_size=1 is_def=true for p in filtered_products[:10]:
|
|
2408
|
+
block_count=4 cstruct_size=1 is_def=true result_str += (
|
|
2409
|
+
block_count=5 cstruct_size=1 is_def=true f"- [ID: {p['id']}] {p['name']} ({p['price']}) @ {p['store']}\n"
|
|
2410
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
2411
|
+
block_count=4 cstruct_size=1 is_def=true if p['description']:
|
|
2412
|
+
block_count=5 cstruct_size=1 is_def=true result_str += f" Desc: {p['description'][:100]}...\n"
|
|
2413
|
+
block_count=4 cstruct_size=1 is_def=true if p['url']:
|
|
2414
|
+
block_count=5 cstruct_size=1 is_def=true result_str += f" URL: {p['url']}\n"
|
|
2415
|
+
block_count=4 cstruct_size=1 is_def=true result_str += '\n'
|
|
2416
|
+
block_count=3 cstruct_size=1 is_def=true return result_str
|
|
2417
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
2418
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error executing search: {e}'
|
|
2419
|
+
block_count=0 cstruct_size=1 is_def=true class FindSimilarProductsInput(BaseModel):
|
|
2420
|
+
end of test_script.SearchProductsTool
|
|
2421
|
+
class_name=test_script.FindSimilarProductsInput
|
|
2422
|
+
base_name=BaseModel
|
|
2423
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
|
|
2424
|
+
compo c_name=Field
|
|
2425
|
+
block_count=0 cstruct_size=1 is_def=false class FindSimilarProductsTool(BaseTool):
|
|
2426
|
+
end of test_script.FindSimilarProductsInput
|
|
2427
|
+
class_name=test_script.FindSimilarProductsTool
|
|
2428
|
+
base_name=BaseTool
|
|
2429
|
+
block_count=1 cstruct_size=1 is_def=false name = 'find_similar_products'
|
|
2430
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
2431
|
+
block_count=2 cstruct_size=1 is_def=false 'Searches for similar products to those in the database and adds the best ones if found.'
|
|
2432
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
2433
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = FindSimilarProductsInput
|
|
2434
|
+
block_count=1 cstruct_size=1 is_def=false agent_logs: str = ''
|
|
2435
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
|
|
2436
|
+
block_count=2 cstruct_size=1 is_def=true print('\n--- Starting Similar Product Search ---')
|
|
2437
|
+
block_count=2 cstruct_size=1 is_def=true products = get_all_products()
|
|
2438
|
+
block_count=2 cstruct_size=1 is_def=true if not products:
|
|
2439
|
+
block_count=3 cstruct_size=1 is_def=true return 'No products in database to base search on.'
|
|
2440
|
+
block_count=2 cstruct_size=1 is_def=true target_products = []
|
|
2441
|
+
block_count=2 cstruct_size=1 is_def=true if query:
|
|
2442
|
+
block_count=3 cstruct_size=1 is_def=true print(f'Filtering products with query: {query}')
|
|
2443
|
+
block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
|
|
2444
|
+
compo c_name=query
|
|
2445
|
+
block_count=3 cstruct_size=1 is_def=true for p in products:
|
|
2446
|
+
block_count=4 cstruct_size=1 is_def=true if query_lower in p['name'].lower() or p['description'
|
|
2447
|
+
block_count=5 cstruct_size=1 is_def=true ] and query_lower in p['description'].lower():
|
|
2448
|
+
block_count=5 cstruct_size=1 is_def=true target_products.append(p)
|
|
2449
|
+
block_count=2 cstruct_size=1 is_def=true else:
|
|
2450
|
+
block_count=3 cstruct_size=1 is_def=true target_products = products
|
|
2451
|
+
block_count=2 cstruct_size=1 is_def=true if not target_products:
|
|
2452
|
+
block_count=3 cstruct_size=1 is_def=true return f"No products found matching query '{query}'."
|
|
2453
|
+
block_count=2 cstruct_size=1 is_def=true unique_products = {}
|
|
2454
|
+
block_count=2 cstruct_size=1 is_def=true for p in target_products:
|
|
2455
|
+
block_count=3 cstruct_size=1 is_def=true if p['name'] not in unique_products:
|
|
2456
|
+
block_count=4 cstruct_size=1 is_def=true unique_products[p['name']] = p
|
|
2457
|
+
block_count=2 cstruct_size=1 is_def=true target_names = list(unique_products.keys())
|
|
2458
|
+
block_count=2 cstruct_size=1 is_def=true print(
|
|
2459
|
+
block_count=3 cstruct_size=1 is_def=true f'Found {len(target_names)} target products to find similar items for.'
|
|
2460
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
2461
|
+
block_count=2 cstruct_size=1 is_def=true logs_context = ''
|
|
2462
|
+
block_count=2 cstruct_size=1 is_def=true if self.agent_logs:
|
|
2463
|
+
compo c_name=self
|
|
2464
|
+
block_count=3 cstruct_size=1 is_def=true logs_context = f'\n参考情報 (過去の検索履歴):\n{self.agent_logs}\n'
|
|
2465
|
+
block_count=2 cstruct_size=1 is_def=true search = get_search_tool_func()
|
|
2466
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
2467
|
+
compo c_name=os
|
|
2468
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
|
|
2469
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
2470
|
+
block_count=2 cstruct_size=1 is_def=true save_tool = SaveProductTool()
|
|
2471
|
+
compo c_name=SaveProductTool
|
|
2472
|
+
block_count=2 cstruct_size=1 is_def=true cached_similar_items = []
|
|
2473
|
+
block_count=2 cstruct_size=1 is_def=true max_targets = 5
|
|
2474
|
+
block_count=2 cstruct_size=1 is_def=true if len(target_names) > max_targets:
|
|
2475
|
+
block_count=3 cstruct_size=1 is_def=true print(f'Limiting search to first {max_targets} products.')
|
|
2476
|
+
block_count=3 cstruct_size=1 is_def=true target_names = target_names[:max_targets]
|
|
2477
|
+
block_count=2 cstruct_size=1 is_def=true for name in target_names:
|
|
2478
|
+
block_count=3 cstruct_size=1 is_def=true product_data = unique_products[name]
|
|
2479
|
+
block_count=3 cstruct_size=1 is_def=true description = product_data.get('description', '')
|
|
2480
|
+
block_count=3 cstruct_size=1 is_def=true print(f'Searching for similar items to: {name}')
|
|
2481
|
+
block_count=3 cstruct_size=1 is_def=true search_query = f'{name} 類似商品 おすすめ 比較 スペック'
|
|
2482
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
2483
|
+
block_count=4 cstruct_size=1 is_def=true search_results = search.run(search_query)
|
|
2484
|
+
compo c_name=search
|
|
2485
|
+
block_count=3 cstruct_size=1 is_def=true except Exception as e:
|
|
2486
|
+
block_count=4 cstruct_size=1 is_def=true print(f'Search failed for {name}: {e}')
|
|
2487
|
+
block_count=4 cstruct_size=1 is_def=true continue
|
|
2488
|
+
block_count=3 cstruct_size=1 is_def=true prompt = f"""
|
|
2489
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
2490
|
+
block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt)
|
|
2491
|
+
compo c_name=llm
|
|
2492
|
+
block_count=4 cstruct_size=1 is_def=true content = response.content
|
|
2493
|
+
compo c_name=response
|
|
2494
|
+
block_count=4 cstruct_size=1 is_def=true if '```json' in content:
|
|
2495
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
2496
|
+
compo c_name=content
|
|
2497
|
+
block_count=4 cstruct_size=1 is_def=true elif '```' in content:
|
|
2498
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
2499
|
+
compo c_name=content
|
|
2500
|
+
block_count=4 cstruct_size=1 is_def=true items = json.loads(content)
|
|
2501
|
+
compo c_name=json
|
|
2502
|
+
block_count=4 cstruct_size=1 is_def=true if isinstance(items, list):
|
|
2503
|
+
block_count=5 cstruct_size=1 is_def=true for item in items:
|
|
2504
|
+
block_count=6 cstruct_size=1 is_def=true if item.get('name') == name:
|
|
2505
|
+
compo c_name=item
|
|
2506
|
+
block_count=7 cstruct_size=1 is_def=true continue
|
|
2507
|
+
block_count=6 cstruct_size=1 is_def=true cached_similar_items.append(item)
|
|
2508
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
2509
|
+
block_count=7 cstruct_size=1 is_def=true f" Cached: {item.get('name')} ({item.get('price')})"
|
|
2510
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
2511
|
+
block_count=3 cstruct_size=1 is_def=true except Exception as e:
|
|
2512
|
+
block_count=4 cstruct_size=1 is_def=true print(f'Error processing similar items for {name}: {e}')
|
|
2513
|
+
block_count=3 cstruct_size=1 is_def=true time.sleep(1)
|
|
2514
|
+
compo c_name=time
|
|
2515
|
+
block_count=2 cstruct_size=1 is_def=true if not cached_similar_items:
|
|
2516
|
+
block_count=3 cstruct_size=1 is_def=true return 'No similar products found.'
|
|
2517
|
+
block_count=2 cstruct_size=1 is_def=true print(
|
|
2518
|
+
block_count=3 cstruct_size=1 is_def=true f'\nCached {len(cached_similar_items)} items. Selecting top 3 recommendations...'
|
|
2519
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
2520
|
+
block_count=2 cstruct_size=1 is_def=true selection_prompt = f"""
|
|
2521
|
+
block_count=2 cstruct_size=1 is_def=true added_count = 0
|
|
2522
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
2523
|
+
block_count=3 cstruct_size=1 is_def=true response = llm.invoke(selection_prompt)
|
|
2524
|
+
compo c_name=llm
|
|
2525
|
+
block_count=3 cstruct_size=1 is_def=true content = response.content
|
|
2526
|
+
compo c_name=response
|
|
2527
|
+
block_count=3 cstruct_size=1 is_def=true if '```json' in content:
|
|
2528
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
2529
|
+
compo c_name=content
|
|
2530
|
+
block_count=3 cstruct_size=1 is_def=true elif '```' in content:
|
|
2531
|
+
block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
2532
|
+
compo c_name=content
|
|
2533
|
+
block_count=3 cstruct_size=1 is_def=true top_picks = json.loads(content)
|
|
2534
|
+
compo c_name=json
|
|
2535
|
+
block_count=3 cstruct_size=1 is_def=true if isinstance(top_picks, list):
|
|
2536
|
+
block_count=4 cstruct_size=1 is_def=true for item in top_picks:
|
|
2537
|
+
block_count=5 cstruct_size=1 is_def=true print(f" Saving recommendation: {item.get('name')}")
|
|
2538
|
+
block_count=5 cstruct_size=1 is_def=true res = save_tool._run(name=item.get('name'), store=item.
|
|
2539
|
+
block_count=6 cstruct_size=1 is_def=true get('store', 'Unknown'), price=item.get('price'),
|
|
2540
|
+
block_count=6 cstruct_size=1 is_def=true url=item.get('url', ''), description=item.get(
|
|
2541
|
+
block_count=6 cstruct_size=1 is_def=true 'description', ''), model_number=item.get(
|
|
2542
|
+
block_count=6 cstruct_size=1 is_def=true 'model_number', ''), release_date=item.get(
|
|
2543
|
+
block_count=6 cstruct_size=1 is_def=true 'release_date', ''))
|
|
2544
|
+
block_count=5 cstruct_size=1 is_def=true print(f' -> {res}')
|
|
2545
|
+
block_count=5 cstruct_size=1 is_def=true added_count += 1
|
|
2546
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
2547
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error selecting top recommendations: {e}'
|
|
2548
|
+
block_count=2 cstruct_size=1 is_def=true return (
|
|
2549
|
+
block_count=3 cstruct_size=1 is_def=true f'Similar product search complete. Added {added_count} recommended items.'
|
|
2550
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
2551
|
+
block_count=0 cstruct_size=1 is_def=true class CompareProductsInput(BaseModel):
|
|
2552
|
+
end of test_script.FindSimilarProductsTool
|
|
2553
|
+
class_name=test_script.CompareProductsInput
|
|
2554
|
+
base_name=BaseModel
|
|
2555
|
+
block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
|
|
2556
|
+
compo c_name=Field
|
|
2557
|
+
block_count=2 cstruct_size=1 is_def=false "Optional category or query to filter products for comparison (e.g., 'laptop', 'monitor')."
|
|
2558
|
+
block_count=2 cstruct_size=1 is_def=false , default='')
|
|
2559
|
+
block_count=0 cstruct_size=1 is_def=false class CompareProductsTool(BaseTool):
|
|
2560
|
+
end of test_script.CompareProductsInput
|
|
2561
|
+
class_name=test_script.CompareProductsTool
|
|
2562
|
+
base_name=BaseTool
|
|
2563
|
+
block_count=1 cstruct_size=1 is_def=false name = 'compare_products'
|
|
2564
|
+
block_count=1 cstruct_size=1 is_def=false description = (
|
|
2565
|
+
block_count=2 cstruct_size=1 is_def=false 'Generates a comparison table of products (e.g. RAM, SSD, Price) and ranks them by recommendation. Saves the result as JSON.'
|
|
2566
|
+
block_count=2 cstruct_size=1 is_def=false )
|
|
2567
|
+
block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = CompareProductsInput
|
|
2568
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
|
|
2569
|
+
block_count=2 cstruct_size=1 is_def=true print(f'\n--- Generating Product Comparison: {query} ---')
|
|
2570
|
+
block_count=2 cstruct_size=1 is_def=true products = get_all_products()
|
|
2571
|
+
block_count=2 cstruct_size=1 is_def=true if not products:
|
|
2572
|
+
block_count=3 cstruct_size=1 is_def=true return 'No products found in database.'
|
|
2573
|
+
block_count=2 cstruct_size=1 is_def=true target_products = []
|
|
2574
|
+
block_count=2 cstruct_size=1 is_def=true if query:
|
|
2575
|
+
block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
|
|
2576
|
+
compo c_name=query
|
|
2577
|
+
block_count=3 cstruct_size=1 is_def=true for p in products:
|
|
2578
|
+
block_count=4 cstruct_size=1 is_def=true text = (p['name'] + ' ' + (p['description'] or '')).lower()
|
|
2579
|
+
block_count=4 cstruct_size=1 is_def=true if query_lower in text:
|
|
2580
|
+
block_count=5 cstruct_size=1 is_def=true target_products.append(p)
|
|
2581
|
+
block_count=2 cstruct_size=1 is_def=true else:
|
|
2582
|
+
block_count=3 cstruct_size=1 is_def=true target_products = products
|
|
2583
|
+
block_count=2 cstruct_size=1 is_def=true if not target_products:
|
|
2584
|
+
block_count=3 cstruct_size=1 is_def=true return f"No products found matching '{query}'."
|
|
2585
|
+
block_count=2 cstruct_size=1 is_def=true CHUNK_SIZE = 5
|
|
2586
|
+
block_count=2 cstruct_size=1 is_def=true print(
|
|
2587
|
+
block_count=3 cstruct_size=1 is_def=true f'Step 1: Extracting specs from {len(target_products)} products in chunks of {CHUNK_SIZE}...'
|
|
2588
|
+
block_count=3 cstruct_size=1 is_def=true )
|
|
2589
|
+
block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
2590
|
+
compo c_name=os
|
|
2591
|
+
block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
|
|
2592
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
2593
|
+
block_count=3 cstruct_size=1 is_def=true max_output_tokens=8192)
|
|
2594
|
+
block_count=2 cstruct_size=1 is_def=true extracted_specs = []
|
|
2595
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
2596
|
+
block_count=3 cstruct_size=1 is_def=true for i in range(0, len(target_products), CHUNK_SIZE):
|
|
2597
|
+
block_count=4 cstruct_size=1 is_def=true chunk = target_products[i:i + CHUNK_SIZE]
|
|
2598
|
+
block_count=4 cstruct_size=1 is_def=true print(
|
|
2599
|
+
block_count=5 cstruct_size=1 is_def=true f' Processing chunk {i // CHUNK_SIZE + 1} ({len(chunk)} items)...'
|
|
2600
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
2601
|
+
block_count=4 cstruct_size=1 is_def=true prompt_extract = f"""
|
|
2602
|
+
block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt_extract)
|
|
2603
|
+
compo c_name=llm
|
|
2604
|
+
block_count=4 cstruct_size=1 is_def=true content = response.content
|
|
2605
|
+
compo c_name=response
|
|
2606
|
+
block_count=4 cstruct_size=1 is_def=true if '```json' in content:
|
|
2607
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
|
|
2608
|
+
compo c_name=content
|
|
2609
|
+
block_count=4 cstruct_size=1 is_def=true elif '```' in content:
|
|
2610
|
+
block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
|
|
2611
|
+
compo c_name=content
|
|
2612
|
+
block_count=4 cstruct_size=1 is_def=true content = content.strip()
|
|
2613
|
+
compo c_name=content
|
|
2614
|
+
block_count=4 cstruct_size=1 is_def=true try:
|
|
2615
|
+
block_count=5 cstruct_size=1 is_def=true chunk_data = json.loads(content)
|
|
2616
|
+
compo c_name=json
|
|
2617
|
+
block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
|
|
2618
|
+
block_count=5 cstruct_size=1 is_def=true print(
|
|
2619
|
+
block_count=6 cstruct_size=1 is_def=true f' [Warning] Failed to parse JSON in chunk {i // CHUNK_SIZE + 1}. Attempting aggressive recovery.'
|
|
2620
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
2621
|
+
block_count=5 cstruct_size=1 is_def=true print(f' Error detail: {e}')
|
|
2622
|
+
block_count=5 cstruct_size=1 is_def=true try:
|
|
2623
|
+
block_count=6 cstruct_size=1 is_def=true start_idx = content.find('[')
|
|
2624
|
+
compo c_name=content
|
|
2625
|
+
block_count=6 cstruct_size=1 is_def=true if start_idx != -1:
|
|
2626
|
+
block_count=7 cstruct_size=1 is_def=true clean_content = content[start_idx:]
|
|
2627
|
+
block_count=7 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
|
|
2628
|
+
block_count=8 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
|
|
2629
|
+
block_count=8 cstruct_size=1 is_def=true if last_brace != -1:
|
|
2630
|
+
block_count=9 cstruct_size=1 is_def=true clean_content = clean_content[:
|
|
2631
|
+
block_count=10 cstruct_size=1 is_def=true last_brace + 1] + ']'
|
|
2632
|
+
block_count=8 cstruct_size=1 is_def=true else:
|
|
2633
|
+
block_count=9 cstruct_size=1 is_def=true clean_content += ']'
|
|
2634
|
+
block_count=7 cstruct_size=1 is_def=true chunk_data = json.loads(clean_content)
|
|
2635
|
+
compo c_name=json
|
|
2636
|
+
block_count=6 cstruct_size=1 is_def=true else:
|
|
2637
|
+
block_count=7 cstruct_size=1 is_def=true raise ValueError('No JSON list found.')
|
|
2638
|
+
compo c_name=ValueError
|
|
2639
|
+
block_count=5 cstruct_size=1 is_def=true except Exception as e2:
|
|
2640
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
2641
|
+
block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover JSON even after aggressive repair: {e2}'
|
|
2642
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
2643
|
+
block_count=6 cstruct_size=1 is_def=true continue
|
|
2644
|
+
block_count=4 cstruct_size=1 is_def=true if isinstance(chunk_data, list):
|
|
2645
|
+
block_count=5 cstruct_size=1 is_def=true extracted_specs.extend(chunk_data)
|
|
2646
|
+
block_count=4 cstruct_size=1 is_def=true time.sleep(1)
|
|
2647
|
+
compo c_name=time
|
|
2648
|
+
block_count=3 cstruct_size=1 is_def=true print(
|
|
2649
|
+
block_count=4 cstruct_size=1 is_def=true f'Step 2: Ranking {len(extracted_specs)} products based on extracted specs...'
|
|
2650
|
+
block_count=4 cstruct_size=1 is_def=true )
|
|
2651
|
+
block_count=3 cstruct_size=1 is_def=true prompt_rank = f"""
|
|
2652
|
+
block_count=3 cstruct_size=1 is_def=true response_rank = llm.invoke(prompt_rank)
|
|
2653
|
+
compo c_name=llm
|
|
2654
|
+
block_count=3 cstruct_size=1 is_def=true content_rank = response_rank.content
|
|
2655
|
+
block_count=3 cstruct_size=1 is_def=true if '```json' in content_rank:
|
|
2656
|
+
block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```json')[1].split('```')[0]
|
|
2657
|
+
block_count=3 cstruct_size=1 is_def=true elif '```' in content_rank:
|
|
2658
|
+
block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```')[1].split('```')[0]
|
|
2659
|
+
block_count=3 cstruct_size=1 is_def=true content_rank = content_rank.strip()
|
|
2660
|
+
block_count=3 cstruct_size=1 is_def=true try:
|
|
2661
|
+
block_count=4 cstruct_size=1 is_def=true ranking_data = json.loads(content_rank)
|
|
2662
|
+
compo c_name=json
|
|
2663
|
+
block_count=3 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
|
|
2664
|
+
block_count=4 cstruct_size=1 is_def=true print(
|
|
2665
|
+
block_count=5 cstruct_size=1 is_def=true f' [Warning] Failed to parse ranking JSON. Error: {e}. Attempting recovery.'
|
|
2666
|
+
block_count=5 cstruct_size=1 is_def=true )
|
|
2667
|
+
block_count=4 cstruct_size=1 is_def=true start_idx = content_rank.find('[')
|
|
2668
|
+
block_count=4 cstruct_size=1 is_def=true if start_idx != -1:
|
|
2669
|
+
block_count=5 cstruct_size=1 is_def=true clean_content = content_rank[start_idx:]
|
|
2670
|
+
block_count=5 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
|
|
2671
|
+
block_count=6 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
|
|
2672
|
+
block_count=6 cstruct_size=1 is_def=true if last_brace != -1:
|
|
2673
|
+
block_count=7 cstruct_size=1 is_def=true clean_content = clean_content[:last_brace + 1
|
|
2674
|
+
block_count=8 cstruct_size=1 is_def=true ] + ']'
|
|
2675
|
+
block_count=6 cstruct_size=1 is_def=true else:
|
|
2676
|
+
block_count=7 cstruct_size=1 is_def=true clean_content += ']'
|
|
2677
|
+
block_count=5 cstruct_size=1 is_def=true try:
|
|
2678
|
+
block_count=6 cstruct_size=1 is_def=true ranking_data = json.loads(clean_content)
|
|
2679
|
+
compo c_name=json
|
|
2680
|
+
block_count=5 cstruct_size=1 is_def=true except Exception as e2:
|
|
2681
|
+
block_count=6 cstruct_size=1 is_def=true print(
|
|
2682
|
+
block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover ranking JSON: {e2}'
|
|
2683
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
2684
|
+
block_count=6 cstruct_size=1 is_def=true return (
|
|
2685
|
+
block_count=7 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
|
|
2686
|
+
block_count=7 cstruct_size=1 is_def=true )
|
|
2687
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
2688
|
+
block_count=5 cstruct_size=1 is_def=true return (
|
|
2689
|
+
block_count=6 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
|
|
2690
|
+
block_count=6 cstruct_size=1 is_def=true )
|
|
2691
|
+
block_count=3 cstruct_size=1 is_def=true spec_dict = {item['id']: item for item in extracted_specs}
|
|
2692
|
+
block_count=3 cstruct_size=1 is_def=true target_dict = {item['id']: item for item in target_products}
|
|
2693
|
+
block_count=3 cstruct_size=1 is_def=true final_comparison_data = []
|
|
2694
|
+
block_count=3 cstruct_size=1 is_def=true for rank_item in ranking_data:
|
|
2695
|
+
block_count=4 cstruct_size=1 is_def=true p_id = rank_item.get('id')
|
|
2696
|
+
block_count=4 cstruct_size=1 is_def=true if p_id in spec_dict:
|
|
2697
|
+
block_count=5 cstruct_size=1 is_def=true merged = spec_dict[p_id].copy()
|
|
2698
|
+
block_count=5 cstruct_size=1 is_def=true merged['rank'] = rank_item.get('rank')
|
|
2699
|
+
block_count=5 cstruct_size=1 is_def=true merged['note'] = rank_item.get('note', '')
|
|
2700
|
+
block_count=5 cstruct_size=1 is_def=true if p_id in target_dict:
|
|
2701
|
+
block_count=6 cstruct_size=1 is_def=true merged['updated_at'] = target_dict[p_id].get(
|
|
2702
|
+
block_count=7 cstruct_size=1 is_def=true 'updated_at', '')
|
|
2703
|
+
block_count=5 cstruct_size=1 is_def=true final_comparison_data.append(merged)
|
|
2704
|
+
block_count=3 cstruct_size=1 is_def=true final_comparison_data.sort(key=lambda x: x.get('rank', 9999))
|
|
2705
|
+
block_count=3 cstruct_size=1 is_def=true for item in final_comparison_data:
|
|
2706
|
+
block_count=4 cstruct_size=1 is_def=true if 'model_number' not in item or item['model_number'] is None:
|
|
2707
|
+
block_count=5 cstruct_size=1 is_def=true item['model_number'] = ''
|
|
2708
|
+
block_count=4 cstruct_size=1 is_def=true if 'release_date' not in item or item['release_date'] is None:
|
|
2709
|
+
block_count=5 cstruct_size=1 is_def=true item['release_date'] = ''
|
|
2710
|
+
block_count=3 cstruct_size=1 is_def=true from datetime import datetime
|
|
2711
|
+
block_count=3 cstruct_size=1 is_def=true output_data = {'updated_at': datetime.now().strftime(
|
|
2712
|
+
compo c_name=datetime
|
|
2713
|
+
block_count=4 cstruct_size=1 is_def=true '%Y-%m-%d %H:%M:%S'), 'products': final_comparison_data}
|
|
2714
|
+
block_count=3 cstruct_size=1 is_def=true output_file = 'product_comparison.json'
|
|
2715
|
+
block_count=3 cstruct_size=1 is_def=true with open(output_file, 'w', encoding='utf-8') as f:
|
|
2716
|
+
block_count=4 cstruct_size=1 is_def=true json.dump(output_data, f, ensure_ascii=False, indent=2)
|
|
2717
|
+
compo c_name=json
|
|
2718
|
+
block_count=3 cstruct_size=1 is_def=true return (
|
|
2719
|
+
block_count=4 cstruct_size=1 is_def=true f'Comparison table generated and saved to {output_file}. Included {len(final_comparison_data)} ranked items.'
|
|
2720
|
+
block_count=4 cstruct_size=1 is_def=true )
|
|
2721
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
2722
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error generating comparison: {e}'
|
|
2723
|
+
block_count=0 cstruct_size=1 is_def=true def display_products():
|
|
2724
|
+
end of test_script.CompareProductsTool
|
|
2725
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
2726
|
+
compo c_name=sqlite3
|
|
2727
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
2728
|
+
compo c_name=conn
|
|
2729
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute('SELECT id, name, store, price FROM products')
|
|
2730
|
+
compo c_name=cursor
|
|
2731
|
+
block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
|
|
2732
|
+
compo c_name=cursor
|
|
2733
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
2734
|
+
compo c_name=conn
|
|
2735
|
+
block_count=1 cstruct_size=0 is_def=true if not rows:
|
|
2736
|
+
block_count=2 cstruct_size=0 is_def=true print('\nNo products saved yet.')
|
|
2737
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
2738
|
+
block_count=1 cstruct_size=0 is_def=true def parse_price_sort(p_str):
|
|
2739
|
+
block_count=2 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(p_str).replace(',', ''))
|
|
2740
|
+
compo c_name=re
|
|
2741
|
+
block_count=2 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
|
|
2742
|
+
block_count=1 cstruct_size=0 is_def=true rows.sort(key=lambda x: (x[1], parse_price_sort(x[3])))
|
|
2743
|
+
compo c_name=rows
|
|
2744
|
+
block_count=1 cstruct_size=0 is_def=false print('\n--- All Saved Products ---')
|
|
2745
|
+
block_count=1 cstruct_size=0 is_def=false print(f"{'ID':<5} {'Name':<40} {'Store':<20} {'Price':<15}")
|
|
2746
|
+
block_count=1 cstruct_size=0 is_def=false print('-' * 85)
|
|
2747
|
+
block_count=1 cstruct_size=0 is_def=false for row in rows:
|
|
2748
|
+
block_count=2 cstruct_size=0 is_def=false name_disp = row[1][:37] + '..' if len(row[1]) > 39 else row[1]
|
|
2749
|
+
block_count=2 cstruct_size=0 is_def=false store_disp = row[2][:18] + '..' if len(row[2]) > 20 else row[2]
|
|
2750
|
+
block_count=2 cstruct_size=0 is_def=false print(f'{row[0]:<5} {name_disp:<40} {store_disp:<20} {row[3]:<15}')
|
|
2751
|
+
block_count=1 cstruct_size=0 is_def=false print('-' * 85)
|
|
2752
|
+
block_count=0 cstruct_size=0 is_def=false def show_product_details(product_id):
|
|
2753
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
2754
|
+
compo c_name=sqlite3
|
|
2755
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
2756
|
+
compo c_name=conn
|
|
2757
|
+
block_count=1 cstruct_size=0 is_def=true cursor.execute(
|
|
2758
|
+
compo c_name=cursor
|
|
2759
|
+
block_count=2 cstruct_size=0 is_def=true 'SELECT id, name, store, price, url, description, model_number, release_date FROM products WHERE id = ?'
|
|
2760
|
+
block_count=2 cstruct_size=0 is_def=true , (product_id,))
|
|
2761
|
+
block_count=1 cstruct_size=0 is_def=true row = cursor.fetchone()
|
|
2762
|
+
compo c_name=cursor
|
|
2763
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
2764
|
+
compo c_name=conn
|
|
2765
|
+
block_count=1 cstruct_size=0 is_def=true if not row:
|
|
2766
|
+
block_count=2 cstruct_size=0 is_def=true print(f'\nProduct with ID {product_id} not found.')
|
|
2767
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
2768
|
+
block_count=1 cstruct_size=0 is_def=true print('\n--- Product Details ---')
|
|
2769
|
+
block_count=1 cstruct_size=0 is_def=true print(f'ID: {row[0]}')
|
|
2770
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Name: {row[1]}')
|
|
2771
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Store: {row[2]}')
|
|
2772
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Price: {row[3]}')
|
|
2773
|
+
block_count=1 cstruct_size=0 is_def=true print(f"Model: {row[6] if len(row) > 6 else ''}")
|
|
2774
|
+
block_count=1 cstruct_size=0 is_def=true print(f"Release: {row[7] if len(row) > 7 else ''}")
|
|
2775
|
+
block_count=1 cstruct_size=0 is_def=true print(f'URL: {row[4]}')
|
|
2776
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Description: {row[5]}')
|
|
2777
|
+
block_count=1 cstruct_size=0 is_def=true print('-' * 30)
|
|
2778
|
+
block_count=0 cstruct_size=0 is_def=true def delete_product_records(identifiers: List[str]):
|
|
2779
|
+
block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
|
|
2780
|
+
compo c_name=sqlite3
|
|
2781
|
+
block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
|
|
2782
|
+
compo c_name=conn
|
|
2783
|
+
block_count=1 cstruct_size=0 is_def=true deleted_count = 0
|
|
2784
|
+
block_count=1 cstruct_size=0 is_def=true errors = []
|
|
2785
|
+
block_count=1 cstruct_size=0 is_def=true print(f'\nAttempting to delete: {identifiers}')
|
|
2786
|
+
block_count=1 cstruct_size=0 is_def=true for identifier in identifiers:
|
|
2787
|
+
block_count=2 cstruct_size=0 is_def=true try:
|
|
2788
|
+
block_count=3 cstruct_size=0 is_def=true if identifier.isdigit():
|
|
2789
|
+
compo c_name=identifier
|
|
2790
|
+
block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (int(
|
|
2791
|
+
compo c_name=cursor
|
|
2792
|
+
block_count=5 cstruct_size=0 is_def=true identifier),))
|
|
2793
|
+
block_count=3 cstruct_size=0 is_def=true else:
|
|
2794
|
+
block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (
|
|
2795
|
+
compo c_name=cursor
|
|
2796
|
+
block_count=5 cstruct_size=0 is_def=true identifier,))
|
|
2797
|
+
block_count=3 cstruct_size=0 is_def=true if cursor.rowcount > 0:
|
|
2798
|
+
compo c_name=cursor
|
|
2799
|
+
block_count=4 cstruct_size=0 is_def=true deleted_count += cursor.rowcount
|
|
2800
|
+
compo c_name=cursor
|
|
2801
|
+
block_count=4 cstruct_size=0 is_def=true print(f' Deleted: {identifier}')
|
|
2802
|
+
block_count=3 cstruct_size=0 is_def=true else:
|
|
2803
|
+
block_count=4 cstruct_size=0 is_def=true errors.append(f'No product found with ID/Name: {identifier}')
|
|
2804
|
+
compo c_name=errors
|
|
2805
|
+
block_count=2 cstruct_size=0 is_def=true except Exception as e:
|
|
2806
|
+
block_count=3 cstruct_size=0 is_def=true errors.append(f'Error deleting {identifier}: {e}')
|
|
2807
|
+
compo c_name=errors
|
|
2808
|
+
block_count=1 cstruct_size=0 is_def=true conn.commit()
|
|
2809
|
+
compo c_name=conn
|
|
2810
|
+
block_count=1 cstruct_size=0 is_def=true conn.close()
|
|
2811
|
+
compo c_name=conn
|
|
2812
|
+
block_count=1 cstruct_size=0 is_def=true print(f'\nTotal deleted: {deleted_count}')
|
|
2813
|
+
block_count=1 cstruct_size=0 is_def=true if errors:
|
|
2814
|
+
block_count=2 cstruct_size=0 is_def=true print('Errors/Warnings:')
|
|
2815
|
+
block_count=2 cstruct_size=0 is_def=true for err in errors:
|
|
2816
|
+
block_count=3 cstruct_size=0 is_def=true print(f' - {err}')
|
|
2817
|
+
block_count=0 cstruct_size=0 is_def=true def main():
|
|
2818
|
+
block_count=1 cstruct_size=0 is_def=true if not os.getenv('GOOGLE_API_KEY'):
|
|
2819
|
+
compo c_name=os
|
|
2820
|
+
block_count=2 cstruct_size=0 is_def=true print('Error: GOOGLE_API_KEY not found.')
|
|
2821
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
2822
|
+
block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
|
|
2823
|
+
compo c_name=os
|
|
2824
|
+
block_count=1 cstruct_size=0 is_def=true if provider == 'serpapi' and not os.getenv('SERPAPI_API_KEY'):
|
|
2825
|
+
compo c_name=os
|
|
2826
|
+
block_count=2 cstruct_size=0 is_def=true print('Error: SERPAPI_API_KEY not found.')
|
|
2827
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
2828
|
+
block_count=1 cstruct_size=0 is_def=true elif provider == 'tavily_api' and not os.getenv('TAVILY_API_KEY'):
|
|
2829
|
+
compo c_name=os
|
|
2830
|
+
block_count=2 cstruct_size=0 is_def=true print('Error: TAVILY_API_KEY not found for Tavily search.')
|
|
2831
|
+
block_count=2 cstruct_size=0 is_def=true return
|
|
2832
|
+
block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
|
|
2833
|
+
block_count=2 cstruct_size=0 is_def=true pass
|
|
2834
|
+
block_count=1 cstruct_size=0 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
|
|
2835
|
+
compo c_name=os
|
|
2836
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Using model: {model_name}')
|
|
2837
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Using Search Provider: {provider}')
|
|
2838
|
+
block_count=1 cstruct_size=0 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
|
|
2839
|
+
compo c_name=ChatGoogleGenerativeAI
|
|
2840
|
+
block_count=2 cstruct_size=0 is_def=true max_retries=10)
|
|
2841
|
+
block_count=1 cstruct_size=0 is_def=true search = get_search_tool_func()
|
|
2842
|
+
block_count=1 cstruct_size=0 is_def=true search_tool = Tool(name='google_search', description=
|
|
2843
|
+
compo c_name=Tool
|
|
2844
|
+
block_count=2 cstruct_size=0 is_def=true 'Search Google for recent results.', func=search.run)
|
|
2845
|
+
block_count=1 cstruct_size=0 is_def=true startup_logs = get_all_agent_logs()
|
|
2846
|
+
block_count=1 cstruct_size=0 is_def=true print(f'Loaded {len(startup_logs)} characters of agent logs.')
|
|
2847
|
+
block_count=1 cstruct_size=0 is_def=true save_tool = SaveProductTool()
|
|
2848
|
+
compo c_name=SaveProductTool
|
|
2849
|
+
block_count=1 cstruct_size=0 is_def=true db_search_tool = SearchProductsTool()
|
|
2850
|
+
compo c_name=SearchProductsTool
|
|
2851
|
+
block_count=1 cstruct_size=0 is_def=true update_tool = UpdatePricesTool()
|
|
2852
|
+
compo c_name=UpdatePricesTool
|
|
2853
|
+
block_count=1 cstruct_size=0 is_def=true similar_tool = FindSimilarProductsTool(agent_logs=startup_logs)
|
|
2854
|
+
compo c_name=FindSimilarProductsTool
|
|
2855
|
+
block_count=1 cstruct_size=0 is_def=true compare_tool = CompareProductsTool()
|
|
2856
|
+
compo c_name=CompareProductsTool
|
|
2857
|
+
block_count=1 cstruct_size=0 is_def=true tools = [search_tool, save_tool, db_search_tool, update_tool,
|
|
2858
|
+
block_count=2 cstruct_size=0 is_def=true similar_tool, compare_tool]
|
|
2859
|
+
block_count=1 cstruct_size=0 is_def=true prompt = ChatPromptTemplate.from_messages([('system',
|
|
2860
|
+
compo c_name=ChatPromptTemplate
|
|
2861
|
+
block_count=2 cstruct_size=0 is_def=true """あなたは、商品の検索、保存、価格更新、類似商品検索、比較表作成を行う有能なアシスタントです。
|
|
2862
|
+
block_count=2 cstruct_size=0 is_def=true ), MessagesPlaceholder(variable_name='chat_history'), ('human',
|
|
2863
|
+
compo c_name=MessagesPlaceholder
|
|
2864
|
+
block_count=2 cstruct_size=0 is_def=true '{input}'), ('placeholder', '{agent_scratchpad}')])
|
|
2865
|
+
block_count=1 cstruct_size=0 is_def=true agent = create_tool_calling_agent(llm, tools, prompt)
|
|
2866
|
+
block_count=1 cstruct_size=0 is_def=true agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True,
|
|
2867
|
+
compo c_name=AgentExecutor
|
|
2868
|
+
block_count=2 cstruct_size=0 is_def=true return_intermediate_steps=True)
|
|
2869
|
+
block_count=1 cstruct_size=0 is_def=true print('Advanced AI Agent initialized.')
|
|
2870
|
+
block_count=1 cstruct_size=0 is_def=true print(
|
|
2871
|
+
block_count=2 cstruct_size=0 is_def=true "Commands: 'list', 'show <ID>', 'update', 'similar', 'delete <ID/Name> ...', 'quit'"
|
|
2872
|
+
block_count=2 cstruct_size=0 is_def=true )
|
|
2873
|
+
block_count=1 cstruct_size=0 is_def=true chat_history = []
|
|
2874
|
+
block_count=1 cstruct_size=0 is_def=true while True:
|
|
2875
|
+
block_count=2 cstruct_size=0 is_def=true try:
|
|
2876
|
+
block_count=3 cstruct_size=0 is_def=true try:
|
|
2877
|
+
block_count=4 cstruct_size=0 is_def=true user_input = input('\nEnter command or search query: ')
|
|
2878
|
+
block_count=3 cstruct_size=0 is_def=true except EOFError:
|
|
2879
|
+
block_count=4 cstruct_size=0 is_def=true print('\nEOF detected. Exiting...')
|
|
2880
|
+
block_count=4 cstruct_size=0 is_def=true break
|
|
2881
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() in ['quit', 'exit']:
|
|
2882
|
+
block_count=4 cstruct_size=0 is_def=true break
|
|
2883
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'list':
|
|
2884
|
+
block_count=4 cstruct_size=0 is_def=true display_products()
|
|
2885
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2886
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'update':
|
|
2887
|
+
block_count=4 cstruct_size=0 is_def=true result = update_tool._run()
|
|
2888
|
+
block_count=4 cstruct_size=0 is_def=true print(result)
|
|
2889
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2890
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'similar':
|
|
2891
|
+
block_count=4 cstruct_size=0 is_def=true result = similar_tool._run()
|
|
2892
|
+
block_count=4 cstruct_size=0 is_def=true print(result)
|
|
2893
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2894
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('compare'):
|
|
2895
|
+
block_count=4 cstruct_size=0 is_def=true parts = user_input.split(maxsplit=1)
|
|
2896
|
+
block_count=4 cstruct_size=0 is_def=true query = parts[1] if len(parts) > 1 else ''
|
|
2897
|
+
block_count=4 cstruct_size=0 is_def=true result = compare_tool._run(query)
|
|
2898
|
+
block_count=4 cstruct_size=0 is_def=true print(result)
|
|
2899
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2900
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('search_products '):
|
|
2901
|
+
block_count=4 cstruct_size=0 is_def=true query = user_input[16:].strip()
|
|
2902
|
+
block_count=4 cstruct_size=0 is_def=true if query:
|
|
2903
|
+
block_count=5 cstruct_size=0 is_def=true result = db_search_tool._run(query)
|
|
2904
|
+
block_count=5 cstruct_size=0 is_def=true print(result)
|
|
2905
|
+
block_count=4 cstruct_size=0 is_def=true else:
|
|
2906
|
+
block_count=5 cstruct_size=0 is_def=true print('Usage: search_products <query>')
|
|
2907
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2908
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('show '):
|
|
2909
|
+
block_count=4 cstruct_size=0 is_def=true parts = user_input.split()
|
|
2910
|
+
block_count=4 cstruct_size=0 is_def=true if len(parts) > 1 and parts[1].isdigit():
|
|
2911
|
+
block_count=5 cstruct_size=0 is_def=true show_product_details(int(parts[1]))
|
|
2912
|
+
block_count=4 cstruct_size=0 is_def=true else:
|
|
2913
|
+
block_count=5 cstruct_size=0 is_def=true print('Usage: show <product_id>')
|
|
2914
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2915
|
+
block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('delete '):
|
|
2916
|
+
block_count=4 cstruct_size=0 is_def=true try:
|
|
2917
|
+
block_count=5 cstruct_size=0 is_def=true parts = shlex.split(user_input)
|
|
2918
|
+
compo c_name=shlex
|
|
2919
|
+
block_count=5 cstruct_size=0 is_def=true if len(parts) > 1:
|
|
2920
|
+
block_count=6 cstruct_size=0 is_def=true identifiers = parts[1:]
|
|
2921
|
+
block_count=6 cstruct_size=0 is_def=true delete_product_records(identifiers)
|
|
2922
|
+
block_count=5 cstruct_size=0 is_def=true else:
|
|
2923
|
+
block_count=6 cstruct_size=0 is_def=true print('Usage: delete <ID/Name> ...')
|
|
2924
|
+
block_count=4 cstruct_size=0 is_def=true except ValueError as e:
|
|
2925
|
+
block_count=5 cstruct_size=0 is_def=true print(f'Error parsing command: {e}')
|
|
2926
|
+
block_count=4 cstruct_size=0 is_def=true continue
|
|
2927
|
+
block_count=3 cstruct_size=0 is_def=true if user_input:
|
|
2928
|
+
block_count=4 cstruct_size=0 is_def=true print(f'\nProcessing: {user_input}...\n')
|
|
2929
|
+
block_count=4 cstruct_size=0 is_def=true result = agent_executor.invoke({'input': user_input,
|
|
2930
|
+
block_count=5 cstruct_size=0 is_def=true 'chat_history': chat_history})
|
|
2931
|
+
block_count=4 cstruct_size=0 is_def=true chat_history.append(HumanMessage(content=user_input))
|
|
2932
|
+
compo c_name=HumanMessage
|
|
2933
|
+
block_count=4 cstruct_size=0 is_def=true if isinstance(result['output'], str):
|
|
2934
|
+
block_count=5 cstruct_size=0 is_def=true chat_history.append(AIMessage(content=result['output']))
|
|
2935
|
+
compo c_name=AIMessage
|
|
2936
|
+
block_count=4 cstruct_size=0 is_def=true if 'intermediate_steps' in result:
|
|
2937
|
+
block_count=5 cstruct_size=0 is_def=true save_agent_log(user_input, result['intermediate_steps'])
|
|
2938
|
+
block_count=4 cstruct_size=0 is_def=true display_products()
|
|
2939
|
+
block_count=2 cstruct_size=0 is_def=true except KeyboardInterrupt:
|
|
2940
|
+
block_count=3 cstruct_size=0 is_def=true print('\nExiting...')
|
|
2941
|
+
block_count=3 cstruct_size=0 is_def=true break
|
|
2942
|
+
block_count=2 cstruct_size=0 is_def=true except Exception as e:
|
|
2943
|
+
block_count=3 cstruct_size=0 is_def=true print(f'An error occurred: {e}')
|
|
2944
|
+
block_count=0 cstruct_size=0 is_def=true if __name__ == '__main__':
|
|
2945
|
+
block_count=1 cstruct_size=0 is_def=false main()
|
|
2946
|
+
endf of ./test_script.py
|
|
2947
|
+
./user_code.py
|
|
2948
|
+
|
|
2949
|
+
|python3 lib/del_comment.py ./user_code.py > /tmp/pylint20260323-827-ywofka
|
|
2950
|
+
block_count=0 cstruct_size=0 is_def=false import json
|
|
2951
|
+
block_count=0 cstruct_size=0 is_def=false import sqlite3
|
|
2952
|
+
block_count=0 cstruct_size=0 is_def=false import re
|
|
2953
|
+
block_count=0 cstruct_size=0 is_def=false from pydantic import BaseModel, Field
|
|
2954
|
+
block_count=0 cstruct_size=0 is_def=false class SaveProductTool:
|
|
2955
|
+
class_name=user_code.SaveProductTool
|
|
2956
|
+
base_name=
|
|
2957
|
+
block_count=1 cstruct_size=1 is_def=false def _run(self, name: str, store: str=None, price: str=None, url: str='',
|
|
2958
|
+
block_count=2 cstruct_size=1 is_def=true description: str='', model_number: str='', release_date: str='',
|
|
2959
|
+
block_count=2 cstruct_size=1 is_def=true ram: str='', ssd: str='', **kwargs):
|
|
2960
|
+
block_count=2 cstruct_size=1 is_def=true try:
|
|
2961
|
+
block_count=3 cstruct_size=1 is_def=true if should_save:
|
|
2962
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute(
|
|
2963
|
+
compo c_name=cursor
|
|
2964
|
+
block_count=5 cstruct_size=1 is_def=true """
|
|
2965
|
+
block_count=5 cstruct_size=1 is_def=true , (name, store, price, url, description, model_number,
|
|
2966
|
+
block_count=5 cstruct_size=1 is_def=true release_date, ram, ssd))
|
|
2967
|
+
block_count=4 cstruct_size=1 is_def=true if not msg:
|
|
2968
|
+
block_count=5 cstruct_size=1 is_def=true msg = f'Saved product: {name} from {store} for {price}.'
|
|
2969
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
2970
|
+
block_count=5 cstruct_size=1 is_def=true msg = msg_prefix
|
|
2971
|
+
block_count=3 cstruct_size=1 is_def=true if should_update:
|
|
2972
|
+
block_count=4 cstruct_size=1 is_def=true if price != curr_price_str:
|
|
2973
|
+
block_count=5 cstruct_size=1 is_def=true cursor.execute(
|
|
2974
|
+
compo c_name=cursor
|
|
2975
|
+
block_count=6 cstruct_size=1 is_def=true """
|
|
2976
|
+
block_count=6 cstruct_size=1 is_def=true , (price, url, description, final_model,
|
|
2977
|
+
block_count=6 cstruct_size=1 is_def=true final_release, final_ram, final_ssd, store,
|
|
2978
|
+
block_count=6 cstruct_size=1 is_def=true current_cheapest['id']))
|
|
2979
|
+
block_count=5 cstruct_size=1 is_def=true if not msg:
|
|
2980
|
+
block_count=6 cstruct_size=1 is_def=true msg = f'Updated product {name} info.'
|
|
2981
|
+
block_count=5 cstruct_size=1 is_def=true else:
|
|
2982
|
+
block_count=6 cstruct_size=1 is_def=true msg = msg_prefix
|
|
2983
|
+
block_count=4 cstruct_size=1 is_def=true else:
|
|
2984
|
+
block_count=5 cstruct_size=1 is_def=true msg = f'No changes for {name} at {store}.'
|
|
2985
|
+
block_count=3 cstruct_size=1 is_def=true if should_save or should_update:
|
|
2986
|
+
block_count=4 cstruct_size=1 is_def=true cursor.execute('SELECT id, price FROM products WHERE name = ?',
|
|
2987
|
+
compo c_name=cursor
|
|
2988
|
+
block_count=5 cstruct_size=1 is_def=true (name,))
|
|
2989
|
+
block_count=4 cstruct_size=1 is_def=true rows = cursor.fetchall()
|
|
2990
|
+
compo c_name=cursor
|
|
2991
|
+
block_count=4 cstruct_size=1 is_def=true if len(rows) > 1:
|
|
2992
|
+
block_count=5 cstruct_size=1 is_def=true rows_parsed = []
|
|
2993
|
+
block_count=5 cstruct_size=1 is_def=true for r in rows:
|
|
2994
|
+
block_count=6 cstruct_size=1 is_def=true rows_parsed.append({'id': r[0], 'val':
|
|
2995
|
+
block_count=7 cstruct_size=1 is_def=true parse_price_val(r[1])})
|
|
2996
|
+
block_count=5 cstruct_size=1 is_def=true rows_parsed.sort(key=lambda x: x['val'])
|
|
2997
|
+
block_count=5 cstruct_size=1 is_def=true winner = rows_parsed[0]
|
|
2998
|
+
block_count=5 cstruct_size=1 is_def=true for loser in rows_parsed[1:]:
|
|
2999
|
+
block_count=6 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?',
|
|
3000
|
+
compo c_name=cursor
|
|
3001
|
+
block_count=7 cstruct_size=1 is_def=true (loser['id'],))
|
|
3002
|
+
block_count=5 cstruct_size=1 is_def=true msg += ' (Cleaned up duplicate records)'
|
|
3003
|
+
block_count=3 cstruct_size=1 is_def=true conn.commit()
|
|
3004
|
+
compo c_name=conn
|
|
3005
|
+
block_count=3 cstruct_size=1 is_def=true conn.close()
|
|
3006
|
+
compo c_name=conn
|
|
3007
|
+
block_count=3 cstruct_size=1 is_def=true return msg
|
|
3008
|
+
block_count=2 cstruct_size=1 is_def=true except Exception as e:
|
|
3009
|
+
block_count=3 cstruct_size=1 is_def=true return f'Error saving product: {str(e)}'
|
|
3010
|
+
endf of ./user_code.py
|
|
3011
|
+
end of user_code.SaveProductTool
|
|
3012
|
+
m=agent.SaveProductTool
|
|
3013
|
+
m=agent.SaveProductTool
|
|
3014
|
+
m=test_script.SaveProductTool
|
|
3015
|
+
m=test_script.SaveProductTool
|
|
3016
|
+
m=user_code.SaveProductTool
|
|
3017
|
+
m=user_code.SaveProductTool
|
|
3018
|
+
m=agent.TavilySearchWrapper
|
|
3019
|
+
m=agent.TavilySearchWrapper
|
|
3020
|
+
m=test_script.TavilySearchWrapper
|
|
3021
|
+
m=test_script.TavilySearchWrapper
|
|
3022
|
+
m=agent.BrowserUseSearchWrapper
|
|
3023
|
+
m=agent.BrowserUseSearchWrapper
|
|
3024
|
+
m=test_script.BrowserUseSearchWrapper
|
|
3025
|
+
m=test_script.BrowserUseSearchWrapper
|
|
3026
|
+
m=agent.SaveProductTool
|
|
3027
|
+
m=agent.SaveProductTool
|
|
3028
|
+
m=test_script.SaveProductTool
|
|
3029
|
+
m=test_script.SaveProductTool
|
|
3030
|
+
m=user_code.SaveProductTool
|
|
3031
|
+
m=user_code.SaveProductTool
|
|
3032
|
+
m=agent.SearchProductsTool
|
|
3033
|
+
m=agent.SearchProductsTool
|
|
3034
|
+
m=test_script.SearchProductsTool
|
|
3035
|
+
m=test_script.SearchProductsTool
|
|
3036
|
+
m=agent.UpdatePricesTool
|
|
3037
|
+
m=agent.UpdatePricesTool
|
|
3038
|
+
m=test_script.UpdatePricesTool
|
|
3039
|
+
m=test_script.UpdatePricesTool
|
|
3040
|
+
m=agent.FindSimilarProductsTool
|
|
3041
|
+
m=agent.FindSimilarProductsTool
|
|
3042
|
+
m=test_script.FindSimilarProductsTool
|
|
3043
|
+
m=test_script.FindSimilarProductsTool
|
|
3044
|
+
m=agent.CompareProductsTool
|
|
3045
|
+
m=agent.CompareProductsTool
|
|
3046
|
+
m=test_script.CompareProductsTool
|
|
3047
|
+
m=test_script.CompareProductsTool
|
|
3048
|
+
m=agent.SaveProductTool
|
|
3049
|
+
m=agent.SaveProductTool
|
|
3050
|
+
m=test_script.SaveProductTool
|
|
3051
|
+
m=test_script.SaveProductTool
|
|
3052
|
+
m=user_code.SaveProductTool
|
|
3053
|
+
m=user_code.SaveProductTool
|
|
3054
|
+
m=agent.TavilySearchWrapper
|
|
3055
|
+
m=agent.TavilySearchWrapper
|
|
3056
|
+
m=test_script.TavilySearchWrapper
|
|
3057
|
+
m=test_script.TavilySearchWrapper
|
|
3058
|
+
m=agent.BrowserUseSearchWrapper
|
|
3059
|
+
m=agent.BrowserUseSearchWrapper
|
|
3060
|
+
m=test_script.BrowserUseSearchWrapper
|
|
3061
|
+
m=test_script.BrowserUseSearchWrapper
|
|
3062
|
+
m=agent.SaveProductTool
|
|
3063
|
+
m=agent.SaveProductTool
|
|
3064
|
+
m=test_script.SaveProductTool
|
|
3065
|
+
m=test_script.SaveProductTool
|
|
3066
|
+
m=user_code.SaveProductTool
|
|
3067
|
+
m=user_code.SaveProductTool
|
|
3068
|
+
m=agent.SearchProductsTool
|
|
3069
|
+
m=agent.SearchProductsTool
|
|
3070
|
+
m=test_script.SearchProductsTool
|
|
3071
|
+
m=test_script.SearchProductsTool
|
|
3072
|
+
m=agent.UpdatePricesTool
|
|
3073
|
+
m=agent.UpdatePricesTool
|
|
3074
|
+
m=test_script.UpdatePricesTool
|
|
3075
|
+
m=test_script.UpdatePricesTool
|
|
3076
|
+
m=agent.FindSimilarProductsTool
|
|
3077
|
+
m=agent.FindSimilarProductsTool
|
|
3078
|
+
m=test_script.FindSimilarProductsTool
|
|
3079
|
+
m=test_script.FindSimilarProductsTool
|
|
3080
|
+
m=agent.CompareProductsTool
|
|
3081
|
+
m=agent.CompareProductsTool
|
|
3082
|
+
m=test_script.CompareProductsTool
|
|
3083
|
+
m=test_script.CompareProductsTool
|