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.
data/test_run.log ADDED
@@ -0,0 +1,3617 @@
1
+ in_dir = .
2
+ ./lib/del_comment.py
3
+
4
+ |python3 lib/del_comment.py ./lib/del_comment.py > /tmp/pylint20260323-801-3zfkts
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
+ block_count=4 cstruct_size=0 is_def=true node.value.s = ''
19
+ compo c_name=node
20
+ block_count=1 cstruct_size=0 is_def=true modified_code = astor.to_source(tree)
21
+ compo c_name=astor
22
+ block_count=1 cstruct_size=0 is_def=true return modified_code
23
+ block_count=0 cstruct_size=0 is_def=true if __name__ == '__main__':
24
+ block_count=1 cstruct_size=0 is_def=false file = sys.argv[1]
25
+ compo c_name=sys
26
+ block_count=1 cstruct_size=0 is_def=false f = open(file, 'r')
27
+ block_count=1 cstruct_size=0 is_def=false code_without_comments = remove_comments_from_code(f.read())
28
+ block_count=1 cstruct_size=0 is_def=false print(code_without_comments)
29
+ endf of ./lib/del_comment.py
30
+ ./test/agent.py
31
+
32
+ |python3 lib/del_comment.py ./test/agent.py > /tmp/pylint20260323-801-pfxpqh
33
+ block_count=0 cstruct_size=0 is_def=false import os
34
+ block_count=0 cstruct_size=0 is_def=false import sqlite3
35
+ block_count=0 cstruct_size=0 is_def=false import re
36
+ block_count=0 cstruct_size=0 is_def=false import json
37
+ block_count=0 cstruct_size=0 is_def=false import time
38
+ block_count=0 cstruct_size=0 is_def=false import shlex
39
+ block_count=0 cstruct_size=0 is_def=false import smtplib
40
+ block_count=0 cstruct_size=0 is_def=false from email.mime.text import MIMEText
41
+ compo c_name=email
42
+ block_count=0 cstruct_size=0 is_def=false from email.mime.multipart import MIMEMultipart
43
+ compo c_name=email
44
+ block_count=0 cstruct_size=0 is_def=false from typing import Optional, Type, Any, List, Dict
45
+ block_count=0 cstruct_size=0 is_def=false import urllib.request
46
+ compo c_name=urllib
47
+ block_count=0 cstruct_size=0 is_def=false import urllib.error
48
+ compo c_name=urllib
49
+ block_count=0 cstruct_size=0 is_def=false from dotenv import load_dotenv
50
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.tools import BaseTool, Tool
51
+ block_count=0 cstruct_size=0 is_def=false from pydantic import BaseModel, Field, model_validator
52
+ block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
53
+ block_count=0 cstruct_size=0 is_def=false from langchain_community.utilities import SerpAPIWrapper
54
+ block_count=0 cstruct_size=0 is_def=false from langchain_community.tools.tavily_search import TavilySearchResults
55
+ block_count=0 cstruct_size=0 is_def=false from googleapiclient.discovery import build
56
+ compo c_name=googleapiclient
57
+ block_count=0 cstruct_size=0 is_def=false import asyncio
58
+ block_count=0 cstruct_size=0 is_def=false from browser_use import Agent, BrowserProfile
59
+ block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
60
+ block_count=0 cstruct_size=0 is_def=false from browser_use.llm.google.chat import ChatGoogle
61
+ block_count=0 cstruct_size=0 is_def=false from langchain.agents import AgentExecutor, create_tool_calling_agent, create_react_agent
62
+ compo c_name=langchain
63
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, MessagesPlaceholder
64
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.output_parsers import JsonOutputParser
65
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.messages import HumanMessage, AIMessage
66
+ block_count=0 cstruct_size=0 is_def=false dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
67
+ compo c_name=os
68
+ block_count=0 cstruct_size=0 is_def=false load_dotenv(dotenv_path, override=True)
69
+ block_count=0 cstruct_size=0 is_def=false DB_NAME = 'products.db'
70
+ block_count=0 cstruct_size=0 is_def=false def init_db():
71
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
72
+ compo c_name=sqlite3
73
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
74
+ compo c_name=conn
75
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
76
+ compo c_name=cursor
77
+ block_count=2 cstruct_size=0 is_def=true """
78
+ block_count=2 cstruct_size=0 is_def=true CREATE TABLE IF NOT EXISTS products (
79
+ block_count=3 cstruct_size=0 is_def=true id INTEGER PRIMARY KEY AUTOINCREMENT,
80
+ block_count=3 cstruct_size=0 is_def=true name TEXT NOT NULL,
81
+ block_count=3 cstruct_size=0 is_def=true store TEXT,
82
+ block_count=3 cstruct_size=0 is_def=true price TEXT,
83
+ block_count=3 cstruct_size=0 is_def=true url TEXT,
84
+ block_count=3 cstruct_size=0 is_def=true description TEXT,
85
+ block_count=3 cstruct_size=0 is_def=true model_number TEXT,
86
+ block_count=3 cstruct_size=0 is_def=true release_date TEXT,
87
+ block_count=3 cstruct_size=0 is_def=true ram TEXT,
88
+ block_count=3 cstruct_size=0 is_def=true ssd TEXT,
89
+ block_count=3 cstruct_size=0 is_def=true updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
90
+ block_count=2 cstruct_size=0 is_def=true )
91
+ block_count=1 cstruct_size=0 is_def=true """
92
+ block_count=2 cstruct_size=0 is_def=true )
93
+ block_count=1 cstruct_size=0 is_def=true try:
94
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN model_number TEXT')
95
+ compo c_name=cursor
96
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
97
+ block_count=2 cstruct_size=0 is_def=true pass
98
+ block_count=1 cstruct_size=0 is_def=true try:
99
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN release_date TEXT')
100
+ compo c_name=cursor
101
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
102
+ block_count=2 cstruct_size=0 is_def=true pass
103
+ block_count=1 cstruct_size=0 is_def=true try:
104
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ram TEXT')
105
+ compo c_name=cursor
106
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
107
+ block_count=2 cstruct_size=0 is_def=true pass
108
+ block_count=1 cstruct_size=0 is_def=true try:
109
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ssd TEXT')
110
+ compo c_name=cursor
111
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
112
+ block_count=2 cstruct_size=0 is_def=true pass
113
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
114
+ compo c_name=cursor
115
+ block_count=2 cstruct_size=0 is_def=true """
116
+ block_count=2 cstruct_size=0 is_def=true CREATE TABLE IF NOT EXISTS agent_logs (
117
+ block_count=3 cstruct_size=0 is_def=true id INTEGER PRIMARY KEY AUTOINCREMENT,
118
+ block_count=3 cstruct_size=0 is_def=true timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
119
+ block_count=3 cstruct_size=0 is_def=true query TEXT,
120
+ block_count=3 cstruct_size=0 is_def=true scratchpad TEXT
121
+ block_count=2 cstruct_size=0 is_def=true )
122
+ block_count=1 cstruct_size=0 is_def=true """
123
+ block_count=2 cstruct_size=0 is_def=true )
124
+ block_count=1 cstruct_size=0 is_def=true conn.commit()
125
+ compo c_name=conn
126
+ block_count=1 cstruct_size=0 is_def=true conn.close()
127
+ compo c_name=conn
128
+ block_count=0 cstruct_size=0 is_def=true init_db()
129
+ block_count=0 cstruct_size=0 is_def=false class TavilySearchWrapper:
130
+ class_name=agent.TavilySearchWrapper
131
+ base_name=
132
+ block_count=1 cstruct_size=1 is_def=false def __init__(self):
133
+ block_count=2 cstruct_size=1 is_def=true self.api_key = os.getenv('TAVILY_API_KEY')
134
+ compo c_name=self
135
+ block_count=2 cstruct_size=1 is_def=true if not self.api_key:
136
+ compo c_name=self
137
+ block_count=3 cstruct_size=1 is_def=true raise ValueError('TAVILY_API_KEY must be set for Tavily Search.')
138
+ compo c_name=ValueError
139
+ block_count=2 cstruct_size=1 is_def=true self.tool = TavilySearchResults(api_key=self.api_key)
140
+ compo c_name=self
141
+ compo c_name=TavilySearchResults
142
+ block_count=1 cstruct_size=1 is_def=true def run(self, query: str) ->str:
143
+ block_count=2 cstruct_size=1 is_def=true try:
144
+ block_count=3 cstruct_size=1 is_def=true results = self.tool.invoke({'query': query})
145
+ compo c_name=self
146
+ block_count=3 cstruct_size=1 is_def=true formatted_results = []
147
+ block_count=3 cstruct_size=1 is_def=true for item in results:
148
+ block_count=4 cstruct_size=1 is_def=true content = item.get('content')
149
+ compo c_name=item
150
+ block_count=4 cstruct_size=1 is_def=true url = item.get('url')
151
+ compo c_name=item
152
+ block_count=4 cstruct_size=1 is_def=true formatted_results.append(f'Content: {content}\nURL: {url}\n')
153
+ block_count=3 cstruct_size=1 is_def=true return '\n'.join(formatted_results
154
+ block_count=4 cstruct_size=1 is_def=true ) if formatted_results else 'No results found.'
155
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
156
+ block_count=3 cstruct_size=1 is_def=true return f'Error during Tavily Search: {e}'
157
+ block_count=0 cstruct_size=1 is_def=true class BrowserUseSearchWrapper:
158
+ end of agent.TavilySearchWrapper
159
+ class_name=agent.BrowserUseSearchWrapper
160
+ base_name=
161
+ block_count=1 cstruct_size=1 is_def=false def __init__(self):
162
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
163
+ compo c_name=os
164
+ block_count=2 cstruct_size=1 is_def=true self.llm = ChatGoogle(model=model_name, api_key=os.getenv(
165
+ compo c_name=self
166
+ compo c_name=ChatGoogle
167
+ block_count=3 cstruct_size=1 is_def=true 'GOOGLE_API_KEY'))
168
+ block_count=2 cstruct_size=1 is_def=true user_agent = (
169
+ 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'
170
+ block_count=3 cstruct_size=1 is_def=true )
171
+ block_count=2 cstruct_size=1 is_def=true self.browser_profile = BrowserProfile(headless=False, user_agent=
172
+ compo c_name=self
173
+ compo c_name=BrowserProfile
174
+ block_count=3 cstruct_size=1 is_def=true user_agent)
175
+ block_count=1 cstruct_size=1 is_def=true async def _search_async(self, query: str) ->str:
176
+ block_count=2 cstruct_size=1 is_def=false task = f"""
177
+ block_count=2 cstruct_size=1 is_def=false Web全体から '{query}' を検索してください。
178
+ block_count=2 cstruct_size=1 is_def=false 検索結果の上位の製品について、以下の情報を確実に抽出してください:
179
+ block_count=2 cstruct_size=1 is_def=false 1. 製品名(タイトル)
180
+ block_count=2 cstruct_size=1 is_def=false 2. 正確な製品ページのURL
181
+ block_count=2 cstruct_size=1 is_def=false 3. 詳細な製品概要(スペックや特徴)
182
+ block_count=2 cstruct_size=1 is_def=false 4. 価格
183
+ block_count=2 cstruct_size=1 is_def=false 5. 型番(モデル番号)
184
+ block_count=2 cstruct_size=1 is_def=false 6. 発売日
185
+ block_count=2 cstruct_size=1 is_def=false 重要:URLと製品概要は必須です。URLは必ず http または https で始まる有効なものを取得してください。
186
+ block_count=2 cstruct_size=1 is_def=false 型番や発売日が見つかる場合はそれらも必ず抽出してください。
187
+ block_count=2 cstruct_size=1 is_def=false 検索結果ページだけでなく、必要であれば個別の製品ページにアクセスして情報を取得してください。
188
+ block_count=2 cstruct_size=1 is_def=false ページが完全に読み込まれるまで待ち、正確な情報を取得するようにしてください。
189
+ block_count=2 cstruct_size=1 is_def=false また、メモリ(RAM)やストレージ(SSDなど)の容量を抽出する際、「最大〇〇GB」「〇〇GBまで増設可能」などと記載されている拡張上限の数値は対象外とし、必ず「標準搭載(初期状態)」の容量を抽出してください。
190
+ block_count=2 cstruct_size=1 is_def=false 【極密事項】
191
+ block_count=2 cstruct_size=1 is_def=false ページ内に以下の文言が含まれている商品は「販売不可」とみなし、絶対に抽出・出力しないでください。
192
+ block_count=2 cstruct_size=1 is_def=false - 「販売終了」
193
+ block_count=2 cstruct_size=1 is_def=false - 「お探しのページは見つかりません」
194
+ block_count=2 cstruct_size=1 is_def=false - 「404 Not Found」
195
+ block_count=2 cstruct_size=1 is_def=false - 「この商品は現在お取り扱いできません」
196
+ block_count=2 cstruct_size=1 is_def=false """
197
+ block_count=2 cstruct_size=1 is_def=false agent = Agent(task=task, llm=self.llm, browser_profile=self.
198
+ compo c_name=Agent
199
+ block_count=3 cstruct_size=1 is_def=false browser_profile)
200
+ block_count=2 cstruct_size=1 is_def=false result = await agent.run()
201
+ compo c_name=agent
202
+ block_count=2 cstruct_size=1 is_def=false return result.final_result()
203
+ compo c_name=result
204
+ block_count=1 cstruct_size=1 is_def=false def run(self, query: str) ->str:
205
+ block_count=2 cstruct_size=1 is_def=true try:
206
+ block_count=3 cstruct_size=1 is_def=true return asyncio.run(self._search_async(query))
207
+ compo c_name=asyncio
208
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
209
+ block_count=3 cstruct_size=1 is_def=true return f'Error during Browser Use Search: {e}'
210
+ block_count=0 cstruct_size=1 is_def=true def get_search_tool_func():
211
+ end of agent.BrowserUseSearchWrapper
212
+ block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
213
+ compo c_name=os
214
+ block_count=1 cstruct_size=0 is_def=true if provider == 'tavily_api':
215
+ block_count=2 cstruct_size=0 is_def=true return TavilySearchWrapper()
216
+ compo c_name=TavilySearchWrapper
217
+ block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
218
+ block_count=2 cstruct_size=0 is_def=true return BrowserUseSearchWrapper()
219
+ compo c_name=BrowserUseSearchWrapper
220
+ block_count=1 cstruct_size=0 is_def=true else:
221
+ block_count=2 cstruct_size=0 is_def=true return SerpAPIWrapper()
222
+ compo c_name=SerpAPIWrapper
223
+ block_count=0 cstruct_size=0 is_def=true def get_all_products():
224
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
225
+ compo c_name=sqlite3
226
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
227
+ compo c_name=conn
228
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
229
+ compo c_name=cursor
230
+ 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'
231
+ block_count=2 cstruct_size=0 is_def=true )
232
+ block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
233
+ compo c_name=cursor
234
+ block_count=1 cstruct_size=0 is_def=true conn.close()
235
+ compo c_name=conn
236
+ block_count=1 cstruct_size=0 is_def=true products = []
237
+ block_count=1 cstruct_size=0 is_def=true for r in rows:
238
+ block_count=2 cstruct_size=0 is_def=true products.append({'id': r[0], 'name': r[1], 'store': r[2], 'price':
239
+ compo c_name=products
240
+ block_count=3 cstruct_size=0 is_def=true r[3], 'url': r[4], 'description': r[5], 'model_number': r[6] if
241
+ block_count=3 cstruct_size=0 is_def=true len(r) > 6 else '', 'release_date': r[7] if len(r) > 7 else '',
242
+ 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
243
+ block_count=3 cstruct_size=0 is_def=true '', 'updated_at': r[10] if len(r) > 10 else ''})
244
+ block_count=1 cstruct_size=0 is_def=true return products
245
+ block_count=0 cstruct_size=0 is_def=true def parse_price_val(p_str):
246
+ block_count=1 cstruct_size=0 is_def=true if not p_str:
247
+ block_count=2 cstruct_size=0 is_def=true return float('inf')
248
+ block_count=1 cstruct_size=0 is_def=true s = str(p_str).replace(',', '')
249
+ block_count=1 cstruct_size=0 is_def=true match_man = re.search('(\\d+(\\.\\d+)?)万', s)
250
+ compo c_name=re
251
+ block_count=1 cstruct_size=0 is_def=true if match_man:
252
+ block_count=2 cstruct_size=0 is_def=true try:
253
+ block_count=3 cstruct_size=0 is_def=true val = float(match_man.group(1)) * 10000
254
+ block_count=3 cstruct_size=0 is_def=true return int(val)
255
+ block_count=2 cstruct_size=0 is_def=true except:
256
+ block_count=3 cstruct_size=0 is_def=true pass
257
+ block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', s)
258
+ compo c_name=re
259
+ block_count=1 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
260
+ block_count=0 cstruct_size=0 is_def=true def extract_alphanumeric(s: str) ->str:
261
+ block_count=1 cstruct_size=0 is_def=true """Extracts only alphanumeric characters from a string and converts to lowercase for robust comparison."""
262
+ block_count=1 cstruct_size=0 is_def=true if not s:
263
+ block_count=2 cstruct_size=0 is_def=true return ''
264
+ block_count=1 cstruct_size=0 is_def=true return re.sub('[^a-zA-Z0-9]', '', str(s)).lower()
265
+ compo c_name=re
266
+ block_count=0 cstruct_size=0 is_def=true def parse_date_val(d_str: str) ->str:
267
+ block_count=1 cstruct_size=0 is_def=true """
268
+ block_count=1 cstruct_size=0 is_def=true Normalizes date strings for comparison.
269
+ block_count=1 cstruct_size=0 is_def=true Examples: '2021年2月' -> '202102', '2021-02' -> '202102'
270
+ block_count=1 cstruct_size=0 is_def=true """
271
+ block_count=1 cstruct_size=0 is_def=true if not d_str:
272
+ block_count=2 cstruct_size=0 is_def=true return ''
273
+ block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(d_str))
274
+ compo c_name=re
275
+ block_count=1 cstruct_size=0 is_def=true if len(nums) >= 2:
276
+ block_count=2 cstruct_size=0 is_def=true year = nums[0]
277
+ block_count=2 cstruct_size=0 is_def=true month = nums[1].zfill(2)
278
+ block_count=2 cstruct_size=0 is_def=true return f'{year}{month}'
279
+ block_count=1 cstruct_size=0 is_def=true elif len(nums) == 1:
280
+ block_count=2 cstruct_size=0 is_def=true return nums[0]
281
+ block_count=1 cstruct_size=0 is_def=true else:
282
+ block_count=2 cstruct_size=0 is_def=true return extract_alphanumeric(d_str)
283
+ block_count=0 cstruct_size=0 is_def=true def is_similar_model(m1: str, m2: str) ->bool:
284
+ block_count=1 cstruct_size=0 is_def=true """
285
+ block_count=1 cstruct_size=0 is_def=true Checks if two model strings are substantially similar.
286
+ block_count=1 cstruct_size=0 is_def=true Considers them similar if the alphanumeric string of one is entirely contained in the other.
287
+ block_count=1 cstruct_size=0 is_def=true e.g., 'dynabookg83hs7n11' and 'g83hs7n11' -> True
288
+ block_count=1 cstruct_size=0 is_def=true """
289
+ block_count=1 cstruct_size=0 is_def=true am1 = extract_alphanumeric(m1)
290
+ block_count=1 cstruct_size=0 is_def=true am2 = extract_alphanumeric(m2)
291
+ block_count=1 cstruct_size=0 is_def=true if not am1 and not am2:
292
+ block_count=2 cstruct_size=0 is_def=true return True
293
+ block_count=1 cstruct_size=0 is_def=true if not am1 or not am2:
294
+ block_count=2 cstruct_size=0 is_def=true return False
295
+ block_count=1 cstruct_size=0 is_def=true return am1 in am2 or am2 in am1
296
+ block_count=0 cstruct_size=0 is_def=true def save_agent_log(query, steps):
297
+ block_count=1 cstruct_size=0 is_def=true """Saves the agent's scratchpad (intermediate steps) to the database."""
298
+ block_count=1 cstruct_size=0 is_def=true if not steps:
299
+ block_count=2 cstruct_size=0 is_def=true return
300
+ block_count=1 cstruct_size=0 is_def=true log_content = []
301
+ block_count=1 cstruct_size=0 is_def=true for action, observation in steps:
302
+ block_count=2 cstruct_size=0 is_def=true if isinstance(action, list):
303
+ block_count=3 cstruct_size=0 is_def=true for a in action:
304
+ block_count=4 cstruct_size=0 is_def=true log_content.append(f'Tool: {a.tool}')
305
+ block_count=4 cstruct_size=0 is_def=true log_content.append(f'Input: {a.tool_input}')
306
+ block_count=4 cstruct_size=0 is_def=true log_content.append(f'Log: {a.log}')
307
+ block_count=2 cstruct_size=0 is_def=true else:
308
+ block_count=3 cstruct_size=0 is_def=true log_content.append(f'Tool: {action.tool}')
309
+ block_count=3 cstruct_size=0 is_def=true log_content.append(f'Input: {action.tool_input}')
310
+ block_count=3 cstruct_size=0 is_def=true log_content.append(f'Log: {action.log}')
311
+ block_count=2 cstruct_size=0 is_def=true log_content.append(f'Observation: {observation}')
312
+ block_count=2 cstruct_size=0 is_def=true log_content.append('-' * 20)
313
+ block_count=1 cstruct_size=0 is_def=true scratchpad_text = '\n'.join(log_content)
314
+ block_count=1 cstruct_size=0 is_def=true try:
315
+ block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
316
+ compo c_name=sqlite3
317
+ block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
318
+ compo c_name=conn
319
+ block_count=2 cstruct_size=0 is_def=true cursor.execute(
320
+ compo c_name=cursor
321
+ block_count=3 cstruct_size=0 is_def=true 'INSERT INTO agent_logs (query, scratchpad) VALUES (?, ?)', (
322
+ block_count=3 cstruct_size=0 is_def=true query, scratchpad_text))
323
+ block_count=2 cstruct_size=0 is_def=true conn.commit()
324
+ compo c_name=conn
325
+ block_count=2 cstruct_size=0 is_def=true conn.close()
326
+ compo c_name=conn
327
+ block_count=2 cstruct_size=0 is_def=true print(f' [Log saved to database]')
328
+ block_count=1 cstruct_size=0 is_def=true except Exception as e:
329
+ block_count=2 cstruct_size=0 is_def=true print(f'Error saving log: {e}')
330
+ block_count=0 cstruct_size=0 is_def=true def get_all_agent_logs():
331
+ block_count=1 cstruct_size=0 is_def=true """Fetches all agent logs from the database."""
332
+ block_count=1 cstruct_size=0 is_def=true try:
333
+ block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
334
+ compo c_name=sqlite3
335
+ block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
336
+ compo c_name=conn
337
+ block_count=2 cstruct_size=0 is_def=true cursor.execute(
338
+ compo c_name=cursor
339
+ block_count=3 cstruct_size=0 is_def=true 'SELECT query, scratchpad FROM agent_logs ORDER BY timestamp DESC')
340
+ block_count=2 cstruct_size=0 is_def=true rows = cursor.fetchall()
341
+ compo c_name=cursor
342
+ block_count=2 cstruct_size=0 is_def=true conn.close()
343
+ compo c_name=conn
344
+ block_count=2 cstruct_size=0 is_def=true logs = []
345
+ block_count=2 cstruct_size=0 is_def=true for r in rows:
346
+ block_count=3 cstruct_size=0 is_def=true logs.append(f'Query: {r[0]}\nLog:\n{r[1]}\n')
347
+ compo c_name=logs
348
+ block_count=2 cstruct_size=0 is_def=true return '\n'.join(logs)
349
+ block_count=1 cstruct_size=0 is_def=true except Exception as e:
350
+ block_count=2 cstruct_size=0 is_def=true print(f'Error fetching logs: {e}')
351
+ block_count=2 cstruct_size=0 is_def=true return ''
352
+ block_count=0 cstruct_size=0 is_def=true def send_email_notification(subject: str, body: str):
353
+ block_count=1 cstruct_size=0 is_def=true """Sends an email notification."""
354
+ block_count=1 cstruct_size=0 is_def=true smtp_server = os.getenv('EMAIL_SMTP_SERVER')
355
+ compo c_name=os
356
+ block_count=1 cstruct_size=0 is_def=true smtp_port = os.getenv('EMAIL_SMTP_PORT')
357
+ compo c_name=os
358
+ block_count=1 cstruct_size=0 is_def=true sender_email = os.getenv('EMAIL_SENDER_ADDRESS')
359
+ compo c_name=os
360
+ block_count=1 cstruct_size=0 is_def=true sender_password = os.getenv('EMAIL_SENDER_PASSWORD')
361
+ compo c_name=os
362
+ block_count=1 cstruct_size=0 is_def=true receiver_email = os.getenv('EMAIL_RECEIVER_ADDRESS')
363
+ compo c_name=os
364
+ block_count=1 cstruct_size=0 is_def=true if not all([smtp_server, smtp_port, sender_email, receiver_email]):
365
+ block_count=2 cstruct_size=0 is_def=true print(
366
+ block_count=3 cstruct_size=0 is_def=true 'Email configuration missing (Server, Port, Sender, Receiver). Skipping notification.'
367
+ block_count=3 cstruct_size=0 is_def=true )
368
+ block_count=2 cstruct_size=0 is_def=true return
369
+ block_count=1 cstruct_size=0 is_def=true try:
370
+ block_count=2 cstruct_size=0 is_def=true msg = MIMEMultipart()
371
+ compo c_name=MIMEMultipart
372
+ block_count=2 cstruct_size=0 is_def=true msg['From'] = sender_email
373
+ block_count=2 cstruct_size=0 is_def=true msg['To'] = receiver_email
374
+ block_count=2 cstruct_size=0 is_def=true msg['Subject'] = subject
375
+ block_count=2 cstruct_size=0 is_def=true msg.attach(MIMEText(body, 'plain'))
376
+ compo c_name=msg
377
+ compo c_name=MIMEText
378
+ block_count=2 cstruct_size=0 is_def=true server = smtplib.SMTP(smtp_server, int(smtp_port))
379
+ compo c_name=SMTP
380
+ block_count=2 cstruct_size=0 is_def=true server.starttls()
381
+ compo c_name=server
382
+ block_count=2 cstruct_size=0 is_def=true if sender_password:
383
+ block_count=3 cstruct_size=0 is_def=true server.login(sender_email, sender_password)
384
+ compo c_name=server
385
+ block_count=2 cstruct_size=0 is_def=true server.send_message(msg)
386
+ compo c_name=server
387
+ block_count=2 cstruct_size=0 is_def=true server.quit()
388
+ compo c_name=server
389
+ block_count=2 cstruct_size=0 is_def=true print(f'Email notification sent: {subject}')
390
+ block_count=1 cstruct_size=0 is_def=true except Exception as e:
391
+ block_count=2 cstruct_size=0 is_def=true print(f'Failed to send email: {e}')
392
+ block_count=0 cstruct_size=0 is_def=true class SaveProductInput(BaseModel):
393
+ class_name=agent.SaveProductInput
394
+ base_name=BaseModel
395
+ block_count=1 cstruct_size=1 is_def=false name: str = Field(description='Name of the product')
396
+ compo c_name=Field
397
+ block_count=1 cstruct_size=1 is_def=false store: str = Field(description='Name of the store selling the product')
398
+ compo c_name=Field
399
+ block_count=1 cstruct_size=1 is_def=false price: str = Field(description='Price of the product')
400
+ compo c_name=Field
401
+ block_count=1 cstruct_size=1 is_def=false url: Optional[str] = Field(description='URL of the product page',
402
+ compo c_name=Field
403
+ block_count=2 cstruct_size=1 is_def=false default='')
404
+ block_count=1 cstruct_size=1 is_def=false description: Optional[str] = Field(description=
405
+ compo c_name=Field
406
+ block_count=2 cstruct_size=1 is_def=false 'Brief description of the product', default='')
407
+ block_count=1 cstruct_size=1 is_def=false model_number: Optional[str] = Field(description=
408
+ compo c_name=Field
409
+ block_count=2 cstruct_size=1 is_def=false 'Model number (型番) of the product', default='')
410
+ block_count=1 cstruct_size=1 is_def=false release_date: Optional[str] = Field(description=
411
+ compo c_name=Field
412
+ block_count=2 cstruct_size=1 is_def=false 'Release date (発売日) of the product', default='')
413
+ block_count=1 cstruct_size=1 is_def=false ram: Optional[str] = Field(description='RAM size', default='')
414
+ compo c_name=Field
415
+ block_count=1 cstruct_size=1 is_def=false ssd: Optional[str] = Field(description='SSD size', default='')
416
+ compo c_name=Field
417
+ block_count=1 cstruct_size=1 is_def=false @model_validator(mode='before')
418
+ block_count=1 cstruct_size=1 is_def=false @classmethod
419
+ block_count=1 cstruct_size=1 is_def=false def parse_json_input(cls, data: Any) ->Any:
420
+ block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict):
421
+ block_count=3 cstruct_size=1 is_def=true if 'name' in data and ('store' not in data or 'price' not in data):
422
+ block_count=4 cstruct_size=1 is_def=true name_val = data['name']
423
+ block_count=4 cstruct_size=1 is_def=true if isinstance(name_val, str) and name_val.strip().startswith(
424
+ block_count=5 cstruct_size=1 is_def=true '{') and name_val.strip().endswith('}'):
425
+ block_count=5 cstruct_size=1 is_def=true try:
426
+ block_count=6 cstruct_size=1 is_def=true parsed = json.loads(name_val)
427
+ compo c_name=json
428
+ block_count=6 cstruct_size=1 is_def=true if isinstance(parsed, dict):
429
+ block_count=7 cstruct_size=1 is_def=true data = parsed
430
+ block_count=5 cstruct_size=1 is_def=true except json.JSONDecodeError:
431
+ block_count=6 cstruct_size=1 is_def=true pass
432
+ block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict) and 'price' in data:
433
+ block_count=3 cstruct_size=1 is_def=true if isinstance(data['price'], (int, float)):
434
+ block_count=4 cstruct_size=1 is_def=true data['price'] = str(data['price'])
435
+ block_count=2 cstruct_size=1 is_def=true return data
436
+ block_count=0 cstruct_size=1 is_def=true class SaveProductTool(BaseTool):
437
+ end of agent.SaveProductInput
438
+ class_name=agent.SaveProductTool
439
+ base_name=BaseTool
440
+ block_count=1 cstruct_size=1 is_def=false name = 'save_product'
441
+ block_count=1 cstruct_size=1 is_def=false description = (
442
+ 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.'
443
+ block_count=2 cstruct_size=1 is_def=false )
444
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SaveProductInput
445
+ block_count=1 cstruct_size=1 is_def=false def _run(self, name: str, store: str=None, price: str=None, url: str='',
446
+ block_count=2 cstruct_size=1 is_def=true description: str='', model_number: str='', release_date: str='',
447
+ block_count=2 cstruct_size=1 is_def=true ram: str='', ssd: str='', **kwargs):
448
+ block_count=2 cstruct_size=1 is_def=true try:
449
+ block_count=3 cstruct_size=1 is_def=true parsed_data = {}
450
+ block_count=3 cstruct_size=1 is_def=true if isinstance(name, dict):
451
+ block_count=4 cstruct_size=1 is_def=true parsed_data = name
452
+ block_count=3 cstruct_size=1 is_def=true elif isinstance(name, str) and name.strip().startswith('{'
453
+ compo c_name=name
454
+ block_count=4 cstruct_size=1 is_def=true ) and name.strip().endswith('}'):
455
+ compo c_name=name
456
+ block_count=4 cstruct_size=1 is_def=true try:
457
+ block_count=5 cstruct_size=1 is_def=true parsed_data = json.loads(name)
458
+ compo c_name=json
459
+ block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError:
460
+ block_count=5 cstruct_size=1 is_def=true pass
461
+ block_count=3 cstruct_size=1 is_def=true if parsed_data:
462
+ block_count=4 cstruct_size=1 is_def=true if 'name' in parsed_data:
463
+ block_count=5 cstruct_size=1 is_def=true name = parsed_data['name']
464
+ block_count=4 cstruct_size=1 is_def=true if store is None:
465
+ block_count=5 cstruct_size=1 is_def=true store = parsed_data.get('store')
466
+ block_count=4 cstruct_size=1 is_def=true if price is None:
467
+ block_count=5 cstruct_size=1 is_def=true price = parsed_data.get('price')
468
+ block_count=4 cstruct_size=1 is_def=true if not url:
469
+ block_count=5 cstruct_size=1 is_def=true url = parsed_data.get('url', '')
470
+ block_count=4 cstruct_size=1 is_def=true if not description:
471
+ block_count=5 cstruct_size=1 is_def=true description = parsed_data.get('description', '')
472
+ block_count=4 cstruct_size=1 is_def=true if not model_number:
473
+ block_count=5 cstruct_size=1 is_def=true model_number = parsed_data.get('model_number', '')
474
+ block_count=4 cstruct_size=1 is_def=true if not release_date:
475
+ block_count=5 cstruct_size=1 is_def=true release_date = parsed_data.get('release_date', '')
476
+ block_count=4 cstruct_size=1 is_def=true if not ram:
477
+ block_count=5 cstruct_size=1 is_def=true ram = parsed_data.get('ram', '')
478
+ block_count=4 cstruct_size=1 is_def=true if not ssd:
479
+ block_count=5 cstruct_size=1 is_def=true ssd = parsed_data.get('ssd', '')
480
+ block_count=3 cstruct_size=1 is_def=true if store is None:
481
+ block_count=4 cstruct_size=1 is_def=true store = kwargs.get('store')
482
+ compo c_name=kwargs
483
+ block_count=3 cstruct_size=1 is_def=true if price is None:
484
+ block_count=4 cstruct_size=1 is_def=true price = kwargs.get('price')
485
+ compo c_name=kwargs
486
+ block_count=3 cstruct_size=1 is_def=true if not model_number:
487
+ block_count=4 cstruct_size=1 is_def=true model_number = kwargs.get('model_number', '')
488
+ compo c_name=kwargs
489
+ block_count=3 cstruct_size=1 is_def=true if not release_date:
490
+ block_count=4 cstruct_size=1 is_def=true release_date = kwargs.get('release_date', '')
491
+ compo c_name=kwargs
492
+ block_count=3 cstruct_size=1 is_def=true if not ram:
493
+ block_count=4 cstruct_size=1 is_def=true ram = kwargs.get('ram', '')
494
+ compo c_name=kwargs
495
+ block_count=3 cstruct_size=1 is_def=true if not ssd:
496
+ block_count=4 cstruct_size=1 is_def=true ssd = kwargs.get('ssd', '')
497
+ compo c_name=kwargs
498
+ block_count=3 cstruct_size=1 is_def=true if not name or not store or not price:
499
+ block_count=4 cstruct_size=1 is_def=true return (
500
+ block_count=5 cstruct_size=1 is_def=true f'Error: Required arguments missing. Name: {name}, Store: {store}, Price: {price}'
501
+ block_count=5 cstruct_size=1 is_def=true )
502
+ block_count=3 cstruct_size=1 is_def=true if not url or not description:
503
+ block_count=4 cstruct_size=1 is_def=true return (
504
+ block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL or description is missing. URL: '{url}', Description: '{description}'"
505
+ block_count=5 cstruct_size=1 is_def=true )
506
+ block_count=3 cstruct_size=1 is_def=true if not url.startswith('http://') and not url.startswith('https://'
507
+ compo c_name=url
508
+ block_count=4 cstruct_size=1 is_def=true ):
509
+ block_count=4 cstruct_size=1 is_def=true return (
510
+ block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL must start with 'http://' or 'https://'. URL: '{url}'"
511
+ block_count=5 cstruct_size=1 is_def=true )
512
+ block_count=3 cstruct_size=1 is_def=true if isinstance(price, (int, float)):
513
+ block_count=4 cstruct_size=1 is_def=true price = str(price)
514
+ block_count=3 cstruct_size=1 is_def=true price_val = parse_price_val(price)
515
+ block_count=3 cstruct_size=1 is_def=true if price_val == float('inf'):
516
+ block_count=4 cstruct_size=1 is_def=true if not re.search('\\d', str(price)):
517
+ compo c_name=re
518
+ block_count=5 cstruct_size=1 is_def=true return (
519
+ block_count=6 cstruct_size=1 is_def=true f"Skipped saving product '{name}': Price info is missing or invalid ('{price}')."
520
+ block_count=6 cstruct_size=1 is_def=true )
521
+ block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
522
+ compo c_name=sqlite3
523
+ block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
524
+ compo c_name=conn
525
+ block_count=3 cstruct_size=1 is_def=true cursor.execute(
526
+ compo c_name=cursor
527
+ block_count=4 cstruct_size=1 is_def=true 'SELECT id, store, price, url FROM products WHERE name = ?',
528
+ block_count=4 cstruct_size=1 is_def=true (name,))
529
+ block_count=3 cstruct_size=1 is_def=true rows = cursor.fetchall()
530
+ compo c_name=cursor
531
+ block_count=3 cstruct_size=1 is_def=true items = []
532
+ block_count=3 cstruct_size=1 is_def=true for r in rows:
533
+ block_count=4 cstruct_size=1 is_def=true items.append({'id': r[0], 'store': r[1], 'price_str': r[2],
534
+ compo c_name=items
535
+ block_count=5 cstruct_size=1 is_def=true 'price_val': parse_price_val(r[2]), 'url': r[3]})
536
+ block_count=3 cstruct_size=1 is_def=true new_price_val = parse_price_val(price)
537
+ block_count=3 cstruct_size=1 is_def=true msg = ''
538
+ block_count=3 cstruct_size=1 is_def=true items.sort(key=lambda x: x['price_val'])
539
+ compo c_name=items
540
+ block_count=3 cstruct_size=1 is_def=true current_cheapest = items[0] if items else None
541
+ block_count=3 cstruct_size=1 is_def=true should_save = False
542
+ block_count=3 cstruct_size=1 is_def=true should_update = False
543
+ block_count=3 cstruct_size=1 is_def=true if not current_cheapest:
544
+ block_count=4 cstruct_size=1 is_def=true should_save = True
545
+ block_count=3 cstruct_size=1 is_def=true elif new_price_val < current_cheapest['price_val']:
546
+ block_count=4 cstruct_size=1 is_def=true should_save = True
547
+ block_count=4 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (name,))
548
+ compo c_name=cursor
549
+ block_count=4 cstruct_size=1 is_def=true msg_prefix = (
550
+ 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})."
551
+ block_count=5 cstruct_size=1 is_def=true )
552
+ block_count=3 cstruct_size=1 is_def=true elif new_price_val == current_cheapest['price_val']:
553
+ block_count=4 cstruct_size=1 is_def=true if store == current_cheapest['store']:
554
+ block_count=5 cstruct_size=1 is_def=true should_update = True
555
+ block_count=5 cstruct_size=1 is_def=true msg_prefix = f'Updated info for {name} at {store}.'
556
+ block_count=4 cstruct_size=1 is_def=true else:
557
+ block_count=5 cstruct_size=1 is_def=true msg = (
558
+ block_count=6 cstruct_size=1 is_def=true f"Product {name} exists with same price at {current_cheapest['store']}. Keeping existing."
559
+ block_count=6 cstruct_size=1 is_def=true )
560
+ block_count=3 cstruct_size=1 is_def=true elif store == current_cheapest['store']:
561
+ block_count=4 cstruct_size=1 is_def=true should_update = True
562
+ block_count=4 cstruct_size=1 is_def=true msg_prefix = (
563
+ block_count=5 cstruct_size=1 is_def=true f"Price increased for {name} at {store}: {current_cheapest['price_str']} -> {price}."
564
+ block_count=5 cstruct_size=1 is_def=true )
565
+ block_count=3 cstruct_size=1 is_def=true else:
566
+ block_count=4 cstruct_size=1 is_def=true msg = (
567
+ 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})."
568
+ block_count=5 cstruct_size=1 is_def=true )
569
+ block_count=3 cstruct_size=1 is_def=true if should_save:
570
+ block_count=4 cstruct_size=1 is_def=true cursor.execute(
571
+ compo c_name=cursor
572
+ block_count=5 cstruct_size=1 is_def=true """
573
+ block_count=5 cstruct_size=1 is_def=true INSERT INTO products (name, store, price, url, description, model_number, release_date, ram, ssd, updated_at)
574
+ block_count=5 cstruct_size=1 is_def=true VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
575
+ block_count=4 cstruct_size=1 is_def=true """
576
+ block_count=5 cstruct_size=1 is_def=true , (name, store, price, url, description, model_number,
577
+ block_count=5 cstruct_size=1 is_def=true release_date, ram, ssd))
578
+ block_count=4 cstruct_size=1 is_def=true if not msg:
579
+ block_count=5 cstruct_size=1 is_def=true msg = f'Saved product: {name} from {store} for {price}.'
580
+ block_count=4 cstruct_size=1 is_def=true else:
581
+ block_count=5 cstruct_size=1 is_def=true msg = msg_prefix
582
+ block_count=4 cstruct_size=1 is_def=true email_subject = f'Product Saved: {name}'
583
+ block_count=4 cstruct_size=1 is_def=true email_body = f"""Action: Saved (New or Cheaper)
584
+ block_count=0 cstruct_size=1 is_def=true Name: {name}
585
+ end of agent.SaveProductTool
586
+ block_count=0 cstruct_size=0 is_def=false Store: {store}
587
+ block_count=0 cstruct_size=0 is_def=false Price: {price}
588
+ block_count=0 cstruct_size=0 is_def=false URL: {url}
589
+ block_count=0 cstruct_size=0 is_def=false Model: {model_number}
590
+ block_count=0 cstruct_size=0 is_def=false Release: {release_date}
591
+ block_count=0 cstruct_size=0 is_def=false RAM: {ram}
592
+ block_count=0 cstruct_size=0 is_def=false SSD: {ssd}
593
+ block_count=0 cstruct_size=0 is_def=false Description: {description}
594
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
595
+ block_count=4 cstruct_size=0 is_def=false send_email_notification(email_subject, email_body)
596
+ block_count=3 cstruct_size=0 is_def=false if should_update:
597
+ block_count=4 cstruct_size=0 is_def=false cursor.execute(
598
+ compo c_name=cursor
599
+ block_count=5 cstruct_size=0 is_def=false 'SELECT price, url, description, model_number, release_date, ram, ssd FROM products WHERE id = ?'
600
+ block_count=5 cstruct_size=0 is_def=false , (current_cheapest['id'],))
601
+ block_count=4 cstruct_size=0 is_def=false curr_row = cursor.fetchone()
602
+ compo c_name=cursor
603
+ block_count=4 cstruct_size=0 is_def=false curr_price_str = curr_row[0]
604
+ block_count=4 cstruct_size=0 is_def=false curr_url = curr_row[1]
605
+ block_count=4 cstruct_size=0 is_def=false curr_desc = curr_row[2]
606
+ block_count=4 cstruct_size=0 is_def=false curr_model = curr_row[3]
607
+ block_count=4 cstruct_size=0 is_def=false curr_release = curr_row[4]
608
+ block_count=4 cstruct_size=0 is_def=false curr_ram = curr_row[5] if len(curr_row) > 5 else ''
609
+ block_count=4 cstruct_size=0 is_def=false curr_ssd = curr_row[6] if len(curr_row) > 6 else ''
610
+ block_count=4 cstruct_size=0 is_def=false final_model = model_number if model_number else curr_model
611
+ block_count=4 cstruct_size=0 is_def=false final_release = release_date if release_date else curr_release
612
+ block_count=4 cstruct_size=0 is_def=false final_ram = ram if ram else curr_ram
613
+ block_count=4 cstruct_size=0 is_def=false final_ssd = ssd if ssd else curr_ssd
614
+ block_count=4 cstruct_size=0 is_def=false if (price != curr_price_str or url != curr_url or
615
+ block_count=5 cstruct_size=0 is_def=false description != curr_desc or final_model != curr_model or
616
+ block_count=5 cstruct_size=0 is_def=false final_release != curr_release or final_ram != curr_ram or
617
+ block_count=5 cstruct_size=0 is_def=false final_ssd != curr_ssd):
618
+ block_count=5 cstruct_size=0 is_def=false cursor.execute(
619
+ compo c_name=cursor
620
+ block_count=6 cstruct_size=0 is_def=false """
621
+ block_count=6 cstruct_size=0 is_def=false UPDATE products
622
+ block_count=6 cstruct_size=0 is_def=false SET price = ?, url = ?, description = ?, model_number = ?, release_date = ?, ram = ?, ssd = ?, store = ?, updated_at = CURRENT_TIMESTAMP
623
+ block_count=6 cstruct_size=0 is_def=false WHERE id = ?
624
+ block_count=5 cstruct_size=0 is_def=false """
625
+ block_count=6 cstruct_size=0 is_def=false , (price, url, description, final_model,
626
+ block_count=6 cstruct_size=0 is_def=false final_release, final_ram, final_ssd, store,
627
+ block_count=6 cstruct_size=0 is_def=false current_cheapest['id']))
628
+ block_count=5 cstruct_size=0 is_def=false if not msg:
629
+ block_count=6 cstruct_size=0 is_def=false msg = f'Updated product {name} info.'
630
+ block_count=5 cstruct_size=0 is_def=false else:
631
+ block_count=6 cstruct_size=0 is_def=false msg = msg_prefix
632
+ block_count=5 cstruct_size=0 is_def=false email_subject = f'Product Updated: {name}'
633
+ block_count=5 cstruct_size=0 is_def=false email_body = f"""Action: Updated Info
634
+ block_count=0 cstruct_size=0 is_def=false Name: {name}
635
+ block_count=0 cstruct_size=0 is_def=false Store: {store}
636
+ block_count=0 cstruct_size=0 is_def=false Price: {price}
637
+ block_count=0 cstruct_size=0 is_def=false URL: {url}
638
+ block_count=0 cstruct_size=0 is_def=false Model: {final_model}
639
+ block_count=0 cstruct_size=0 is_def=false Release: {final_release}
640
+ block_count=0 cstruct_size=0 is_def=false RAM: {final_ram}
641
+ block_count=0 cstruct_size=0 is_def=false SSD: {final_ssd}
642
+ block_count=0 cstruct_size=0 is_def=false Description: {description}
643
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
644
+ block_count=5 cstruct_size=0 is_def=false send_email_notification(email_subject, email_body)
645
+ block_count=4 cstruct_size=0 is_def=false else:
646
+ block_count=5 cstruct_size=0 is_def=false msg = f'No changes for {name} at {store}.'
647
+ block_count=3 cstruct_size=0 is_def=false if should_save or should_update:
648
+ block_count=4 cstruct_size=0 is_def=false cursor.execute('SELECT id, price FROM products WHERE name = ?',
649
+ compo c_name=cursor
650
+ block_count=5 cstruct_size=0 is_def=false (name,))
651
+ block_count=4 cstruct_size=0 is_def=false rows = cursor.fetchall()
652
+ compo c_name=cursor
653
+ block_count=4 cstruct_size=0 is_def=false if len(rows) > 1:
654
+ block_count=5 cstruct_size=0 is_def=false rows_parsed = []
655
+ block_count=5 cstruct_size=0 is_def=false for r in rows:
656
+ block_count=6 cstruct_size=0 is_def=false rows_parsed.append({'id': r[0], 'val':
657
+ block_count=7 cstruct_size=0 is_def=false parse_price_val(r[1])})
658
+ block_count=5 cstruct_size=0 is_def=false rows_parsed.sort(key=lambda x: x['val'])
659
+ block_count=5 cstruct_size=0 is_def=false winner = rows_parsed[0]
660
+ block_count=5 cstruct_size=0 is_def=false for loser in rows_parsed[1:]:
661
+ block_count=6 cstruct_size=0 is_def=false cursor.execute('DELETE FROM products WHERE id = ?',
662
+ compo c_name=cursor
663
+ block_count=7 cstruct_size=0 is_def=false (loser['id'],))
664
+ block_count=5 cstruct_size=0 is_def=false msg += ' (Cleaned up duplicate records)'
665
+ block_count=3 cstruct_size=0 is_def=false conn.commit()
666
+ compo c_name=conn
667
+ block_count=3 cstruct_size=0 is_def=false conn.close()
668
+ compo c_name=conn
669
+ block_count=3 cstruct_size=0 is_def=false return msg
670
+ block_count=2 cstruct_size=0 is_def=false except Exception as e:
671
+ block_count=3 cstruct_size=0 is_def=false return f'Error saving product: {str(e)}'
672
+ block_count=0 cstruct_size=0 is_def=false class UpdatePricesInput(BaseModel):
673
+ class_name=agent.UpdatePricesInput
674
+ base_name=BaseModel
675
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
676
+ compo c_name=Field
677
+ block_count=0 cstruct_size=1 is_def=false class UpdatePricesTool(BaseTool):
678
+ end of agent.UpdatePricesInput
679
+ class_name=agent.UpdatePricesTool
680
+ base_name=BaseTool
681
+ block_count=1 cstruct_size=1 is_def=false name = 'update_prices'
682
+ block_count=1 cstruct_size=1 is_def=false description = (
683
+ 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.'
684
+ block_count=2 cstruct_size=1 is_def=false )
685
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = UpdatePricesInput
686
+ block_count=1 cstruct_size=1 is_def=false def _fetch_page_content(self, url: str) ->(bool, str, str):
687
+ block_count=2 cstruct_size=1 is_def=true """
688
+ block_count=2 cstruct_size=1 is_def=true URLにアクセスし、(成功したか, 理由/エラー, HTMLテキスト) を返す
689
+ block_count=2 cstruct_size=1 is_def=true """
690
+ block_count=2 cstruct_size=1 is_def=true if not url or not url.startswith('http'):
691
+ compo c_name=url
692
+ block_count=3 cstruct_size=1 is_def=true return False, 'Invalid URL', ''
693
+ block_count=2 cstruct_size=1 is_def=true try:
694
+ block_count=3 cstruct_size=1 is_def=true req = urllib.request.Request(url, headers={'User-Agent':
695
+ compo c_name=urllib
696
+ compo c_name=Request
697
+ 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'
698
+ block_count=4 cstruct_size=1 is_def=true , 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8'})
699
+ block_count=3 cstruct_size=1 is_def=true with urllib.request.urlopen(req, timeout=15) as response:
700
+ compo c_name=urllib
701
+ block_count=4 cstruct_size=1 is_def=true html_content = response.read().decode('utf-8', errors='ignore')
702
+ compo c_name=response
703
+ block_count=4 cstruct_size=1 is_def=true bot_keywords = ['ロボットではありません', 'アクセスが制限されています', 'キャプチャ',
704
+ block_count=5 cstruct_size=1 is_def=true 'CAPTCHA', 'Are you a human?',
705
+ block_count=5 cstruct_size=1 is_def=true 'Please verify you are a human', 'Incapsula', 'Cloudflare']
706
+ block_count=4 cstruct_size=1 is_def=true html_lower = html_content.lower()
707
+ block_count=4 cstruct_size=1 is_def=true for kw in bot_keywords:
708
+ block_count=5 cstruct_size=1 is_def=true if kw.lower() in html_lower:
709
+ compo c_name=kw
710
+ block_count=6 cstruct_size=1 is_def=true return False, 'Bot Challenge Detected', ''
711
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<script.*?>.*?</script>', '',
712
+ compo c_name=re
713
+ block_count=5 cstruct_size=1 is_def=true html_content, flags=re.DOTALL | re.IGNORECASE)
714
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<style.*?>.*?</style>', '', clean_text,
715
+ compo c_name=re
716
+ block_count=5 cstruct_size=1 is_def=true flags=re.DOTALL | re.IGNORECASE)
717
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<img[^>]+alt="([^"]*)"[^>]*>', ' \\1 ',
718
+ compo c_name=re
719
+ block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
720
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub("<img[^>]+alt='([^']*)'[^>]*>", ' \\1 ',
721
+ compo c_name=re
722
+ block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
723
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<.*?>', ' ', clean_text)
724
+ compo c_name=re
725
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('\\s+', ' ', clean_text).strip()
726
+ compo c_name=re
727
+ block_count=4 cstruct_size=1 is_def=true if len(clean_text) > 10000:
728
+ block_count=5 cstruct_size=1 is_def=true clean_text = clean_text[:10000]
729
+ block_count=4 cstruct_size=1 is_def=true return True, 'Success', clean_text
730
+ block_count=2 cstruct_size=1 is_def=true except urllib.error.HTTPError as e:
731
+ compo c_name=urllib
732
+ block_count=3 cstruct_size=1 is_def=true if e.code == 404:
733
+ block_count=4 cstruct_size=1 is_def=true return False, '404 Not Found', ''
734
+ block_count=3 cstruct_size=1 is_def=true elif e.code == 410:
735
+ block_count=4 cstruct_size=1 is_def=true return False, '410 Gone', ''
736
+ block_count=3 cstruct_size=1 is_def=true elif e.code in [500, 502, 503, 504]:
737
+ block_count=4 cstruct_size=1 is_def=true return False, f'Retryable Server Error ({e.code})', ''
738
+ block_count=3 cstruct_size=1 is_def=true elif e.code == 403:
739
+ block_count=4 cstruct_size=1 is_def=true return False, '403 Forbidden (Possible Bot Block)', ''
740
+ block_count=3 cstruct_size=1 is_def=true else:
741
+ block_count=4 cstruct_size=1 is_def=true return False, f'HTTP Error {e.code}', ''
742
+ block_count=2 cstruct_size=1 is_def=true except urllib.error.URLError as e:
743
+ compo c_name=urllib
744
+ block_count=3 cstruct_size=1 is_def=true return False, f'URL Error: {e.reason}', ''
745
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
746
+ block_count=3 cstruct_size=1 is_def=true return False, f'Connection Error: {e}', ''
747
+ block_count=1 cstruct_size=1 is_def=true def _delete_product(self, product: dict, reason: str):
748
+ block_count=2 cstruct_size=1 is_def=true try:
749
+ block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
750
+ compo c_name=sqlite3
751
+ block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
752
+ compo c_name=conn
753
+ block_count=3 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (product[
754
+ compo c_name=cursor
755
+ block_count=4 cstruct_size=1 is_def=true 'id'],))
756
+ block_count=3 cstruct_size=1 is_def=true conn.commit()
757
+ compo c_name=conn
758
+ block_count=3 cstruct_size=1 is_def=true conn.close()
759
+ compo c_name=conn
760
+ block_count=3 cstruct_size=1 is_def=true msg = f"Deleted {product['name']} at {product['store']} ({reason})"
761
+ block_count=3 cstruct_size=1 is_def=true print(f' {msg}')
762
+ block_count=3 cstruct_size=1 is_def=true email_subject = f"Product Deleted: {product['name']}"
763
+ block_count=3 cstruct_size=1 is_def=true email_body = f"""Action: Deleted (Unavailable/Not Found)
764
+ block_count=0 cstruct_size=1 is_def=true Name: {product['name']}
765
+ end of agent.UpdatePricesTool
766
+ block_count=0 cstruct_size=0 is_def=false Store: {product['store']}
767
+ block_count=0 cstruct_size=0 is_def=false URL: {product['url']}
768
+ block_count=0 cstruct_size=0 is_def=false Reason: {reason}
769
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
770
+ block_count=3 cstruct_size=0 is_def=false send_email_notification(email_subject, email_body)
771
+ block_count=3 cstruct_size=0 is_def=false return True
772
+ block_count=2 cstruct_size=0 is_def=false except Exception as e:
773
+ block_count=3 cstruct_size=0 is_def=false print(f' Error deleting product: {e}')
774
+ block_count=3 cstruct_size=0 is_def=false return False
775
+ block_count=1 cstruct_size=0 is_def=false def _run(self, query: str='', **kwargs):
776
+ block_count=2 cstruct_size=0 is_def=true print('\n--- Starting Price Update (Direct URL Access) ---')
777
+ block_count=2 cstruct_size=0 is_def=true products = get_all_products()
778
+ block_count=2 cstruct_size=0 is_def=true if not products:
779
+ block_count=3 cstruct_size=0 is_def=true return 'No products in database to update.'
780
+ block_count=2 cstruct_size=0 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
781
+ compo c_name=os
782
+ block_count=2 cstruct_size=0 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
783
+ compo c_name=ChatGoogleGenerativeAI
784
+ block_count=2 cstruct_size=0 is_def=true updated_count = 0
785
+ block_count=2 cstruct_size=0 is_def=true deleted_count = 0
786
+ block_count=2 cstruct_size=0 is_def=true for p in products:
787
+ block_count=3 cstruct_size=0 is_def=true name = p['name']
788
+ block_count=3 cstruct_size=0 is_def=true store = p['store']
789
+ block_count=3 cstruct_size=0 is_def=true url = p['url']
790
+ block_count=3 cstruct_size=0 is_def=true print(f"Checking: {name} at {store} (ID: {p['id']})")
791
+ block_count=3 cstruct_size=0 is_def=true if not url:
792
+ block_count=4 cstruct_size=0 is_def=true print(f' [Warning] No URL for this product. Skipping.')
793
+ block_count=4 cstruct_size=0 is_def=true continue
794
+ block_count=3 cstruct_size=0 is_def=true success, access_reason, page_text = self._fetch_page_content(url)
795
+ block_count=3 cstruct_size=0 is_def=true if not success:
796
+ block_count=4 cstruct_size=0 is_def=true if ('404 Not Found' in access_reason or '410 Gone' in
797
+ block_count=5 cstruct_size=0 is_def=true access_reason or 'Invalid URL' in access_reason):
798
+ block_count=5 cstruct_size=0 is_def=true print(
799
+ block_count=6 cstruct_size=0 is_def=true f' [Info] URL is dead ({access_reason}). Deleting product.'
800
+ block_count=6 cstruct_size=0 is_def=true )
801
+ block_count=5 cstruct_size=0 is_def=true if self._delete_product(p, access_reason):
802
+ block_count=6 cstruct_size=0 is_def=true deleted_count += 1
803
+ block_count=4 cstruct_size=0 is_def=true else:
804
+ block_count=5 cstruct_size=0 is_def=true print(
805
+ block_count=6 cstruct_size=0 is_def=true f' [Warning] Skipping update due to temporary/access error: {access_reason}'
806
+ block_count=6 cstruct_size=0 is_def=true )
807
+ block_count=4 cstruct_size=0 is_def=true continue
808
+ block_count=3 cstruct_size=0 is_def=true prompt = f"""
809
+ block_count=3 cstruct_size=0 is_def=true 以下のテキストは、ある商品のウェブページから抽出した内容です。
810
+ block_count=3 cstruct_size=0 is_def=true このページの内容を分析し、以下のタスクを行ってください。
811
+ block_count=3 cstruct_size=0 is_def=true 対象商品名: {name}
812
+ block_count=3 cstruct_size=0 is_def=true 対象店舗: {store}
813
+ block_count=3 cstruct_size=0 is_def=true 現在の価格: {p['price']}
814
+ block_count=3 cstruct_size=0 is_def=true 抽出テキスト:
815
+ block_count=3 cstruct_size=0 is_def=true {page_text}
816
+ block_count=3 cstruct_size=0 is_def=true タスク:
817
+ block_count=3 cstruct_size=0 is_def=true 1. このページで対象商品が現在も「販売中」かつ「在庫がある」か判定してください。
818
+ block_count=3 cstruct_size=0 is_def=true ※「販売終了」「お探しのページは見つかりません」「在庫なし」「在庫切れ」「取り扱いできません」「該当の商品がありません」などの明確な記載(テキストや画像の代替テキスト(alt属性)含む)がある場合は is_unavailable を true にしてください。
819
+ block_count=3 cstruct_size=0 is_def=true ※商品とは無関係な別商品の在庫情報に騙されないでください。
820
+ block_count=3 cstruct_size=0 is_def=true 2. 販売中である場合、最新の「価格」「詳細情報・スペック(description)」「型番(model_number)」「発売日(release_date)」「メモリ容量(ram)」「SSD容量(ssd)」を抽出してください。発売日は数字とハイフンのみの日付にしてください(例: 2023-10-01)。型番は日付ではなくメーカー名やシリーズ名を含む英数字の文字列を抽出してください。もし発売日と混同されるような表記や数字とハイフンのみであれば、型番として抽出しないでください。
821
+ block_count=3 cstruct_size=0 is_def=true 見つからない項目は空文字("")にしてください。
822
+ block_count=3 cstruct_size=0 is_def=true ※注意: メモリ(RAM)やSSDなどの容量を抽出する際は、「最大〇〇GB」「〇〇GBまで増設可能」といった拡張上限の数値は対象外とし、必ず「標準搭載の容量」を抽出・記載してください。
823
+ block_count=3 cstruct_size=0 is_def=true ※RAM容量の単位はGBに統一してください(例: 16384MB -> 16GB)。また、RAM容量やSSD容量に複数候補がある場合は、/(スラッシュ)区切りで保存してください(例: 256GB/512GB/1TB)。
824
+ block_count=3 cstruct_size=0 is_def=true JSON形式で返してください。
825
+ block_count=3 cstruct_size=0 is_def=true 出力例:
826
+ block_count=3 cstruct_size=0 is_def=true {{
827
+ block_count=4 cstruct_size=0 is_def=true "is_unavailable": false,
828
+ block_count=4 cstruct_size=0 is_def=true "unavailability_reason": "",
829
+ block_count=4 cstruct_size=0 is_def=true "price": "10,500円",
830
+ block_count=4 cstruct_size=0 is_def=true "description": "最新モデル、送料無料",
831
+ block_count=4 cstruct_size=0 is_def=true "model_number": "ABC-123",
832
+ block_count=4 cstruct_size=0 is_def=true "release_date": "2023-10-01",
833
+ block_count=4 cstruct_size=0 is_def=true "ram": "16GB",
834
+ block_count=4 cstruct_size=0 is_def=true "ssd": "512GB"
835
+ block_count=3 cstruct_size=0 is_def=true }}
836
+ block_count=3 cstruct_size=0 is_def=true """
837
+ block_count=3 cstruct_size=0 is_def=true try:
838
+ block_count=4 cstruct_size=0 is_def=true response = llm.invoke(prompt)
839
+ compo c_name=llm
840
+ block_count=4 cstruct_size=0 is_def=true content = response.content
841
+ compo c_name=response
842
+ block_count=4 cstruct_size=0 is_def=true if '```json' in content:
843
+ block_count=5 cstruct_size=0 is_def=true content = content.split('```json')[1].split('```')[0]
844
+ compo c_name=content
845
+ block_count=4 cstruct_size=0 is_def=true elif '```' in content:
846
+ block_count=5 cstruct_size=0 is_def=true content = content.split('```')[1].split('```')[0]
847
+ compo c_name=content
848
+ block_count=4 cstruct_size=0 is_def=true content = content.strip()
849
+ compo c_name=content
850
+ block_count=4 cstruct_size=0 is_def=true content = re.sub('\\\\(?![/"\\\\bfnrtu])', '\\\\\\\\', content)
851
+ compo c_name=re
852
+ block_count=4 cstruct_size=0 is_def=true try:
853
+ block_count=5 cstruct_size=0 is_def=true result_data = json.loads(content, strict=False)
854
+ compo c_name=json
855
+ block_count=4 cstruct_size=0 is_def=true except json.JSONDecodeError as e:
856
+ block_count=5 cstruct_size=0 is_def=true print(
857
+ block_count=6 cstruct_size=0 is_def=true f' [Warning] Failed to parse JSON: {e}. Attempting further cleanup.'
858
+ block_count=6 cstruct_size=0 is_def=true )
859
+ block_count=5 cstruct_size=0 is_def=true content = content.replace('\\', '')
860
+ compo c_name=content
861
+ block_count=5 cstruct_size=0 is_def=true result_data = json.loads(content, strict=False)
862
+ compo c_name=json
863
+ block_count=4 cstruct_size=0 is_def=true is_unavailable = result_data.get('is_unavailable', False)
864
+ block_count=4 cstruct_size=0 is_def=true unavailability_reason = result_data.get('unavailability_reason'
865
+ block_count=5 cstruct_size=0 is_def=true , 'ページ内に在庫なし・販売終了の記載あり')
866
+ block_count=4 cstruct_size=0 is_def=true if is_unavailable:
867
+ block_count=5 cstruct_size=0 is_def=true print(
868
+ block_count=6 cstruct_size=0 is_def=true f' [Info] LLM determined product is unavailable. Reason: {unavailability_reason}. Deleting product.'
869
+ block_count=6 cstruct_size=0 is_def=true )
870
+ block_count=5 cstruct_size=0 is_def=true if self._delete_product(p, unavailability_reason):
871
+ block_count=6 cstruct_size=0 is_def=true deleted_count += 1
872
+ block_count=4 cstruct_size=0 is_def=true else:
873
+ block_count=5 cstruct_size=0 is_def=true new_price = result_data.get('price', '')
874
+ block_count=5 cstruct_size=0 is_def=true new_desc = result_data.get('description', '')
875
+ block_count=5 cstruct_size=0 is_def=true new_model = result_data.get('model_number', '')
876
+ block_count=5 cstruct_size=0 is_def=true new_release = result_data.get('release_date', '')
877
+ block_count=5 cstruct_size=0 is_def=true new_ram = result_data.get('ram', '')
878
+ block_count=5 cstruct_size=0 is_def=true new_ssd = result_data.get('ssd', '')
879
+ block_count=5 cstruct_size=0 is_def=true if not new_price:
880
+ block_count=6 cstruct_size=0 is_def=true print(
881
+ block_count=7 cstruct_size=0 is_def=true f' [Warning] Could not extract price from page. Skipping update.'
882
+ block_count=7 cstruct_size=0 is_def=true )
883
+ block_count=6 cstruct_size=0 is_def=true continue
884
+ block_count=5 cstruct_size=0 is_def=true final_desc = new_desc if new_desc else p['description']
885
+ block_count=5 cstruct_size=0 is_def=true final_model = new_model if new_model else p.get(
886
+ block_count=6 cstruct_size=0 is_def=true 'model_number', '')
887
+ block_count=5 cstruct_size=0 is_def=true final_release = new_release if new_release else p.get(
888
+ block_count=6 cstruct_size=0 is_def=true 'release_date', '')
889
+ block_count=5 cstruct_size=0 is_def=true final_ram = new_ram if new_ram else p.get('ram', '')
890
+ block_count=5 cstruct_size=0 is_def=true final_ssd = new_ssd if new_ssd else p.get('ssd', '')
891
+ block_count=5 cstruct_size=0 is_def=true changes = []
892
+ block_count=5 cstruct_size=0 is_def=true new_price_val = parse_price_val(new_price)
893
+ block_count=5 cstruct_size=0 is_def=true old_price_val = parse_price_val(p['price'])
894
+ block_count=5 cstruct_size=0 is_def=true if new_price_val != old_price_val:
895
+ block_count=6 cstruct_size=0 is_def=true changes.append(f"Price ({p['price']} -> {new_price})")
896
+ compo c_name=changes
897
+ block_count=5 cstruct_size=0 is_def=true else:
898
+ block_count=6 cstruct_size=0 is_def=true new_price = p['price']
899
+ block_count=5 cstruct_size=0 is_def=true old_model = p.get('model_number', '')
900
+ block_count=5 cstruct_size=0 is_def=true if not is_similar_model(final_model, old_model):
901
+ block_count=6 cstruct_size=0 is_def=true changes.append(f'Model ({old_model} -> {final_model})')
902
+ compo c_name=changes
903
+ block_count=5 cstruct_size=0 is_def=true else:
904
+ block_count=6 cstruct_size=0 is_def=true final_model = old_model
905
+ block_count=5 cstruct_size=0 is_def=true old_release = p.get('release_date', '')
906
+ block_count=5 cstruct_size=0 is_def=true if parse_date_val(final_release) != parse_date_val(
907
+ block_count=6 cstruct_size=0 is_def=true old_release):
908
+ block_count=6 cstruct_size=0 is_def=true changes.append(
909
+ compo c_name=changes
910
+ block_count=7 cstruct_size=0 is_def=true f'Release Date ({old_release} -> {final_release})')
911
+ block_count=5 cstruct_size=0 is_def=true else:
912
+ block_count=6 cstruct_size=0 is_def=true final_release = old_release
913
+ block_count=5 cstruct_size=0 is_def=true old_ram = p.get('ram', '')
914
+ block_count=5 cstruct_size=0 is_def=true if extract_alphanumeric(final_ram) != extract_alphanumeric(
915
+ block_count=6 cstruct_size=0 is_def=true old_ram):
916
+ block_count=6 cstruct_size=0 is_def=true changes.append(f'RAM ({old_ram} -> {final_ram})')
917
+ compo c_name=changes
918
+ block_count=5 cstruct_size=0 is_def=true else:
919
+ block_count=6 cstruct_size=0 is_def=true final_ram = old_ram
920
+ block_count=5 cstruct_size=0 is_def=true old_ssd = p.get('ssd', '')
921
+ block_count=5 cstruct_size=0 is_def=true if extract_alphanumeric(final_ssd) != extract_alphanumeric(
922
+ block_count=6 cstruct_size=0 is_def=true old_ssd):
923
+ block_count=6 cstruct_size=0 is_def=true changes.append(f'SSD ({old_ssd} -> {final_ssd})')
924
+ compo c_name=changes
925
+ block_count=5 cstruct_size=0 is_def=true else:
926
+ block_count=6 cstruct_size=0 is_def=true final_ssd = old_ssd
927
+ block_count=5 cstruct_size=0 is_def=true if changes:
928
+ block_count=6 cstruct_size=0 is_def=true try:
929
+ block_count=7 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
930
+ compo c_name=sqlite3
931
+ block_count=7 cstruct_size=0 is_def=true cursor = conn.cursor()
932
+ compo c_name=conn
933
+ block_count=7 cstruct_size=0 is_def=true cursor.execute(
934
+ compo c_name=cursor
935
+ block_count=8 cstruct_size=0 is_def=true """
936
+ block_count=8 cstruct_size=0 is_def=true UPDATE products
937
+ block_count=8 cstruct_size=0 is_def=true SET price = ?, description = ?, model_number = ?, release_date = ?, ram = ?, ssd = ?, updated_at = CURRENT_TIMESTAMP
938
+ block_count=8 cstruct_size=0 is_def=true WHERE id = ?
939
+ block_count=7 cstruct_size=0 is_def=true """
940
+ block_count=8 cstruct_size=0 is_def=true , (new_price, final_desc, final_model,
941
+ block_count=8 cstruct_size=0 is_def=true final_release, final_ram, final_ssd, p['id']))
942
+ block_count=7 cstruct_size=0 is_def=true conn.commit()
943
+ compo c_name=conn
944
+ block_count=7 cstruct_size=0 is_def=true if cursor.rowcount > 0:
945
+ compo c_name=cursor
946
+ block_count=8 cstruct_size=0 is_def=true updated_count += 1
947
+ block_count=8 cstruct_size=0 is_def=true changes_str = ', '.join(changes)
948
+ block_count=8 cstruct_size=0 is_def=true msg = (
949
+ block_count=9 cstruct_size=0 is_def=true f'Updated {name} at {store}. Changes: {changes_str}'
950
+ block_count=9 cstruct_size=0 is_def=true )
951
+ block_count=8 cstruct_size=0 is_def=true print(f' {msg}')
952
+ block_count=8 cstruct_size=0 is_def=true email_subject = f'Product Updated: {name}'
953
+ block_count=8 cstruct_size=0 is_def=true email_body = f"""Action: Updated Info (Direct URL Check)
954
+ block_count=0 cstruct_size=0 is_def=true Name: {name}
955
+ block_count=0 cstruct_size=0 is_def=false Store: {store}
956
+ block_count=0 cstruct_size=0 is_def=false URL: {url}
957
+ block_count=0 cstruct_size=0 is_def=false Changed Fields:
958
+ block_count=0 cstruct_size=0 is_def=false {changes_str}
959
+ block_count=0 cstruct_size=0 is_def=false --- Current Data ---
960
+ block_count=0 cstruct_size=0 is_def=false Price: {new_price}
961
+ block_count=0 cstruct_size=0 is_def=false Model: {final_model}
962
+ block_count=0 cstruct_size=0 is_def=false Release: {final_release}
963
+ block_count=0 cstruct_size=0 is_def=false RAM: {final_ram}
964
+ block_count=0 cstruct_size=0 is_def=false SSD: {final_ssd}
965
+ block_count=0 cstruct_size=0 is_def=false Description: {final_desc}
966
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
967
+ block_count=8 cstruct_size=0 is_def=false send_email_notification(email_subject,
968
+ block_count=9 cstruct_size=0 is_def=false email_body)
969
+ block_count=7 cstruct_size=0 is_def=false conn.close()
970
+ compo c_name=conn
971
+ block_count=6 cstruct_size=0 is_def=false except Exception as e:
972
+ block_count=7 cstruct_size=0 is_def=false print(f' Error updating {name} at {store}: {e}')
973
+ block_count=5 cstruct_size=0 is_def=false else:
974
+ block_count=6 cstruct_size=0 is_def=false print(f' No spec/price changes for {name} at {store}.'
975
+ block_count=7 cstruct_size=0 is_def=false )
976
+ block_count=3 cstruct_size=0 is_def=false except Exception as e:
977
+ block_count=4 cstruct_size=0 is_def=false print(f' Error processing LLM response for {name}: {e}')
978
+ block_count=3 cstruct_size=0 is_def=false time.sleep(1)
979
+ compo c_name=time
980
+ block_count=2 cstruct_size=0 is_def=false return (
981
+ block_count=3 cstruct_size=0 is_def=false f'Price update complete. Updated {updated_count} items, Deleted {deleted_count} unavailable items.'
982
+ block_count=3 cstruct_size=0 is_def=false )
983
+ block_count=0 cstruct_size=0 is_def=false class SearchProductsInput(BaseModel):
984
+ class_name=agent.SearchProductsInput
985
+ base_name=BaseModel
986
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
987
+ compo c_name=Field
988
+ block_count=2 cstruct_size=1 is_def=false 'Natural language query to search products in the database')
989
+ block_count=0 cstruct_size=1 is_def=false class SearchProductsTool(BaseTool):
990
+ end of agent.SearchProductsInput
991
+ class_name=agent.SearchProductsTool
992
+ base_name=BaseTool
993
+ block_count=1 cstruct_size=1 is_def=false name = 'search_products'
994
+ block_count=1 cstruct_size=1 is_def=false description = (
995
+ 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')."
996
+ block_count=2 cstruct_size=1 is_def=false )
997
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SearchProductsInput
998
+ block_count=1 cstruct_size=1 is_def=false def _run(self, query: str, **kwargs):
999
+ block_count=2 cstruct_size=1 is_def=true print(f'\n--- Searching Database: {query} ---')
1000
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
1001
+ compo c_name=os
1002
+ block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
1003
+ compo c_name=ChatGoogleGenerativeAI
1004
+ block_count=2 cstruct_size=1 is_def=true prompt = f"""
1005
+ block_count=2 cstruct_size=1 is_def=true Analyze the user's search query for products and extract search criteria.
1006
+ block_count=2 cstruct_size=1 is_def=true User Query: {query}
1007
+ block_count=2 cstruct_size=1 is_def=true Return a JSON object with the following keys:
1008
+ block_count=2 cstruct_size=1 is_def=true - keyword_groups: List of LISTS of keywords. Each inner list represents synonyms (OR condition), and all outer lists must be satisfied (AND condition).
1009
+ block_count=2 cstruct_size=1 is_def=true - Example for "Cheap Mouse": [["mouse", "マウス"]] (Price is handled by sort_by)
1010
+ block_count=2 cstruct_size=1 is_def=true - Example for "16GB Memory": [["16GB", "16G", "16ギガ"], ["memory", "メモリ"]] -> "memory" is often redundant if "16GB" is unique, so prefer specific specs.
1011
+ block_count=2 cstruct_size=1 is_def=true - Example for "32GB PC": [["32GB", "32G"], ["PC", "パソコン", "computer"]]
1012
+ block_count=2 cstruct_size=1 is_def=true - exclude_keywords: List of keywords that MUST NOT appear in the product info (name, description, url).
1013
+ block_count=2 cstruct_size=1 is_def=true - Example for "No SSD": ["SSD"]
1014
+ block_count=2 cstruct_size=1 is_def=true - Example for "exclude memory info": ["memory", "メモリ"]
1015
+ block_count=2 cstruct_size=1 is_def=true - empty_fields: List of field names that must be empty or null (e.g. for "no description", "desc is empty", "url not set").
1016
+ block_count=2 cstruct_size=1 is_def=true - Valid values: "name", "price", "store", "url", "description"
1017
+ block_count=2 cstruct_size=1 is_def=true - sort_by: "price_asc" (cheapest), "price_desc" (expensive), or null (relevance)
1018
+ block_count=2 cstruct_size=1 is_def=true - max_price: integer or null
1019
+ block_count=2 cstruct_size=1 is_def=true - min_price: integer or null
1020
+ block_count=2 cstruct_size=1 is_def=true Important Rules for Keywords extraction:
1021
+ block_count=2 cstruct_size=1 is_def=true 1. Exclude Metadata Field Names: NEVER include words that refer to database columns like "URL", "url", "price", "name", "title", "description", "store" in `keyword_groups`.
1022
+ block_count=2 cstruct_size=1 is_def=true - CORRECT: "URL with example" -> [["example"]]
1023
+ block_count=2 cstruct_size=1 is_def=true - WRONG: "URL with example" -> [["URL"], ["example"]]
1024
+ block_count=2 cstruct_size=1 is_def=true - CORRECT: "URLにexampleが含まれる" -> [["example"]]
1025
+ block_count=2 cstruct_size=1 is_def=true 2. Exclude Action Verbs: Do not include "search", "find", "探して", "検索", "教えて".
1026
+ block_count=2 cstruct_size=1 is_def=true 3. Exclude General Terms: Do not include "product", "item", "thing", "もの", "商品".
1027
+ block_count=2 cstruct_size=1 is_def=true 4. If the query implies a category (e.g. "PC"), include it as a keyword group.
1028
+ block_count=2 cstruct_size=1 is_def=true Example JSON:
1029
+ block_count=2 cstruct_size=1 is_def=true {{
1030
+ block_count=3 cstruct_size=1 is_def=true "keyword_groups": [["mouse", "マウス"]],
1031
+ block_count=3 cstruct_size=1 is_def=true "exclude_keywords": [],
1032
+ block_count=3 cstruct_size=1 is_def=true "empty_fields": [],
1033
+ block_count=3 cstruct_size=1 is_def=true "sort_by": "price_asc",
1034
+ block_count=3 cstruct_size=1 is_def=true "max_price": 5000,
1035
+ block_count=3 cstruct_size=1 is_def=true "min_price": null
1036
+ block_count=2 cstruct_size=1 is_def=true }}
1037
+ block_count=2 cstruct_size=1 is_def=true """
1038
+ block_count=2 cstruct_size=1 is_def=true try:
1039
+ block_count=3 cstruct_size=1 is_def=true response = llm.invoke(prompt)
1040
+ compo c_name=llm
1041
+ block_count=3 cstruct_size=1 is_def=true content = response.content.strip()
1042
+ compo c_name=response
1043
+ block_count=3 cstruct_size=1 is_def=true if '```json' in content:
1044
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
1045
+ compo c_name=content
1046
+ block_count=3 cstruct_size=1 is_def=true elif '```' in content:
1047
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
1048
+ compo c_name=content
1049
+ block_count=3 cstruct_size=1 is_def=true criteria = json.loads(content)
1050
+ compo c_name=json
1051
+ block_count=3 cstruct_size=1 is_def=true print(f' Search Criteria: {criteria}')
1052
+ block_count=3 cstruct_size=1 is_def=true all_products = get_all_products()
1053
+ block_count=3 cstruct_size=1 is_def=true filtered_products = []
1054
+ block_count=3 cstruct_size=1 is_def=true keyword_groups = criteria.get('keyword_groups', [])
1055
+ compo c_name=criteria
1056
+ block_count=3 cstruct_size=1 is_def=true exclude_keywords = criteria.get('exclude_keywords', [])
1057
+ compo c_name=criteria
1058
+ block_count=3 cstruct_size=1 is_def=true empty_fields = criteria.get('empty_fields', [])
1059
+ compo c_name=criteria
1060
+ block_count=3 cstruct_size=1 is_def=true sort_by = criteria.get('sort_by')
1061
+ compo c_name=criteria
1062
+ block_count=3 cstruct_size=1 is_def=true max_p = criteria.get('max_price')
1063
+ compo c_name=criteria
1064
+ block_count=3 cstruct_size=1 is_def=true min_p = criteria.get('min_price')
1065
+ compo c_name=criteria
1066
+ block_count=3 cstruct_size=1 is_def=true for p in all_products:
1067
+ block_count=4 cstruct_size=1 is_def=true text_to_search = (p['name'] + ' ' + (p['description'] or ''
1068
+ block_count=5 cstruct_size=1 is_def=true ) + ' ' + (p['url'] or '')).lower()
1069
+ block_count=4 cstruct_size=1 is_def=true if empty_fields:
1070
+ block_count=5 cstruct_size=1 is_def=true is_empty_match = True
1071
+ block_count=5 cstruct_size=1 is_def=true for field in empty_fields:
1072
+ block_count=6 cstruct_size=1 is_def=true val = p.get(field)
1073
+ block_count=6 cstruct_size=1 is_def=true if val and str(val).strip():
1074
+ block_count=7 cstruct_size=1 is_def=true is_empty_match = False
1075
+ block_count=7 cstruct_size=1 is_def=true break
1076
+ block_count=5 cstruct_size=1 is_def=true if not is_empty_match:
1077
+ block_count=6 cstruct_size=1 is_def=true continue
1078
+ block_count=4 cstruct_size=1 is_def=true if exclude_keywords:
1079
+ block_count=5 cstruct_size=1 is_def=true should_exclude = False
1080
+ block_count=5 cstruct_size=1 is_def=true for k in exclude_keywords:
1081
+ block_count=6 cstruct_size=1 is_def=true if k.lower() in text_to_search:
1082
+ block_count=7 cstruct_size=1 is_def=true should_exclude = True
1083
+ block_count=7 cstruct_size=1 is_def=true break
1084
+ block_count=5 cstruct_size=1 is_def=true if should_exclude:
1085
+ block_count=6 cstruct_size=1 is_def=true continue
1086
+ block_count=4 cstruct_size=1 is_def=true if keyword_groups:
1087
+ block_count=5 cstruct_size=1 is_def=true all_groups_match = True
1088
+ block_count=5 cstruct_size=1 is_def=true for group in keyword_groups:
1089
+ block_count=6 cstruct_size=1 is_def=true group_match = False
1090
+ block_count=6 cstruct_size=1 is_def=true for k in group:
1091
+ block_count=7 cstruct_size=1 is_def=true if k.lower() in text_to_search:
1092
+ block_count=8 cstruct_size=1 is_def=true group_match = True
1093
+ block_count=8 cstruct_size=1 is_def=true break
1094
+ block_count=6 cstruct_size=1 is_def=true if not group_match:
1095
+ block_count=7 cstruct_size=1 is_def=true all_groups_match = False
1096
+ block_count=7 cstruct_size=1 is_def=true break
1097
+ block_count=5 cstruct_size=1 is_def=true if not all_groups_match:
1098
+ block_count=6 cstruct_size=1 is_def=true continue
1099
+ block_count=4 cstruct_size=1 is_def=true price_val = parse_price_val(p['price'])
1100
+ block_count=4 cstruct_size=1 is_def=true if max_p is not None and price_val > max_p:
1101
+ block_count=5 cstruct_size=1 is_def=true continue
1102
+ block_count=4 cstruct_size=1 is_def=true if min_p is not None and price_val < min_p:
1103
+ block_count=5 cstruct_size=1 is_def=true continue
1104
+ block_count=4 cstruct_size=1 is_def=true p['price_val'] = price_val
1105
+ block_count=4 cstruct_size=1 is_def=true filtered_products.append(p)
1106
+ block_count=3 cstruct_size=1 is_def=true if sort_by == 'price_asc':
1107
+ block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'])
1108
+ block_count=3 cstruct_size=1 is_def=true elif sort_by == 'price_desc':
1109
+ block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'],
1110
+ block_count=5 cstruct_size=1 is_def=true reverse=True)
1111
+ block_count=3 cstruct_size=1 is_def=true if not filtered_products:
1112
+ block_count=4 cstruct_size=1 is_def=true return 'No products found matching your criteria.'
1113
+ block_count=3 cstruct_size=1 is_def=true result_str = f'Found {len(filtered_products)} products:\n'
1114
+ block_count=3 cstruct_size=1 is_def=true for p in filtered_products[:10]:
1115
+ block_count=4 cstruct_size=1 is_def=true result_str += (
1116
+ block_count=5 cstruct_size=1 is_def=true f"- [ID: {p['id']}] {p['name']} ({p['price']}) @ {p['store']}\n"
1117
+ block_count=5 cstruct_size=1 is_def=true )
1118
+ block_count=4 cstruct_size=1 is_def=true if p['description']:
1119
+ block_count=5 cstruct_size=1 is_def=true result_str += f" Desc: {p['description'][:100]}...\n"
1120
+ block_count=4 cstruct_size=1 is_def=true if p['url']:
1121
+ block_count=5 cstruct_size=1 is_def=true result_str += f" URL: {p['url']}\n"
1122
+ block_count=4 cstruct_size=1 is_def=true result_str += '\n'
1123
+ block_count=3 cstruct_size=1 is_def=true return result_str
1124
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
1125
+ block_count=3 cstruct_size=1 is_def=true return f'Error executing search: {e}'
1126
+ block_count=0 cstruct_size=1 is_def=true class FindSimilarProductsInput(BaseModel):
1127
+ end of agent.SearchProductsTool
1128
+ class_name=agent.FindSimilarProductsInput
1129
+ base_name=BaseModel
1130
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
1131
+ compo c_name=Field
1132
+ block_count=0 cstruct_size=1 is_def=false class FindSimilarProductsTool(BaseTool):
1133
+ end of agent.FindSimilarProductsInput
1134
+ class_name=agent.FindSimilarProductsTool
1135
+ base_name=BaseTool
1136
+ block_count=1 cstruct_size=1 is_def=false name = 'find_similar_products'
1137
+ block_count=1 cstruct_size=1 is_def=false description = (
1138
+ 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.'
1139
+ block_count=2 cstruct_size=1 is_def=false )
1140
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = FindSimilarProductsInput
1141
+ block_count=1 cstruct_size=1 is_def=false agent_logs: str = ''
1142
+ block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
1143
+ block_count=2 cstruct_size=1 is_def=true print('\n--- Starting Similar Product Search ---')
1144
+ block_count=2 cstruct_size=1 is_def=true products = get_all_products()
1145
+ block_count=2 cstruct_size=1 is_def=true if not products:
1146
+ block_count=3 cstruct_size=1 is_def=true return 'No products in database to base search on.'
1147
+ block_count=2 cstruct_size=1 is_def=true target_products = []
1148
+ block_count=2 cstruct_size=1 is_def=true if query:
1149
+ block_count=3 cstruct_size=1 is_def=true print(f'Filtering products with query: {query}')
1150
+ block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
1151
+ compo c_name=query
1152
+ block_count=3 cstruct_size=1 is_def=true for p in products:
1153
+ block_count=4 cstruct_size=1 is_def=true if query_lower in p['name'].lower() or p['description'
1154
+ block_count=5 cstruct_size=1 is_def=true ] and query_lower in p['description'].lower():
1155
+ block_count=5 cstruct_size=1 is_def=true target_products.append(p)
1156
+ block_count=2 cstruct_size=1 is_def=true else:
1157
+ block_count=3 cstruct_size=1 is_def=true target_products = products
1158
+ block_count=2 cstruct_size=1 is_def=true if not target_products:
1159
+ block_count=3 cstruct_size=1 is_def=true return f"No products found matching query '{query}'."
1160
+ block_count=2 cstruct_size=1 is_def=true unique_products = {}
1161
+ block_count=2 cstruct_size=1 is_def=true for p in target_products:
1162
+ block_count=3 cstruct_size=1 is_def=true if p['name'] not in unique_products:
1163
+ block_count=4 cstruct_size=1 is_def=true unique_products[p['name']] = p
1164
+ block_count=2 cstruct_size=1 is_def=true target_names = list(unique_products.keys())
1165
+ block_count=2 cstruct_size=1 is_def=true print(
1166
+ block_count=3 cstruct_size=1 is_def=true f'Found {len(target_names)} target products to find similar items for.'
1167
+ block_count=3 cstruct_size=1 is_def=true )
1168
+ block_count=2 cstruct_size=1 is_def=true logs_context = ''
1169
+ block_count=2 cstruct_size=1 is_def=true if self.agent_logs:
1170
+ compo c_name=self
1171
+ block_count=3 cstruct_size=1 is_def=true logs_context = f'\n参考情報 (過去の検索履歴):\n{self.agent_logs}\n'
1172
+ block_count=2 cstruct_size=1 is_def=true search = get_search_tool_func()
1173
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
1174
+ compo c_name=os
1175
+ block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
1176
+ compo c_name=ChatGoogleGenerativeAI
1177
+ block_count=2 cstruct_size=1 is_def=true save_tool = SaveProductTool()
1178
+ compo c_name=SaveProductTool
1179
+ block_count=2 cstruct_size=1 is_def=true cached_similar_items = []
1180
+ block_count=2 cstruct_size=1 is_def=true max_targets = 5
1181
+ block_count=2 cstruct_size=1 is_def=true if len(target_names) > max_targets:
1182
+ block_count=3 cstruct_size=1 is_def=true print(f'Limiting search to first {max_targets} products.')
1183
+ block_count=3 cstruct_size=1 is_def=true target_names = target_names[:max_targets]
1184
+ block_count=2 cstruct_size=1 is_def=true for name in target_names:
1185
+ block_count=3 cstruct_size=1 is_def=true product_data = unique_products[name]
1186
+ block_count=3 cstruct_size=1 is_def=true description = product_data.get('description', '')
1187
+ block_count=3 cstruct_size=1 is_def=true print(f'Searching for similar items to: {name}')
1188
+ block_count=3 cstruct_size=1 is_def=true search_query = f'{name} 類似商品 おすすめ 比較 スペック'
1189
+ block_count=3 cstruct_size=1 is_def=true try:
1190
+ block_count=4 cstruct_size=1 is_def=true search_results = search.run(search_query)
1191
+ compo c_name=search
1192
+ block_count=3 cstruct_size=1 is_def=true except Exception as e:
1193
+ block_count=4 cstruct_size=1 is_def=true print(f'Search failed for {name}: {e}')
1194
+ block_count=4 cstruct_size=1 is_def=true continue
1195
+ block_count=3 cstruct_size=1 is_def=true prompt = f"""
1196
+ block_count=3 cstruct_size=1 is_def=true 以下の検索結果に基づいて、"{name}" に類似した、または競合する製品を抽出してください。
1197
+ block_count=3 cstruct_size=1 is_def=true データベースに既に存在する "{name}" は除外してください。
1198
+ block_count=3 cstruct_size=1 is_def=true 【重要】選定基準:
1199
+ block_count=3 cstruct_size=1 is_def=true 基準となる商品情報:
1200
+ block_count=3 cstruct_size=1 is_def=true 名前: {name}
1201
+ block_count=3 cstruct_size=1 is_def=true 詳細: {description}
1202
+ block_count=3 cstruct_size=1 is_def=true 上記の基準商品と比較して、「スペック(CPU、メモリ、ストレージ、機能など)が同等かそれ以上」の製品のみを厳選してください。
1203
+ block_count=3 cstruct_size=1 is_def=true 基準商品より明らかにスペックが劣る製品(例: 古い世代のCPU、少ないメモリ、低い解像度など)は絶対に含めないでください。
1204
+ block_count=3 cstruct_size=1 is_def=true 価格が安くてもスペックが低いものは除外します。
1205
+ block_count=3 cstruct_size=1 is_def=true {logs_context}
1206
+ block_count=3 cstruct_size=1 is_def=true 検索結果:
1207
+ block_count=3 cstruct_size=1 is_def=true {search_results}
1208
+ block_count=3 cstruct_size=1 is_def=true タスク:
1209
+ block_count=3 cstruct_size=1 is_def=true 条件に合う製品の 名前、価格、販売店舗、URL、簡単な説明、型番(model_number)、発売日(release_date) を抽出してJSONリストで返してください。型番や発売日が不明な場合は空文字列にしてください。
1210
+ block_count=3 cstruct_size=1 is_def=true 重要:
1211
+ block_count=3 cstruct_size=1 is_def=true - URLと詳細情報は必須です。URLは必ず http または https で始まる有効なものにしてください。これらが見つからない、または取得できない場合は、その商品はスキップしてください。
1212
+ block_count=3 cstruct_size=1 is_def=true - 価格が不明な場合、または商品名や価格に(例)などと記載されている場合もスキップしてください。
1213
+ block_count=3 cstruct_size=1 is_def=true - 【必須条件】検索結果のスニペットやページ内に「販売終了」「お探しのページは見つかりません」「404 Not Found」「この商品は現在お取り扱いできません」のいずれかが含まれている場合は、その商品は「無効」とみなし、絶対にリストに含めないでください(スキップしてください)。
1214
+ block_count=3 cstruct_size=1 is_def=true - メモリ(RAM)やSSDなどの容量を説明に含める際、「最大〇〇GB」「〇〇GBまで増設可能」といった拡張上限の数値は対象外とし、必ず「標準搭載の容量」を抽出してください。
1215
+ block_count=3 cstruct_size=1 is_def=true JSON出力例:
1216
+ block_count=3 cstruct_size=1 is_def=true [
1217
+ block_count=4 cstruct_size=1 is_def=true {{
1218
+ block_count=5 cstruct_size=1 is_def=true "name": "競合商品A",
1219
+ block_count=5 cstruct_size=1 is_def=true "store": "Amazon",
1220
+ block_count=5 cstruct_size=1 is_def=true "price": "5,000円",
1221
+ block_count=5 cstruct_size=1 is_def=true "url": "https://www.amazon.co.jp/...",
1222
+ block_count=5 cstruct_size=1 is_def=true "description": "商品Aの類似品。機能X搭載。",
1223
+ block_count=5 cstruct_size=1 is_def=true "model_number": "XYZ-999",
1224
+ block_count=5 cstruct_size=1 is_def=true "release_date": "2024-01-15"
1225
+ block_count=4 cstruct_size=1 is_def=true }}
1226
+ block_count=3 cstruct_size=1 is_def=true ]
1227
+ block_count=3 cstruct_size=1 is_def=true """
1228
+ block_count=3 cstruct_size=1 is_def=true try:
1229
+ block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt)
1230
+ compo c_name=llm
1231
+ block_count=4 cstruct_size=1 is_def=true content = response.content
1232
+ compo c_name=response
1233
+ block_count=4 cstruct_size=1 is_def=true if '```json' in content:
1234
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
1235
+ compo c_name=content
1236
+ block_count=4 cstruct_size=1 is_def=true elif '```' in content:
1237
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
1238
+ compo c_name=content
1239
+ block_count=4 cstruct_size=1 is_def=true items = json.loads(content)
1240
+ compo c_name=json
1241
+ block_count=4 cstruct_size=1 is_def=true if isinstance(items, list):
1242
+ block_count=5 cstruct_size=1 is_def=true for item in items:
1243
+ block_count=6 cstruct_size=1 is_def=true if item.get('name') == name:
1244
+ compo c_name=item
1245
+ block_count=7 cstruct_size=1 is_def=true continue
1246
+ block_count=6 cstruct_size=1 is_def=true cached_similar_items.append(item)
1247
+ block_count=6 cstruct_size=1 is_def=true print(
1248
+ block_count=7 cstruct_size=1 is_def=true f" Cached: {item.get('name')} ({item.get('price')})"
1249
+ block_count=7 cstruct_size=1 is_def=true )
1250
+ block_count=3 cstruct_size=1 is_def=true except Exception as e:
1251
+ block_count=4 cstruct_size=1 is_def=true print(f'Error processing similar items for {name}: {e}')
1252
+ block_count=3 cstruct_size=1 is_def=true time.sleep(1)
1253
+ compo c_name=time
1254
+ block_count=2 cstruct_size=1 is_def=true if not cached_similar_items:
1255
+ block_count=3 cstruct_size=1 is_def=true return 'No similar products found.'
1256
+ block_count=2 cstruct_size=1 is_def=true print(
1257
+ block_count=3 cstruct_size=1 is_def=true f'\nCached {len(cached_similar_items)} items. Selecting top 3 recommendations...'
1258
+ block_count=3 cstruct_size=1 is_def=true )
1259
+ block_count=2 cstruct_size=1 is_def=true selection_prompt = f"""
1260
+ block_count=2 cstruct_size=1 is_def=true 以下の類似商品リストから、最もおすすめの製品を最大3つ選んでください。
1261
+ block_count=2 cstruct_size=1 is_def=true 選定基準:
1262
+ block_count=2 cstruct_size=1 is_def=true 1. 元の商品と同等かそれ以上の性能・品質であること。
1263
+ block_count=2 cstruct_size=1 is_def=true 2. 価格と性能のバランスが良いこと。
1264
+ block_count=2 cstruct_size=1 is_def=true 3. 詳細情報が豊富であること。
1265
+ block_count=2 cstruct_size=1 is_def=true {logs_context}
1266
+ block_count=2 cstruct_size=1 is_def=true 候補リスト:
1267
+ block_count=2 cstruct_size=1 is_def=true {json.dumps(cached_similar_items, ensure_ascii=False, indent=2)}
1268
+ block_count=2 cstruct_size=1 is_def=true タスク:
1269
+ block_count=2 cstruct_size=1 is_def=true 選定した3つの商品をJSONリスト形式で返してください。形式は入力と同じです。
1270
+ block_count=2 cstruct_size=1 is_def=true """
1271
+ block_count=2 cstruct_size=1 is_def=true added_count = 0
1272
+ block_count=2 cstruct_size=1 is_def=true try:
1273
+ block_count=3 cstruct_size=1 is_def=true response = llm.invoke(selection_prompt)
1274
+ compo c_name=llm
1275
+ block_count=3 cstruct_size=1 is_def=true content = response.content
1276
+ compo c_name=response
1277
+ block_count=3 cstruct_size=1 is_def=true if '```json' in content:
1278
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
1279
+ compo c_name=content
1280
+ block_count=3 cstruct_size=1 is_def=true elif '```' in content:
1281
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
1282
+ compo c_name=content
1283
+ block_count=3 cstruct_size=1 is_def=true top_picks = json.loads(content)
1284
+ compo c_name=json
1285
+ block_count=3 cstruct_size=1 is_def=true if isinstance(top_picks, list):
1286
+ block_count=4 cstruct_size=1 is_def=true for item in top_picks:
1287
+ block_count=5 cstruct_size=1 is_def=true print(f" Saving recommendation: {item.get('name')}")
1288
+ block_count=5 cstruct_size=1 is_def=true res = save_tool._run(name=item.get('name'), store=item.
1289
+ block_count=6 cstruct_size=1 is_def=true get('store', 'Unknown'), price=item.get('price'),
1290
+ block_count=6 cstruct_size=1 is_def=true url=item.get('url', ''), description=item.get(
1291
+ block_count=6 cstruct_size=1 is_def=true 'description', ''), model_number=item.get(
1292
+ block_count=6 cstruct_size=1 is_def=true 'model_number', ''), release_date=item.get(
1293
+ block_count=6 cstruct_size=1 is_def=true 'release_date', ''))
1294
+ block_count=5 cstruct_size=1 is_def=true print(f' -> {res}')
1295
+ block_count=5 cstruct_size=1 is_def=true added_count += 1
1296
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
1297
+ block_count=3 cstruct_size=1 is_def=true return f'Error selecting top recommendations: {e}'
1298
+ block_count=2 cstruct_size=1 is_def=true return (
1299
+ block_count=3 cstruct_size=1 is_def=true f'Similar product search complete. Added {added_count} recommended items.'
1300
+ block_count=3 cstruct_size=1 is_def=true )
1301
+ block_count=0 cstruct_size=1 is_def=true class CompareProductsInput(BaseModel):
1302
+ end of agent.FindSimilarProductsTool
1303
+ class_name=agent.CompareProductsInput
1304
+ base_name=BaseModel
1305
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
1306
+ compo c_name=Field
1307
+ block_count=2 cstruct_size=1 is_def=false "Optional category or query to filter products for comparison (e.g., 'laptop', 'monitor')."
1308
+ block_count=2 cstruct_size=1 is_def=false , default='')
1309
+ block_count=0 cstruct_size=1 is_def=false class CompareProductsTool(BaseTool):
1310
+ end of agent.CompareProductsInput
1311
+ class_name=agent.CompareProductsTool
1312
+ base_name=BaseTool
1313
+ block_count=1 cstruct_size=1 is_def=false name = 'compare_products'
1314
+ block_count=1 cstruct_size=1 is_def=false description = (
1315
+ 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.'
1316
+ block_count=2 cstruct_size=1 is_def=false )
1317
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = CompareProductsInput
1318
+ block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
1319
+ block_count=2 cstruct_size=1 is_def=true print(f'\n--- Generating Product Comparison: {query} ---')
1320
+ block_count=2 cstruct_size=1 is_def=true products = get_all_products()
1321
+ block_count=2 cstruct_size=1 is_def=true if not products:
1322
+ block_count=3 cstruct_size=1 is_def=true return 'No products found in database.'
1323
+ block_count=2 cstruct_size=1 is_def=true target_products = []
1324
+ block_count=2 cstruct_size=1 is_def=true if query:
1325
+ block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
1326
+ compo c_name=query
1327
+ block_count=3 cstruct_size=1 is_def=true for p in products:
1328
+ block_count=4 cstruct_size=1 is_def=true text = (p['name'] + ' ' + (p['description'] or '')).lower()
1329
+ block_count=4 cstruct_size=1 is_def=true if query_lower in text:
1330
+ block_count=5 cstruct_size=1 is_def=true target_products.append(p)
1331
+ block_count=2 cstruct_size=1 is_def=true else:
1332
+ block_count=3 cstruct_size=1 is_def=true target_products = products
1333
+ block_count=2 cstruct_size=1 is_def=true if not target_products:
1334
+ block_count=3 cstruct_size=1 is_def=true return f"No products found matching '{query}'."
1335
+ block_count=2 cstruct_size=1 is_def=true CHUNK_SIZE = 5
1336
+ block_count=2 cstruct_size=1 is_def=true print(
1337
+ block_count=3 cstruct_size=1 is_def=true f'Step 1: Extracting specs from {len(target_products)} products in chunks of {CHUNK_SIZE}...'
1338
+ block_count=3 cstruct_size=1 is_def=true )
1339
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
1340
+ compo c_name=os
1341
+ block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
1342
+ compo c_name=ChatGoogleGenerativeAI
1343
+ block_count=3 cstruct_size=1 is_def=true max_output_tokens=8192)
1344
+ block_count=2 cstruct_size=1 is_def=true extracted_specs = []
1345
+ block_count=2 cstruct_size=1 is_def=true try:
1346
+ block_count=3 cstruct_size=1 is_def=true for i in range(0, len(target_products), CHUNK_SIZE):
1347
+ block_count=4 cstruct_size=1 is_def=true chunk = target_products[i:i + CHUNK_SIZE]
1348
+ block_count=4 cstruct_size=1 is_def=true print(
1349
+ block_count=5 cstruct_size=1 is_def=true f' Processing chunk {i // CHUNK_SIZE + 1} ({len(chunk)} items)...'
1350
+ block_count=5 cstruct_size=1 is_def=true )
1351
+ block_count=4 cstruct_size=1 is_def=true prompt_extract = f"""
1352
+ block_count=4 cstruct_size=1 is_def=true 以下の製品リストから、各製品の主要スペック情報を抽出してください。
1353
+ block_count=4 cstruct_size=1 is_def=true 製品リスト:
1354
+ block_count=4 cstruct_size=1 is_def=true {json.dumps([{k: v for k, v in p.items() if k != 'updated_at'} for p in chunk], ensure_ascii=False, indent=2)}
1355
+ block_count=4 cstruct_size=1 is_def=true タスク:
1356
+ block_count=4 cstruct_size=1 is_def=true 各製品について以下の情報を抽出し、JSONリスト形式で出力してください:
1357
+ block_count=4 cstruct_size=1 is_def=true 1. id: 元の製品ID (必須)
1358
+ block_count=4 cstruct_size=1 is_def=true 2. name: 製品名
1359
+ block_count=4 cstruct_size=1 is_def=true 3. price: 価格 (そのまま)
1360
+ block_count=4 cstruct_size=1 is_def=true 4. url: 製品ページのURL
1361
+ block_count=4 cstruct_size=1 is_def=true 5. ram: メモリ容量 (例: "16GB", "8GB", 不明なら "-") ※「最大〇〇GB」「増設可能」は無視し、標準搭載量のみを抽出すること。
1362
+ block_count=4 cstruct_size=1 is_def=true 6. ssd: ストレージ容量 (例: "512GB", "1TB", 不明なら "-") ※「最大〇〇GB」「増設可能」は無視し、標準搭載量のみを抽出すること。
1363
+ block_count=4 cstruct_size=1 is_def=true 7. cpu: プロセッサ (例: "Core i5", "M2", 不明なら "-")
1364
+ block_count=4 cstruct_size=1 is_def=true 8. os: OSの種類 (例: "Windows 11", "macOS", "ChromeOS", 不明なら "-")
1365
+ block_count=4 cstruct_size=1 is_def=true 9. model_number: 型番 (不明なら "-")
1366
+ block_count=4 cstruct_size=1 is_def=true 10. release_date: 発売日 (不明なら "-")
1367
+ block_count=4 cstruct_size=1 is_def=true 出力はJSONのみとし、Markdownコードブロックで囲ってください。
1368
+ block_count=4 cstruct_size=1 is_def=true """
1369
+ block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt_extract)
1370
+ compo c_name=llm
1371
+ block_count=4 cstruct_size=1 is_def=true content = response.content
1372
+ compo c_name=response
1373
+ block_count=4 cstruct_size=1 is_def=true if '```json' in content:
1374
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
1375
+ compo c_name=content
1376
+ block_count=4 cstruct_size=1 is_def=true elif '```' in content:
1377
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
1378
+ compo c_name=content
1379
+ block_count=4 cstruct_size=1 is_def=true content = content.strip()
1380
+ compo c_name=content
1381
+ block_count=4 cstruct_size=1 is_def=true try:
1382
+ block_count=5 cstruct_size=1 is_def=true chunk_data = json.loads(content)
1383
+ compo c_name=json
1384
+ block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
1385
+ block_count=5 cstruct_size=1 is_def=true print(
1386
+ block_count=6 cstruct_size=1 is_def=true f' [Warning] Failed to parse JSON in chunk {i // CHUNK_SIZE + 1}. Attempting aggressive recovery.'
1387
+ block_count=6 cstruct_size=1 is_def=true )
1388
+ block_count=5 cstruct_size=1 is_def=true print(f' Error detail: {e}')
1389
+ block_count=5 cstruct_size=1 is_def=true try:
1390
+ block_count=6 cstruct_size=1 is_def=true start_idx = content.find('[')
1391
+ compo c_name=content
1392
+ block_count=6 cstruct_size=1 is_def=true if start_idx != -1:
1393
+ block_count=7 cstruct_size=1 is_def=true clean_content = content[start_idx:]
1394
+ block_count=7 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
1395
+ block_count=8 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
1396
+ block_count=8 cstruct_size=1 is_def=true if last_brace != -1:
1397
+ block_count=9 cstruct_size=1 is_def=true clean_content = clean_content[:
1398
+ block_count=10 cstruct_size=1 is_def=true last_brace + 1] + ']'
1399
+ block_count=8 cstruct_size=1 is_def=true else:
1400
+ block_count=9 cstruct_size=1 is_def=true clean_content += ']'
1401
+ block_count=7 cstruct_size=1 is_def=true chunk_data = json.loads(clean_content)
1402
+ compo c_name=json
1403
+ block_count=6 cstruct_size=1 is_def=true else:
1404
+ block_count=7 cstruct_size=1 is_def=true raise ValueError('No JSON list found.')
1405
+ compo c_name=ValueError
1406
+ block_count=5 cstruct_size=1 is_def=true except Exception as e2:
1407
+ block_count=6 cstruct_size=1 is_def=true print(
1408
+ block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover JSON even after aggressive repair: {e2}'
1409
+ block_count=7 cstruct_size=1 is_def=true )
1410
+ block_count=6 cstruct_size=1 is_def=true continue
1411
+ block_count=4 cstruct_size=1 is_def=true if isinstance(chunk_data, list):
1412
+ block_count=5 cstruct_size=1 is_def=true extracted_specs.extend(chunk_data)
1413
+ block_count=4 cstruct_size=1 is_def=true time.sleep(1)
1414
+ compo c_name=time
1415
+ block_count=3 cstruct_size=1 is_def=true print(
1416
+ block_count=4 cstruct_size=1 is_def=true f'Step 2: Ranking {len(extracted_specs)} products based on extracted specs...'
1417
+ block_count=4 cstruct_size=1 is_def=true )
1418
+ block_count=3 cstruct_size=1 is_def=true prompt_rank = f"""
1419
+ block_count=3 cstruct_size=1 is_def=true 以下の製品の主要スペック一覧を分析し、価格と性能のバランスに基づいて全体の中からランキングを付けてください。
1420
+ block_count=3 cstruct_size=1 is_def=true 製品スペックリスト:
1421
+ block_count=3 cstruct_size=1 is_def=true {json.dumps(extracted_specs, ensure_ascii=False, indent=2)}
1422
+ block_count=3 cstruct_size=1 is_def=true タスク:
1423
+ block_count=3 cstruct_size=1 is_def=true 各製品に対して、以下の3つの情報のみを含むJSONリスト形式で出力してください:
1424
+ block_count=3 cstruct_size=1 is_def=true 1. id: 元の製品ID (必須)
1425
+ block_count=3 cstruct_size=1 is_def=true 2. note: 詳細なコメント (推奨理由、メリット・デメリット、他の製品と比較した際の特徴などを具体的に記述してください。例: "同価格帯の中で最もCPU性能が高く、動画編集に適している", "価格は安いがメモリが少ないため、軽作業向け")
1426
+ block_count=3 cstruct_size=1 is_def=true 3. rank: 全体の中での「おすすめ順位」 (1から始まる連番)
1427
+ block_count=3 cstruct_size=1 is_def=true 重要: 出力トークンを節約するため、nameやprice, url, specs(ram/ssd/cpu/os)等の再出力は絶対にしないでください。「id」「note」「rank」の3つだけを出力してください。
1428
+ block_count=3 cstruct_size=1 is_def=true 出力はJSONのみとし、Markdownコードブロックで囲ってください。
1429
+ block_count=3 cstruct_size=1 is_def=true リストの並び順は、rank(1位から順番)にしてください。
1430
+ block_count=3 cstruct_size=1 is_def=true """
1431
+ block_count=3 cstruct_size=1 is_def=true response_rank = llm.invoke(prompt_rank)
1432
+ compo c_name=llm
1433
+ block_count=3 cstruct_size=1 is_def=true content_rank = response_rank.content
1434
+ block_count=3 cstruct_size=1 is_def=true if '```json' in content_rank:
1435
+ block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```json')[1].split('```')[0]
1436
+ block_count=3 cstruct_size=1 is_def=true elif '```' in content_rank:
1437
+ block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```')[1].split('```')[0]
1438
+ block_count=3 cstruct_size=1 is_def=true content_rank = content_rank.strip()
1439
+ block_count=3 cstruct_size=1 is_def=true try:
1440
+ block_count=4 cstruct_size=1 is_def=true ranking_data = json.loads(content_rank)
1441
+ compo c_name=json
1442
+ block_count=3 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
1443
+ block_count=4 cstruct_size=1 is_def=true print(
1444
+ block_count=5 cstruct_size=1 is_def=true f' [Warning] Failed to parse ranking JSON. Error: {e}. Attempting recovery.'
1445
+ block_count=5 cstruct_size=1 is_def=true )
1446
+ block_count=4 cstruct_size=1 is_def=true start_idx = content_rank.find('[')
1447
+ block_count=4 cstruct_size=1 is_def=true if start_idx != -1:
1448
+ block_count=5 cstruct_size=1 is_def=true clean_content = content_rank[start_idx:]
1449
+ block_count=5 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
1450
+ block_count=6 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
1451
+ block_count=6 cstruct_size=1 is_def=true if last_brace != -1:
1452
+ block_count=7 cstruct_size=1 is_def=true clean_content = clean_content[:last_brace + 1
1453
+ block_count=8 cstruct_size=1 is_def=true ] + ']'
1454
+ block_count=6 cstruct_size=1 is_def=true else:
1455
+ block_count=7 cstruct_size=1 is_def=true clean_content += ']'
1456
+ block_count=5 cstruct_size=1 is_def=true try:
1457
+ block_count=6 cstruct_size=1 is_def=true ranking_data = json.loads(clean_content)
1458
+ compo c_name=json
1459
+ block_count=5 cstruct_size=1 is_def=true except Exception as e2:
1460
+ block_count=6 cstruct_size=1 is_def=true print(
1461
+ block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover ranking JSON: {e2}'
1462
+ block_count=7 cstruct_size=1 is_def=true )
1463
+ block_count=6 cstruct_size=1 is_def=true return (
1464
+ block_count=7 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
1465
+ block_count=7 cstruct_size=1 is_def=true )
1466
+ block_count=4 cstruct_size=1 is_def=true else:
1467
+ block_count=5 cstruct_size=1 is_def=true return (
1468
+ block_count=6 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
1469
+ block_count=6 cstruct_size=1 is_def=true )
1470
+ block_count=3 cstruct_size=1 is_def=true spec_dict = {item['id']: item for item in extracted_specs}
1471
+ block_count=3 cstruct_size=1 is_def=true target_dict = {item['id']: item for item in target_products}
1472
+ block_count=3 cstruct_size=1 is_def=true final_comparison_data = []
1473
+ block_count=3 cstruct_size=1 is_def=true for rank_item in ranking_data:
1474
+ block_count=4 cstruct_size=1 is_def=true p_id = rank_item.get('id')
1475
+ block_count=4 cstruct_size=1 is_def=true if p_id in spec_dict:
1476
+ block_count=5 cstruct_size=1 is_def=true merged = spec_dict[p_id].copy()
1477
+ block_count=5 cstruct_size=1 is_def=true merged['rank'] = rank_item.get('rank')
1478
+ block_count=5 cstruct_size=1 is_def=true merged['note'] = rank_item.get('note', '')
1479
+ block_count=5 cstruct_size=1 is_def=true if p_id in target_dict:
1480
+ block_count=6 cstruct_size=1 is_def=true merged['updated_at'] = target_dict[p_id].get(
1481
+ block_count=7 cstruct_size=1 is_def=true 'updated_at', '')
1482
+ block_count=5 cstruct_size=1 is_def=true final_comparison_data.append(merged)
1483
+ block_count=3 cstruct_size=1 is_def=true final_comparison_data.sort(key=lambda x: x.get('rank', 9999))
1484
+ block_count=3 cstruct_size=1 is_def=true for item in final_comparison_data:
1485
+ block_count=4 cstruct_size=1 is_def=true if 'model_number' not in item or item['model_number'] is None:
1486
+ block_count=5 cstruct_size=1 is_def=true item['model_number'] = ''
1487
+ block_count=4 cstruct_size=1 is_def=true if 'release_date' not in item or item['release_date'] is None:
1488
+ block_count=5 cstruct_size=1 is_def=true item['release_date'] = ''
1489
+ block_count=3 cstruct_size=1 is_def=true from datetime import datetime
1490
+ block_count=3 cstruct_size=1 is_def=true output_data = {'updated_at': datetime.now().strftime(
1491
+ compo c_name=datetime
1492
+ block_count=4 cstruct_size=1 is_def=true '%Y-%m-%d %H:%M:%S'), 'products': final_comparison_data}
1493
+ block_count=3 cstruct_size=1 is_def=true output_file = 'product_comparison.json'
1494
+ block_count=3 cstruct_size=1 is_def=true with open(output_file, 'w', encoding='utf-8') as f:
1495
+ block_count=4 cstruct_size=1 is_def=true json.dump(output_data, f, ensure_ascii=False, indent=2)
1496
+ compo c_name=json
1497
+ block_count=3 cstruct_size=1 is_def=true return (
1498
+ 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.'
1499
+ block_count=4 cstruct_size=1 is_def=true )
1500
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
1501
+ block_count=3 cstruct_size=1 is_def=true return f'Error generating comparison: {e}'
1502
+ block_count=0 cstruct_size=1 is_def=true def display_products():
1503
+ end of agent.CompareProductsTool
1504
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
1505
+ compo c_name=sqlite3
1506
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
1507
+ compo c_name=conn
1508
+ block_count=1 cstruct_size=0 is_def=true cursor.execute('SELECT id, name, store, price FROM products')
1509
+ compo c_name=cursor
1510
+ block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
1511
+ compo c_name=cursor
1512
+ block_count=1 cstruct_size=0 is_def=true conn.close()
1513
+ compo c_name=conn
1514
+ block_count=1 cstruct_size=0 is_def=true if not rows:
1515
+ block_count=2 cstruct_size=0 is_def=true print('\nNo products saved yet.')
1516
+ block_count=2 cstruct_size=0 is_def=true return
1517
+ block_count=1 cstruct_size=0 is_def=true def parse_price_sort(p_str):
1518
+ block_count=2 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(p_str).replace(',', ''))
1519
+ compo c_name=re
1520
+ block_count=2 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
1521
+ block_count=1 cstruct_size=0 is_def=true rows.sort(key=lambda x: (x[1], parse_price_sort(x[3])))
1522
+ compo c_name=rows
1523
+ block_count=1 cstruct_size=0 is_def=false print('\n--- All Saved Products ---')
1524
+ block_count=1 cstruct_size=0 is_def=false print(f"{'ID':<5} {'Name':<40} {'Store':<20} {'Price':<15}")
1525
+ block_count=1 cstruct_size=0 is_def=false print('-' * 85)
1526
+ block_count=1 cstruct_size=0 is_def=false for row in rows:
1527
+ block_count=2 cstruct_size=0 is_def=false name_disp = row[1][:37] + '..' if len(row[1]) > 39 else row[1]
1528
+ block_count=2 cstruct_size=0 is_def=false store_disp = row[2][:18] + '..' if len(row[2]) > 20 else row[2]
1529
+ block_count=2 cstruct_size=0 is_def=false print(f'{row[0]:<5} {name_disp:<40} {store_disp:<20} {row[3]:<15}')
1530
+ block_count=1 cstruct_size=0 is_def=false print('-' * 85)
1531
+ block_count=0 cstruct_size=0 is_def=false def show_product_details(product_id):
1532
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
1533
+ compo c_name=sqlite3
1534
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
1535
+ compo c_name=conn
1536
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
1537
+ compo c_name=cursor
1538
+ block_count=2 cstruct_size=0 is_def=true 'SELECT id, name, store, price, url, description, model_number, release_date FROM products WHERE id = ?'
1539
+ block_count=2 cstruct_size=0 is_def=true , (product_id,))
1540
+ block_count=1 cstruct_size=0 is_def=true row = cursor.fetchone()
1541
+ compo c_name=cursor
1542
+ block_count=1 cstruct_size=0 is_def=true conn.close()
1543
+ compo c_name=conn
1544
+ block_count=1 cstruct_size=0 is_def=true if not row:
1545
+ block_count=2 cstruct_size=0 is_def=true print(f'\nProduct with ID {product_id} not found.')
1546
+ block_count=2 cstruct_size=0 is_def=true return
1547
+ block_count=1 cstruct_size=0 is_def=true print('\n--- Product Details ---')
1548
+ block_count=1 cstruct_size=0 is_def=true print(f'ID: {row[0]}')
1549
+ block_count=1 cstruct_size=0 is_def=true print(f'Name: {row[1]}')
1550
+ block_count=1 cstruct_size=0 is_def=true print(f'Store: {row[2]}')
1551
+ block_count=1 cstruct_size=0 is_def=true print(f'Price: {row[3]}')
1552
+ block_count=1 cstruct_size=0 is_def=true print(f"Model: {row[6] if len(row) > 6 else ''}")
1553
+ block_count=1 cstruct_size=0 is_def=true print(f"Release: {row[7] if len(row) > 7 else ''}")
1554
+ block_count=1 cstruct_size=0 is_def=true print(f'URL: {row[4]}')
1555
+ block_count=1 cstruct_size=0 is_def=true print(f'Description: {row[5]}')
1556
+ block_count=1 cstruct_size=0 is_def=true print('-' * 30)
1557
+ block_count=0 cstruct_size=0 is_def=true def delete_product_records(identifiers: List[str]):
1558
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
1559
+ compo c_name=sqlite3
1560
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
1561
+ compo c_name=conn
1562
+ block_count=1 cstruct_size=0 is_def=true deleted_count = 0
1563
+ block_count=1 cstruct_size=0 is_def=true errors = []
1564
+ block_count=1 cstruct_size=0 is_def=true print(f'\nAttempting to delete: {identifiers}')
1565
+ block_count=1 cstruct_size=0 is_def=true for identifier in identifiers:
1566
+ block_count=2 cstruct_size=0 is_def=true try:
1567
+ block_count=3 cstruct_size=0 is_def=true if identifier.isdigit():
1568
+ compo c_name=identifier
1569
+ block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (int(
1570
+ compo c_name=cursor
1571
+ block_count=5 cstruct_size=0 is_def=true identifier),))
1572
+ block_count=3 cstruct_size=0 is_def=true else:
1573
+ block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (
1574
+ compo c_name=cursor
1575
+ block_count=5 cstruct_size=0 is_def=true identifier,))
1576
+ block_count=3 cstruct_size=0 is_def=true if cursor.rowcount > 0:
1577
+ compo c_name=cursor
1578
+ block_count=4 cstruct_size=0 is_def=true deleted_count += cursor.rowcount
1579
+ compo c_name=cursor
1580
+ block_count=4 cstruct_size=0 is_def=true print(f' Deleted: {identifier}')
1581
+ block_count=3 cstruct_size=0 is_def=true else:
1582
+ block_count=4 cstruct_size=0 is_def=true errors.append(f'No product found with ID/Name: {identifier}')
1583
+ compo c_name=errors
1584
+ block_count=2 cstruct_size=0 is_def=true except Exception as e:
1585
+ block_count=3 cstruct_size=0 is_def=true errors.append(f'Error deleting {identifier}: {e}')
1586
+ compo c_name=errors
1587
+ block_count=1 cstruct_size=0 is_def=true conn.commit()
1588
+ compo c_name=conn
1589
+ block_count=1 cstruct_size=0 is_def=true conn.close()
1590
+ compo c_name=conn
1591
+ block_count=1 cstruct_size=0 is_def=true print(f'\nTotal deleted: {deleted_count}')
1592
+ block_count=1 cstruct_size=0 is_def=true if errors:
1593
+ block_count=2 cstruct_size=0 is_def=true print('Errors/Warnings:')
1594
+ block_count=2 cstruct_size=0 is_def=true for err in errors:
1595
+ block_count=3 cstruct_size=0 is_def=true print(f' - {err}')
1596
+ block_count=0 cstruct_size=0 is_def=true def main():
1597
+ block_count=1 cstruct_size=0 is_def=true if not os.getenv('GOOGLE_API_KEY'):
1598
+ compo c_name=os
1599
+ block_count=2 cstruct_size=0 is_def=true print('Error: GOOGLE_API_KEY not found.')
1600
+ block_count=2 cstruct_size=0 is_def=true return
1601
+ block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
1602
+ compo c_name=os
1603
+ block_count=1 cstruct_size=0 is_def=true if provider == 'serpapi' and not os.getenv('SERPAPI_API_KEY'):
1604
+ compo c_name=os
1605
+ block_count=2 cstruct_size=0 is_def=true print('Error: SERPAPI_API_KEY not found.')
1606
+ block_count=2 cstruct_size=0 is_def=true return
1607
+ block_count=1 cstruct_size=0 is_def=true elif provider == 'tavily_api' and not os.getenv('TAVILY_API_KEY'):
1608
+ compo c_name=os
1609
+ block_count=2 cstruct_size=0 is_def=true print('Error: TAVILY_API_KEY not found for Tavily search.')
1610
+ block_count=2 cstruct_size=0 is_def=true return
1611
+ block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
1612
+ block_count=2 cstruct_size=0 is_def=true pass
1613
+ block_count=1 cstruct_size=0 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
1614
+ compo c_name=os
1615
+ block_count=1 cstruct_size=0 is_def=true print(f'Using model: {model_name}')
1616
+ block_count=1 cstruct_size=0 is_def=true print(f'Using Search Provider: {provider}')
1617
+ block_count=1 cstruct_size=0 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
1618
+ compo c_name=ChatGoogleGenerativeAI
1619
+ block_count=2 cstruct_size=0 is_def=true max_retries=10)
1620
+ block_count=1 cstruct_size=0 is_def=true search = get_search_tool_func()
1621
+ block_count=1 cstruct_size=0 is_def=true search_tool = Tool(name='google_search', description=
1622
+ compo c_name=Tool
1623
+ block_count=2 cstruct_size=0 is_def=true 'Search Google for recent results.', func=search.run)
1624
+ block_count=1 cstruct_size=0 is_def=true startup_logs = get_all_agent_logs()
1625
+ block_count=1 cstruct_size=0 is_def=true print(f'Loaded {len(startup_logs)} characters of agent logs.')
1626
+ block_count=1 cstruct_size=0 is_def=true save_tool = SaveProductTool()
1627
+ compo c_name=SaveProductTool
1628
+ block_count=1 cstruct_size=0 is_def=true db_search_tool = SearchProductsTool()
1629
+ compo c_name=SearchProductsTool
1630
+ block_count=1 cstruct_size=0 is_def=true update_tool = UpdatePricesTool()
1631
+ compo c_name=UpdatePricesTool
1632
+ block_count=1 cstruct_size=0 is_def=true similar_tool = FindSimilarProductsTool(agent_logs=startup_logs)
1633
+ compo c_name=FindSimilarProductsTool
1634
+ block_count=1 cstruct_size=0 is_def=true compare_tool = CompareProductsTool()
1635
+ compo c_name=CompareProductsTool
1636
+ block_count=1 cstruct_size=0 is_def=true tools = [search_tool, save_tool, db_search_tool, update_tool,
1637
+ block_count=2 cstruct_size=0 is_def=true similar_tool, compare_tool]
1638
+ block_count=1 cstruct_size=0 is_def=true prompt = ChatPromptTemplate.from_messages([('system',
1639
+ compo c_name=ChatPromptTemplate
1640
+ block_count=2 cstruct_size=0 is_def=true """あなたは、商品の検索、保存、価格更新、類似商品検索、比較表作成を行う有能なアシスタントです。
1641
+ block_count=2 cstruct_size=0 is_def=true 利用可能なツール:
1642
+ block_count=2 cstruct_size=0 is_def=true 1. google_search: インターネット上の商品情報の検索に使用します。
1643
+ block_count=2 cstruct_size=0 is_def=true 2. save_product: 商品情報をデータベースに保存します。
1644
+ block_count=2 cstruct_size=0 is_def=true 3. search_products: データベース内に保存された商品を自然言語で検索します(例:「安いもの」「メモリが多いもの」)。
1645
+ block_count=2 cstruct_size=0 is_def=true 4. update_prices: データベース内の全商品の価格を最新の状態に更新します。
1646
+ block_count=2 cstruct_size=0 is_def=true 5. find_similar_products: データベース内の商品に類似した商品を探して追加します。
1647
+ block_count=2 cstruct_size=0 is_def=true 6. compare_products: データベース内の商品の比較表(RAM, SSD, 価格など)を作成し、おすすめ順に並べます。
1648
+ block_count=2 cstruct_size=0 is_def=true 重要: 検索を行って商品が見つかった場合は、必ず `save_product` ツールを使用して、見つかった各商品をデータベースに保存してください。
1649
+ block_count=2 cstruct_size=0 is_def=true 保存する際は、商品名、価格、店舗名、詳細、URLを含めてください。
1650
+ block_count=2 cstruct_size=0 is_def=true 【重要】URLと詳細情報(description)は保存において必須項目です。
1651
+ block_count=2 cstruct_size=0 is_def=true 特にURLは `http://` または `https://` で始まる有効な形式である必要があります。
1652
+ block_count=2 cstruct_size=0 is_def=true これらが取得できない場合やURLが無効な場合は、その商品は保存しないでください。
1653
+ block_count=2 cstruct_size=0 is_def=true 検索結果から情報を抽出する際は、これらの項目を必ず探してください。
1654
+ block_count=2 cstruct_size=0 is_def=true また、可能であれば「型番(model_number)」と「発売日(release_date)」も抽出・保存してください。
1655
+ block_count=2 cstruct_size=0 is_def=true 【無効な商品の保存禁止】
1656
+ block_count=2 cstruct_size=0 is_def=true 検索結果のスニペットや実際のページ内に、以下のいずれかの文言が含まれている商品は、現在利用できない無効な商品です。これらは絶対に `save_product` でデータベースに保存しないでください。
1657
+ block_count=2 cstruct_size=0 is_def=true - 「販売終了」
1658
+ block_count=2 cstruct_size=0 is_def=true - 「お探しのページは見つかりません」
1659
+ block_count=2 cstruct_size=0 is_def=true - 「404 Not Found」
1660
+ block_count=2 cstruct_size=0 is_def=true - 「この商品は現在お取り扱いできません」
1661
+ block_count=2 cstruct_size=0 is_def=true 価格情報が曖昧な場合(例:「10万円以下」)でも、上記の無効条件に該当せず、URLと詳細情報があれば `save_product` を使用して保存してください。
1662
+ block_count=2 cstruct_size=0 is_def=true その際、priceフィールドには見つかったテキスト(例:「10万円以下」)を入力してください。
1663
+ block_count=2 cstruct_size=0 is_def=true ユーザーの指示に従って適切なツールを使用してください。
1664
+ block_count=2 cstruct_size=0 is_def=true 「価格を更新して」と言われたら update_prices を使用してください。
1665
+ block_count=2 cstruct_size=0 is_def=true 「類似商品を探して」と言われたら find_similar_products を使用してください。
1666
+ block_count=2 cstruct_size=0 is_def=true """
1667
+ block_count=2 cstruct_size=0 is_def=true ), MessagesPlaceholder(variable_name='chat_history'), ('human',
1668
+ compo c_name=MessagesPlaceholder
1669
+ block_count=2 cstruct_size=0 is_def=true '{input}'), ('placeholder', '{agent_scratchpad}')])
1670
+ block_count=1 cstruct_size=0 is_def=true agent = create_tool_calling_agent(llm, tools, prompt)
1671
+ block_count=1 cstruct_size=0 is_def=true agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True,
1672
+ compo c_name=AgentExecutor
1673
+ block_count=2 cstruct_size=0 is_def=true return_intermediate_steps=True)
1674
+ block_count=1 cstruct_size=0 is_def=true print('Advanced AI Agent initialized.')
1675
+ block_count=1 cstruct_size=0 is_def=true print(
1676
+ block_count=2 cstruct_size=0 is_def=true "Commands: 'list', 'show <ID>', 'update', 'similar', 'delete <ID/Name> ...', 'quit'"
1677
+ block_count=2 cstruct_size=0 is_def=true )
1678
+ block_count=1 cstruct_size=0 is_def=true chat_history = []
1679
+ block_count=1 cstruct_size=0 is_def=true while True:
1680
+ block_count=2 cstruct_size=0 is_def=true try:
1681
+ block_count=3 cstruct_size=0 is_def=true try:
1682
+ block_count=4 cstruct_size=0 is_def=true user_input = input('\nEnter command or search query: ')
1683
+ block_count=3 cstruct_size=0 is_def=true except EOFError:
1684
+ block_count=4 cstruct_size=0 is_def=true print('\nEOF detected. Exiting...')
1685
+ block_count=4 cstruct_size=0 is_def=true break
1686
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() in ['quit', 'exit']:
1687
+ block_count=4 cstruct_size=0 is_def=true break
1688
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'list':
1689
+ block_count=4 cstruct_size=0 is_def=true display_products()
1690
+ block_count=4 cstruct_size=0 is_def=true continue
1691
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'update':
1692
+ block_count=4 cstruct_size=0 is_def=true result = update_tool._run()
1693
+ block_count=4 cstruct_size=0 is_def=true print(result)
1694
+ block_count=4 cstruct_size=0 is_def=true continue
1695
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'similar':
1696
+ block_count=4 cstruct_size=0 is_def=true result = similar_tool._run()
1697
+ block_count=4 cstruct_size=0 is_def=true print(result)
1698
+ block_count=4 cstruct_size=0 is_def=true continue
1699
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('compare'):
1700
+ block_count=4 cstruct_size=0 is_def=true parts = user_input.split(maxsplit=1)
1701
+ block_count=4 cstruct_size=0 is_def=true query = parts[1] if len(parts) > 1 else ''
1702
+ block_count=4 cstruct_size=0 is_def=true result = compare_tool._run(query)
1703
+ block_count=4 cstruct_size=0 is_def=true print(result)
1704
+ block_count=4 cstruct_size=0 is_def=true continue
1705
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('search_products '):
1706
+ block_count=4 cstruct_size=0 is_def=true query = user_input[16:].strip()
1707
+ block_count=4 cstruct_size=0 is_def=true if query:
1708
+ block_count=5 cstruct_size=0 is_def=true result = db_search_tool._run(query)
1709
+ block_count=5 cstruct_size=0 is_def=true print(result)
1710
+ block_count=4 cstruct_size=0 is_def=true else:
1711
+ block_count=5 cstruct_size=0 is_def=true print('Usage: search_products <query>')
1712
+ block_count=4 cstruct_size=0 is_def=true continue
1713
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('show '):
1714
+ block_count=4 cstruct_size=0 is_def=true parts = user_input.split()
1715
+ block_count=4 cstruct_size=0 is_def=true if len(parts) > 1 and parts[1].isdigit():
1716
+ block_count=5 cstruct_size=0 is_def=true show_product_details(int(parts[1]))
1717
+ block_count=4 cstruct_size=0 is_def=true else:
1718
+ block_count=5 cstruct_size=0 is_def=true print('Usage: show <product_id>')
1719
+ block_count=4 cstruct_size=0 is_def=true continue
1720
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('delete '):
1721
+ block_count=4 cstruct_size=0 is_def=true try:
1722
+ block_count=5 cstruct_size=0 is_def=true parts = shlex.split(user_input)
1723
+ compo c_name=shlex
1724
+ block_count=5 cstruct_size=0 is_def=true if len(parts) > 1:
1725
+ block_count=6 cstruct_size=0 is_def=true identifiers = parts[1:]
1726
+ block_count=6 cstruct_size=0 is_def=true delete_product_records(identifiers)
1727
+ block_count=5 cstruct_size=0 is_def=true else:
1728
+ block_count=6 cstruct_size=0 is_def=true print('Usage: delete <ID/Name> ...')
1729
+ block_count=4 cstruct_size=0 is_def=true except ValueError as e:
1730
+ block_count=5 cstruct_size=0 is_def=true print(f'Error parsing command: {e}')
1731
+ block_count=4 cstruct_size=0 is_def=true continue
1732
+ block_count=3 cstruct_size=0 is_def=true if user_input:
1733
+ block_count=4 cstruct_size=0 is_def=true print(f'\nProcessing: {user_input}...\n')
1734
+ block_count=4 cstruct_size=0 is_def=true result = agent_executor.invoke({'input': user_input,
1735
+ block_count=5 cstruct_size=0 is_def=true 'chat_history': chat_history})
1736
+ block_count=4 cstruct_size=0 is_def=true chat_history.append(HumanMessage(content=user_input))
1737
+ compo c_name=HumanMessage
1738
+ block_count=4 cstruct_size=0 is_def=true if isinstance(result['output'], str):
1739
+ block_count=5 cstruct_size=0 is_def=true chat_history.append(AIMessage(content=result['output']))
1740
+ compo c_name=AIMessage
1741
+ block_count=4 cstruct_size=0 is_def=true if 'intermediate_steps' in result:
1742
+ block_count=5 cstruct_size=0 is_def=true save_agent_log(user_input, result['intermediate_steps'])
1743
+ block_count=4 cstruct_size=0 is_def=true display_products()
1744
+ block_count=2 cstruct_size=0 is_def=true except KeyboardInterrupt:
1745
+ block_count=3 cstruct_size=0 is_def=true print('\nExiting...')
1746
+ block_count=3 cstruct_size=0 is_def=true break
1747
+ block_count=2 cstruct_size=0 is_def=true except Exception as e:
1748
+ block_count=3 cstruct_size=0 is_def=true print(f'An error occurred: {e}')
1749
+ block_count=0 cstruct_size=0 is_def=true if __name__ == '__main__':
1750
+ block_count=1 cstruct_size=0 is_def=false main()
1751
+ endf of ./test/agent.py
1752
+ ./test_script.py
1753
+
1754
+ |python3 lib/del_comment.py ./test_script.py > /tmp/pylint20260323-801-l0pxs8
1755
+ block_count=0 cstruct_size=0 is_def=false import os
1756
+ block_count=0 cstruct_size=0 is_def=false import sqlite3
1757
+ block_count=0 cstruct_size=0 is_def=false import re
1758
+ block_count=0 cstruct_size=0 is_def=false import json
1759
+ block_count=0 cstruct_size=0 is_def=false import time
1760
+ block_count=0 cstruct_size=0 is_def=false import shlex
1761
+ block_count=0 cstruct_size=0 is_def=false import smtplib
1762
+ block_count=0 cstruct_size=0 is_def=false from email.mime.text import MIMEText
1763
+ compo c_name=email
1764
+ block_count=0 cstruct_size=0 is_def=false from email.mime.multipart import MIMEMultipart
1765
+ compo c_name=email
1766
+ block_count=0 cstruct_size=0 is_def=false from typing import Optional, Type, Any, List, Dict
1767
+ block_count=0 cstruct_size=0 is_def=false import urllib.request
1768
+ compo c_name=urllib
1769
+ block_count=0 cstruct_size=0 is_def=false import urllib.error
1770
+ compo c_name=urllib
1771
+ block_count=0 cstruct_size=0 is_def=false from dotenv import load_dotenv
1772
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.tools import BaseTool, Tool
1773
+ block_count=0 cstruct_size=0 is_def=false from pydantic import BaseModel, Field, model_validator
1774
+ block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
1775
+ block_count=0 cstruct_size=0 is_def=false from langchain_community.utilities import SerpAPIWrapper
1776
+ block_count=0 cstruct_size=0 is_def=false from langchain_community.tools.tavily_search import TavilySearchResults
1777
+ block_count=0 cstruct_size=0 is_def=false from googleapiclient.discovery import build
1778
+ compo c_name=googleapiclient
1779
+ block_count=0 cstruct_size=0 is_def=false import asyncio
1780
+ block_count=0 cstruct_size=0 is_def=false from browser_use import Agent, BrowserProfile
1781
+ block_count=0 cstruct_size=0 is_def=false from langchain_google_genai import ChatGoogleGenerativeAI
1782
+ block_count=0 cstruct_size=0 is_def=false from browser_use.llm.google.chat import ChatGoogle
1783
+ block_count=0 cstruct_size=0 is_def=false from langchain.agents import AgentExecutor, create_tool_calling_agent, create_react_agent
1784
+ compo c_name=langchain
1785
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, MessagesPlaceholder
1786
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.output_parsers import JsonOutputParser
1787
+ block_count=0 cstruct_size=0 is_def=false from langchain_core.messages import HumanMessage, AIMessage
1788
+ block_count=0 cstruct_size=0 is_def=false dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
1789
+ compo c_name=os
1790
+ block_count=0 cstruct_size=0 is_def=false load_dotenv(dotenv_path, override=True)
1791
+ block_count=0 cstruct_size=0 is_def=false DB_NAME = 'products.db'
1792
+ block_count=0 cstruct_size=0 is_def=false def init_db():
1793
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
1794
+ compo c_name=sqlite3
1795
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
1796
+ compo c_name=conn
1797
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
1798
+ compo c_name=cursor
1799
+ block_count=2 cstruct_size=0 is_def=true """
1800
+ block_count=2 cstruct_size=0 is_def=true CREATE TABLE IF NOT EXISTS products (
1801
+ block_count=3 cstruct_size=0 is_def=true id INTEGER PRIMARY KEY AUTOINCREMENT,
1802
+ block_count=3 cstruct_size=0 is_def=true name TEXT NOT NULL,
1803
+ block_count=3 cstruct_size=0 is_def=true store TEXT,
1804
+ block_count=3 cstruct_size=0 is_def=true price TEXT,
1805
+ block_count=3 cstruct_size=0 is_def=true url TEXT,
1806
+ block_count=3 cstruct_size=0 is_def=true description TEXT,
1807
+ block_count=3 cstruct_size=0 is_def=true model_number TEXT,
1808
+ block_count=3 cstruct_size=0 is_def=true release_date TEXT,
1809
+ block_count=3 cstruct_size=0 is_def=true ram TEXT,
1810
+ block_count=3 cstruct_size=0 is_def=true ssd TEXT,
1811
+ block_count=3 cstruct_size=0 is_def=true updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
1812
+ block_count=2 cstruct_size=0 is_def=true )
1813
+ block_count=1 cstruct_size=0 is_def=true """
1814
+ block_count=2 cstruct_size=0 is_def=true )
1815
+ block_count=1 cstruct_size=0 is_def=true try:
1816
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN model_number TEXT')
1817
+ compo c_name=cursor
1818
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
1819
+ block_count=2 cstruct_size=0 is_def=true pass
1820
+ block_count=1 cstruct_size=0 is_def=true try:
1821
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN release_date TEXT')
1822
+ compo c_name=cursor
1823
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
1824
+ block_count=2 cstruct_size=0 is_def=true pass
1825
+ block_count=1 cstruct_size=0 is_def=true try:
1826
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ram TEXT')
1827
+ compo c_name=cursor
1828
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
1829
+ block_count=2 cstruct_size=0 is_def=true pass
1830
+ block_count=1 cstruct_size=0 is_def=true try:
1831
+ block_count=2 cstruct_size=0 is_def=true cursor.execute('ALTER TABLE products ADD COLUMN ssd TEXT')
1832
+ compo c_name=cursor
1833
+ block_count=1 cstruct_size=0 is_def=true except sqlite3.OperationalError:
1834
+ block_count=2 cstruct_size=0 is_def=true pass
1835
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
1836
+ compo c_name=cursor
1837
+ block_count=2 cstruct_size=0 is_def=true """
1838
+ block_count=2 cstruct_size=0 is_def=true CREATE TABLE IF NOT EXISTS agent_logs (
1839
+ block_count=3 cstruct_size=0 is_def=true id INTEGER PRIMARY KEY AUTOINCREMENT,
1840
+ block_count=3 cstruct_size=0 is_def=true timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
1841
+ block_count=3 cstruct_size=0 is_def=true query TEXT,
1842
+ block_count=3 cstruct_size=0 is_def=true scratchpad TEXT
1843
+ block_count=2 cstruct_size=0 is_def=true )
1844
+ block_count=1 cstruct_size=0 is_def=true """
1845
+ block_count=2 cstruct_size=0 is_def=true )
1846
+ block_count=1 cstruct_size=0 is_def=true conn.commit()
1847
+ compo c_name=conn
1848
+ block_count=1 cstruct_size=0 is_def=true conn.close()
1849
+ compo c_name=conn
1850
+ block_count=0 cstruct_size=0 is_def=true init_db()
1851
+ block_count=0 cstruct_size=0 is_def=false class TavilySearchWrapper:
1852
+ class_name=test_script.TavilySearchWrapper
1853
+ base_name=
1854
+ block_count=1 cstruct_size=1 is_def=false def __init__(self):
1855
+ block_count=2 cstruct_size=1 is_def=true self.api_key = os.getenv('TAVILY_API_KEY')
1856
+ compo c_name=self
1857
+ block_count=2 cstruct_size=1 is_def=true if not self.api_key:
1858
+ compo c_name=self
1859
+ block_count=3 cstruct_size=1 is_def=true raise ValueError('TAVILY_API_KEY must be set for Tavily Search.')
1860
+ compo c_name=ValueError
1861
+ block_count=2 cstruct_size=1 is_def=true self.tool = TavilySearchResults(api_key=self.api_key)
1862
+ compo c_name=self
1863
+ compo c_name=TavilySearchResults
1864
+ block_count=1 cstruct_size=1 is_def=true def run(self, query: str) ->str:
1865
+ block_count=2 cstruct_size=1 is_def=true try:
1866
+ block_count=3 cstruct_size=1 is_def=true results = self.tool.invoke({'query': query})
1867
+ compo c_name=self
1868
+ block_count=3 cstruct_size=1 is_def=true formatted_results = []
1869
+ block_count=3 cstruct_size=1 is_def=true for item in results:
1870
+ block_count=4 cstruct_size=1 is_def=true content = item.get('content')
1871
+ compo c_name=item
1872
+ block_count=4 cstruct_size=1 is_def=true url = item.get('url')
1873
+ compo c_name=item
1874
+ block_count=4 cstruct_size=1 is_def=true formatted_results.append(f'Content: {content}\nURL: {url}\n')
1875
+ block_count=3 cstruct_size=1 is_def=true return '\n'.join(formatted_results
1876
+ block_count=4 cstruct_size=1 is_def=true ) if formatted_results else 'No results found.'
1877
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
1878
+ block_count=3 cstruct_size=1 is_def=true return f'Error during Tavily Search: {e}'
1879
+ block_count=0 cstruct_size=1 is_def=true class BrowserUseSearchWrapper:
1880
+ end of test_script.TavilySearchWrapper
1881
+ class_name=test_script.BrowserUseSearchWrapper
1882
+ base_name=
1883
+ block_count=1 cstruct_size=1 is_def=false def __init__(self):
1884
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
1885
+ compo c_name=os
1886
+ block_count=2 cstruct_size=1 is_def=true self.llm = ChatGoogle(model=model_name, api_key=os.getenv(
1887
+ compo c_name=self
1888
+ compo c_name=ChatGoogle
1889
+ block_count=3 cstruct_size=1 is_def=true 'GOOGLE_API_KEY'))
1890
+ block_count=2 cstruct_size=1 is_def=true user_agent = (
1891
+ 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'
1892
+ block_count=3 cstruct_size=1 is_def=true )
1893
+ block_count=2 cstruct_size=1 is_def=true self.browser_profile = BrowserProfile(headless=False, user_agent=
1894
+ compo c_name=self
1895
+ compo c_name=BrowserProfile
1896
+ block_count=3 cstruct_size=1 is_def=true user_agent)
1897
+ block_count=1 cstruct_size=1 is_def=true async def _search_async(self, query: str) ->str:
1898
+ block_count=2 cstruct_size=1 is_def=false task = f"""
1899
+ block_count=2 cstruct_size=1 is_def=false Web全体から '{query}' を検索してください。
1900
+ block_count=2 cstruct_size=1 is_def=false 検索結果の上位の製品について、以下の情報を確実に抽出してください:
1901
+ block_count=2 cstruct_size=1 is_def=false 1. 製品名(タイトル)
1902
+ block_count=2 cstruct_size=1 is_def=false 2. 正確な製品ページのURL
1903
+ block_count=2 cstruct_size=1 is_def=false 3. 詳細な製品概要(スペックや特徴)
1904
+ block_count=2 cstruct_size=1 is_def=false 4. 価格
1905
+ block_count=2 cstruct_size=1 is_def=false 5. 型番(モデル番号)
1906
+ block_count=2 cstruct_size=1 is_def=false 6. 発売日
1907
+ block_count=2 cstruct_size=1 is_def=false 重要:URLと製品概要は必須です。URLは必ず http または https で始まる有効なものを取得してください。
1908
+ block_count=2 cstruct_size=1 is_def=false 型番や発売日が見つかる場合はそれらも必ず抽出してください。
1909
+ block_count=2 cstruct_size=1 is_def=false 検索結果ページだけでなく、必要であれば個別の製品ページにアクセスして情報を取得してください。
1910
+ block_count=2 cstruct_size=1 is_def=false ページが完全に読み込まれるまで待ち、正確な情報を取得するようにしてください。
1911
+ block_count=2 cstruct_size=1 is_def=false また、メモリ(RAM)やストレージ(SSDなど)の容量を抽出する際、「最大〇〇GB」「〇〇GBまで増設可能」などと記載されている拡張上限の数値は対象外とし、必ず「標準搭載(初期状態)」の容量を抽出してください。
1912
+ block_count=2 cstruct_size=1 is_def=false 【極密事項】
1913
+ block_count=2 cstruct_size=1 is_def=false ページ内に以下の文言が含まれている商品は「販売不可」とみなし、絶対に抽出・出力しないでください。
1914
+ block_count=2 cstruct_size=1 is_def=false - 「販売終了」
1915
+ block_count=2 cstruct_size=1 is_def=false - 「お探しのページは見つかりません」
1916
+ block_count=2 cstruct_size=1 is_def=false - 「404 Not Found」
1917
+ block_count=2 cstruct_size=1 is_def=false - 「この商品は現在お取り扱いできません」
1918
+ block_count=2 cstruct_size=1 is_def=false """
1919
+ block_count=2 cstruct_size=1 is_def=false agent = Agent(task=task, llm=self.llm, browser_profile=self.
1920
+ compo c_name=Agent
1921
+ block_count=3 cstruct_size=1 is_def=false browser_profile)
1922
+ block_count=2 cstruct_size=1 is_def=false result = await agent.run()
1923
+ compo c_name=agent
1924
+ block_count=2 cstruct_size=1 is_def=false return result.final_result()
1925
+ compo c_name=result
1926
+ block_count=1 cstruct_size=1 is_def=false def run(self, query: str) ->str:
1927
+ block_count=2 cstruct_size=1 is_def=true try:
1928
+ block_count=3 cstruct_size=1 is_def=true return asyncio.run(self._search_async(query))
1929
+ compo c_name=asyncio
1930
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
1931
+ block_count=3 cstruct_size=1 is_def=true return f'Error during Browser Use Search: {e}'
1932
+ block_count=0 cstruct_size=1 is_def=true def get_search_tool_func():
1933
+ end of test_script.BrowserUseSearchWrapper
1934
+ block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
1935
+ compo c_name=os
1936
+ block_count=1 cstruct_size=0 is_def=true if provider == 'tavily_api':
1937
+ block_count=2 cstruct_size=0 is_def=true return TavilySearchWrapper()
1938
+ compo c_name=TavilySearchWrapper
1939
+ block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
1940
+ block_count=2 cstruct_size=0 is_def=true return BrowserUseSearchWrapper()
1941
+ compo c_name=BrowserUseSearchWrapper
1942
+ block_count=1 cstruct_size=0 is_def=true else:
1943
+ block_count=2 cstruct_size=0 is_def=true return SerpAPIWrapper()
1944
+ compo c_name=SerpAPIWrapper
1945
+ block_count=0 cstruct_size=0 is_def=true def get_all_products():
1946
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
1947
+ compo c_name=sqlite3
1948
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
1949
+ compo c_name=conn
1950
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
1951
+ compo c_name=cursor
1952
+ 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'
1953
+ block_count=2 cstruct_size=0 is_def=true )
1954
+ block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
1955
+ compo c_name=cursor
1956
+ block_count=1 cstruct_size=0 is_def=true conn.close()
1957
+ compo c_name=conn
1958
+ block_count=1 cstruct_size=0 is_def=true products = []
1959
+ block_count=1 cstruct_size=0 is_def=true for r in rows:
1960
+ block_count=2 cstruct_size=0 is_def=true products.append({'id': r[0], 'name': r[1], 'store': r[2], 'price':
1961
+ compo c_name=products
1962
+ block_count=3 cstruct_size=0 is_def=true r[3], 'url': r[4], 'description': r[5], 'model_number': r[6] if
1963
+ block_count=3 cstruct_size=0 is_def=true len(r) > 6 else '', 'release_date': r[7] if len(r) > 7 else '',
1964
+ 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
1965
+ block_count=3 cstruct_size=0 is_def=true '', 'updated_at': r[10] if len(r) > 10 else ''})
1966
+ block_count=1 cstruct_size=0 is_def=true return products
1967
+ block_count=0 cstruct_size=0 is_def=true def parse_price_val(p_str):
1968
+ block_count=1 cstruct_size=0 is_def=true if not p_str:
1969
+ block_count=2 cstruct_size=0 is_def=true return float('inf')
1970
+ block_count=1 cstruct_size=0 is_def=true s = str(p_str).replace(',', '')
1971
+ block_count=1 cstruct_size=0 is_def=true match_man = re.search('(\\d+(\\.\\d+)?)万', s)
1972
+ compo c_name=re
1973
+ block_count=1 cstruct_size=0 is_def=true if match_man:
1974
+ block_count=2 cstruct_size=0 is_def=true try:
1975
+ block_count=3 cstruct_size=0 is_def=true val = float(match_man.group(1)) * 10000
1976
+ block_count=3 cstruct_size=0 is_def=true return int(val)
1977
+ block_count=2 cstruct_size=0 is_def=true except:
1978
+ block_count=3 cstruct_size=0 is_def=true pass
1979
+ block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', s)
1980
+ compo c_name=re
1981
+ block_count=1 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
1982
+ block_count=0 cstruct_size=0 is_def=true def extract_alphanumeric(s: str) ->str:
1983
+ block_count=1 cstruct_size=0 is_def=true """Extracts only alphanumeric characters from a string and converts to lowercase for robust comparison."""
1984
+ block_count=1 cstruct_size=0 is_def=true if not s:
1985
+ block_count=2 cstruct_size=0 is_def=true return ''
1986
+ block_count=1 cstruct_size=0 is_def=true return re.sub('[^a-zA-Z0-9]', '', str(s)).lower()
1987
+ compo c_name=re
1988
+ block_count=0 cstruct_size=0 is_def=true def parse_date_val(d_str: str) ->str:
1989
+ block_count=1 cstruct_size=0 is_def=true """
1990
+ block_count=1 cstruct_size=0 is_def=true Normalizes date strings for comparison.
1991
+ block_count=1 cstruct_size=0 is_def=true Examples: '2021年2月' -> '202102', '2021-02' -> '202102'
1992
+ block_count=1 cstruct_size=0 is_def=true """
1993
+ block_count=1 cstruct_size=0 is_def=true if not d_str:
1994
+ block_count=2 cstruct_size=0 is_def=true return ''
1995
+ block_count=1 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(d_str))
1996
+ compo c_name=re
1997
+ block_count=1 cstruct_size=0 is_def=true if len(nums) >= 2:
1998
+ block_count=2 cstruct_size=0 is_def=true year = nums[0]
1999
+ block_count=2 cstruct_size=0 is_def=true month = nums[1].zfill(2)
2000
+ block_count=2 cstruct_size=0 is_def=true return f'{year}{month}'
2001
+ block_count=1 cstruct_size=0 is_def=true elif len(nums) == 1:
2002
+ block_count=2 cstruct_size=0 is_def=true return nums[0]
2003
+ block_count=1 cstruct_size=0 is_def=true else:
2004
+ block_count=2 cstruct_size=0 is_def=true return extract_alphanumeric(d_str)
2005
+ block_count=0 cstruct_size=0 is_def=true def is_similar_model(m1: str, m2: str) ->bool:
2006
+ block_count=1 cstruct_size=0 is_def=true """
2007
+ block_count=1 cstruct_size=0 is_def=true Checks if two model strings are substantially similar.
2008
+ block_count=1 cstruct_size=0 is_def=true Considers them similar if the alphanumeric string of one is entirely contained in the other.
2009
+ block_count=1 cstruct_size=0 is_def=true e.g., 'dynabookg83hs7n11' and 'g83hs7n11' -> True
2010
+ block_count=1 cstruct_size=0 is_def=true """
2011
+ block_count=1 cstruct_size=0 is_def=true am1 = extract_alphanumeric(m1)
2012
+ block_count=1 cstruct_size=0 is_def=true am2 = extract_alphanumeric(m2)
2013
+ block_count=1 cstruct_size=0 is_def=true if not am1 and not am2:
2014
+ block_count=2 cstruct_size=0 is_def=true return True
2015
+ block_count=1 cstruct_size=0 is_def=true if not am1 or not am2:
2016
+ block_count=2 cstruct_size=0 is_def=true return False
2017
+ block_count=1 cstruct_size=0 is_def=true return am1 in am2 or am2 in am1
2018
+ block_count=0 cstruct_size=0 is_def=true def save_agent_log(query, steps):
2019
+ block_count=1 cstruct_size=0 is_def=true """Saves the agent's scratchpad (intermediate steps) to the database."""
2020
+ block_count=1 cstruct_size=0 is_def=true if not steps:
2021
+ block_count=2 cstruct_size=0 is_def=true return
2022
+ block_count=1 cstruct_size=0 is_def=true log_content = []
2023
+ block_count=1 cstruct_size=0 is_def=true for action, observation in steps:
2024
+ block_count=2 cstruct_size=0 is_def=true if isinstance(action, list):
2025
+ block_count=3 cstruct_size=0 is_def=true for a in action:
2026
+ block_count=4 cstruct_size=0 is_def=true log_content.append(f'Tool: {a.tool}')
2027
+ block_count=4 cstruct_size=0 is_def=true log_content.append(f'Input: {a.tool_input}')
2028
+ block_count=4 cstruct_size=0 is_def=true log_content.append(f'Log: {a.log}')
2029
+ block_count=2 cstruct_size=0 is_def=true else:
2030
+ block_count=3 cstruct_size=0 is_def=true log_content.append(f'Tool: {action.tool}')
2031
+ block_count=3 cstruct_size=0 is_def=true log_content.append(f'Input: {action.tool_input}')
2032
+ block_count=3 cstruct_size=0 is_def=true log_content.append(f'Log: {action.log}')
2033
+ block_count=2 cstruct_size=0 is_def=true log_content.append(f'Observation: {observation}')
2034
+ block_count=2 cstruct_size=0 is_def=true log_content.append('-' * 20)
2035
+ block_count=1 cstruct_size=0 is_def=true scratchpad_text = '\n'.join(log_content)
2036
+ block_count=1 cstruct_size=0 is_def=true try:
2037
+ block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
2038
+ compo c_name=sqlite3
2039
+ block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
2040
+ compo c_name=conn
2041
+ block_count=2 cstruct_size=0 is_def=true cursor.execute(
2042
+ compo c_name=cursor
2043
+ block_count=3 cstruct_size=0 is_def=true 'INSERT INTO agent_logs (query, scratchpad) VALUES (?, ?)', (
2044
+ block_count=3 cstruct_size=0 is_def=true query, scratchpad_text))
2045
+ block_count=2 cstruct_size=0 is_def=true conn.commit()
2046
+ compo c_name=conn
2047
+ block_count=2 cstruct_size=0 is_def=true conn.close()
2048
+ compo c_name=conn
2049
+ block_count=2 cstruct_size=0 is_def=true print(f' [Log saved to database]')
2050
+ block_count=1 cstruct_size=0 is_def=true except Exception as e:
2051
+ block_count=2 cstruct_size=0 is_def=true print(f'Error saving log: {e}')
2052
+ block_count=0 cstruct_size=0 is_def=true def get_all_agent_logs():
2053
+ block_count=1 cstruct_size=0 is_def=true """Fetches all agent logs from the database."""
2054
+ block_count=1 cstruct_size=0 is_def=true try:
2055
+ block_count=2 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
2056
+ compo c_name=sqlite3
2057
+ block_count=2 cstruct_size=0 is_def=true cursor = conn.cursor()
2058
+ compo c_name=conn
2059
+ block_count=2 cstruct_size=0 is_def=true cursor.execute(
2060
+ compo c_name=cursor
2061
+ block_count=3 cstruct_size=0 is_def=true 'SELECT query, scratchpad FROM agent_logs ORDER BY timestamp DESC')
2062
+ block_count=2 cstruct_size=0 is_def=true rows = cursor.fetchall()
2063
+ compo c_name=cursor
2064
+ block_count=2 cstruct_size=0 is_def=true conn.close()
2065
+ compo c_name=conn
2066
+ block_count=2 cstruct_size=0 is_def=true logs = []
2067
+ block_count=2 cstruct_size=0 is_def=true for r in rows:
2068
+ block_count=3 cstruct_size=0 is_def=true logs.append(f'Query: {r[0]}\nLog:\n{r[1]}\n')
2069
+ compo c_name=logs
2070
+ block_count=2 cstruct_size=0 is_def=true return '\n'.join(logs)
2071
+ block_count=1 cstruct_size=0 is_def=true except Exception as e:
2072
+ block_count=2 cstruct_size=0 is_def=true print(f'Error fetching logs: {e}')
2073
+ block_count=2 cstruct_size=0 is_def=true return ''
2074
+ block_count=0 cstruct_size=0 is_def=true def send_email_notification(subject: str, body: str):
2075
+ block_count=1 cstruct_size=0 is_def=true """Sends an email notification."""
2076
+ block_count=1 cstruct_size=0 is_def=true smtp_server = os.getenv('EMAIL_SMTP_SERVER')
2077
+ compo c_name=os
2078
+ block_count=1 cstruct_size=0 is_def=true smtp_port = os.getenv('EMAIL_SMTP_PORT')
2079
+ compo c_name=os
2080
+ block_count=1 cstruct_size=0 is_def=true sender_email = os.getenv('EMAIL_SENDER_ADDRESS')
2081
+ compo c_name=os
2082
+ block_count=1 cstruct_size=0 is_def=true sender_password = os.getenv('EMAIL_SENDER_PASSWORD')
2083
+ compo c_name=os
2084
+ block_count=1 cstruct_size=0 is_def=true receiver_email = os.getenv('EMAIL_RECEIVER_ADDRESS')
2085
+ compo c_name=os
2086
+ block_count=1 cstruct_size=0 is_def=true if not all([smtp_server, smtp_port, sender_email, receiver_email]):
2087
+ block_count=2 cstruct_size=0 is_def=true print(
2088
+ block_count=3 cstruct_size=0 is_def=true 'Email configuration missing (Server, Port, Sender, Receiver). Skipping notification.'
2089
+ block_count=3 cstruct_size=0 is_def=true )
2090
+ block_count=2 cstruct_size=0 is_def=true return
2091
+ block_count=1 cstruct_size=0 is_def=true try:
2092
+ block_count=2 cstruct_size=0 is_def=true msg = MIMEMultipart()
2093
+ compo c_name=MIMEMultipart
2094
+ block_count=2 cstruct_size=0 is_def=true msg['From'] = sender_email
2095
+ block_count=2 cstruct_size=0 is_def=true msg['To'] = receiver_email
2096
+ block_count=2 cstruct_size=0 is_def=true msg['Subject'] = subject
2097
+ block_count=2 cstruct_size=0 is_def=true msg.attach(MIMEText(body, 'plain'))
2098
+ compo c_name=msg
2099
+ compo c_name=MIMEText
2100
+ block_count=2 cstruct_size=0 is_def=true server = smtplib.SMTP(smtp_server, int(smtp_port))
2101
+ compo c_name=SMTP
2102
+ block_count=2 cstruct_size=0 is_def=true server.starttls()
2103
+ compo c_name=server
2104
+ block_count=2 cstruct_size=0 is_def=true if sender_password:
2105
+ block_count=3 cstruct_size=0 is_def=true server.login(sender_email, sender_password)
2106
+ compo c_name=server
2107
+ block_count=2 cstruct_size=0 is_def=true server.send_message(msg)
2108
+ compo c_name=server
2109
+ block_count=2 cstruct_size=0 is_def=true server.quit()
2110
+ compo c_name=server
2111
+ block_count=2 cstruct_size=0 is_def=true print(f'Email notification sent: {subject}')
2112
+ block_count=1 cstruct_size=0 is_def=true except Exception as e:
2113
+ block_count=2 cstruct_size=0 is_def=true print(f'Failed to send email: {e}')
2114
+ block_count=0 cstruct_size=0 is_def=true class SaveProductInput(BaseModel):
2115
+ class_name=test_script.SaveProductInput
2116
+ base_name=BaseModel
2117
+ block_count=1 cstruct_size=1 is_def=false name: str = Field(description='Name of the product')
2118
+ compo c_name=Field
2119
+ block_count=1 cstruct_size=1 is_def=false store: str = Field(description='Name of the store selling the product')
2120
+ compo c_name=Field
2121
+ block_count=1 cstruct_size=1 is_def=false price: str = Field(description='Price of the product')
2122
+ compo c_name=Field
2123
+ block_count=1 cstruct_size=1 is_def=false url: Optional[str] = Field(description='URL of the product page',
2124
+ compo c_name=Field
2125
+ block_count=2 cstruct_size=1 is_def=false default='')
2126
+ block_count=1 cstruct_size=1 is_def=false description: Optional[str] = Field(description=
2127
+ compo c_name=Field
2128
+ block_count=2 cstruct_size=1 is_def=false 'Brief description of the product', default='')
2129
+ block_count=1 cstruct_size=1 is_def=false model_number: Optional[str] = Field(description=
2130
+ compo c_name=Field
2131
+ block_count=2 cstruct_size=1 is_def=false 'Model number (型番) of the product', default='')
2132
+ block_count=1 cstruct_size=1 is_def=false release_date: Optional[str] = Field(description=
2133
+ compo c_name=Field
2134
+ block_count=2 cstruct_size=1 is_def=false 'Release date (発売日) of the product', default='')
2135
+ block_count=1 cstruct_size=1 is_def=false ram: Optional[str] = Field(description='RAM size', default='')
2136
+ compo c_name=Field
2137
+ block_count=1 cstruct_size=1 is_def=false ssd: Optional[str] = Field(description='SSD size', default='')
2138
+ compo c_name=Field
2139
+ block_count=1 cstruct_size=1 is_def=false @model_validator(mode='before')
2140
+ block_count=1 cstruct_size=1 is_def=false @classmethod
2141
+ block_count=1 cstruct_size=1 is_def=false def parse_json_input(cls, data: Any) ->Any:
2142
+ block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict):
2143
+ block_count=3 cstruct_size=1 is_def=true if 'name' in data and ('store' not in data or 'price' not in data):
2144
+ block_count=4 cstruct_size=1 is_def=true name_val = data['name']
2145
+ block_count=4 cstruct_size=1 is_def=true if isinstance(name_val, str) and name_val.strip().startswith(
2146
+ block_count=5 cstruct_size=1 is_def=true '{') and name_val.strip().endswith('}'):
2147
+ block_count=5 cstruct_size=1 is_def=true try:
2148
+ block_count=6 cstruct_size=1 is_def=true parsed = json.loads(name_val)
2149
+ compo c_name=json
2150
+ block_count=6 cstruct_size=1 is_def=true if isinstance(parsed, dict):
2151
+ block_count=7 cstruct_size=1 is_def=true data = parsed
2152
+ block_count=5 cstruct_size=1 is_def=true except json.JSONDecodeError:
2153
+ block_count=6 cstruct_size=1 is_def=true pass
2154
+ block_count=2 cstruct_size=1 is_def=true if isinstance(data, dict) and 'price' in data:
2155
+ block_count=3 cstruct_size=1 is_def=true if isinstance(data['price'], (int, float)):
2156
+ block_count=4 cstruct_size=1 is_def=true data['price'] = str(data['price'])
2157
+ block_count=2 cstruct_size=1 is_def=true return data
2158
+ block_count=0 cstruct_size=1 is_def=true class SaveProductTool(BaseTool):
2159
+ end of test_script.SaveProductInput
2160
+ class_name=test_script.SaveProductTool
2161
+ base_name=BaseTool
2162
+ block_count=1 cstruct_size=1 is_def=false name = 'save_product'
2163
+ block_count=1 cstruct_size=1 is_def=false description = (
2164
+ 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.'
2165
+ block_count=2 cstruct_size=1 is_def=false )
2166
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SaveProductInput
2167
+ block_count=1 cstruct_size=1 is_def=false def _run(self, name: str, store: str=None, price: str=None, url: str='',
2168
+ block_count=2 cstruct_size=1 is_def=true description: str='', model_number: str='', release_date: str='',
2169
+ block_count=2 cstruct_size=1 is_def=true ram: str='', ssd: str='', **kwargs):
2170
+ block_count=2 cstruct_size=1 is_def=true try:
2171
+ block_count=3 cstruct_size=1 is_def=true parsed_data = {}
2172
+ block_count=3 cstruct_size=1 is_def=true if isinstance(name, dict):
2173
+ block_count=4 cstruct_size=1 is_def=true parsed_data = name
2174
+ block_count=3 cstruct_size=1 is_def=true elif isinstance(name, str) and name.strip().startswith('{'
2175
+ compo c_name=name
2176
+ block_count=4 cstruct_size=1 is_def=true ) and name.strip().endswith('}'):
2177
+ compo c_name=name
2178
+ block_count=4 cstruct_size=1 is_def=true try:
2179
+ block_count=5 cstruct_size=1 is_def=true parsed_data = json.loads(name)
2180
+ compo c_name=json
2181
+ block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError:
2182
+ block_count=5 cstruct_size=1 is_def=true pass
2183
+ block_count=3 cstruct_size=1 is_def=true if parsed_data:
2184
+ block_count=4 cstruct_size=1 is_def=true if 'name' in parsed_data:
2185
+ block_count=5 cstruct_size=1 is_def=true name = parsed_data['name']
2186
+ block_count=4 cstruct_size=1 is_def=true if store is None:
2187
+ block_count=5 cstruct_size=1 is_def=true store = parsed_data.get('store')
2188
+ block_count=4 cstruct_size=1 is_def=true if price is None:
2189
+ block_count=5 cstruct_size=1 is_def=true price = parsed_data.get('price')
2190
+ block_count=4 cstruct_size=1 is_def=true if not url:
2191
+ block_count=5 cstruct_size=1 is_def=true url = parsed_data.get('url', '')
2192
+ block_count=4 cstruct_size=1 is_def=true if not description:
2193
+ block_count=5 cstruct_size=1 is_def=true description = parsed_data.get('description', '')
2194
+ block_count=4 cstruct_size=1 is_def=true if not model_number:
2195
+ block_count=5 cstruct_size=1 is_def=true model_number = parsed_data.get('model_number', '')
2196
+ block_count=4 cstruct_size=1 is_def=true if not release_date:
2197
+ block_count=5 cstruct_size=1 is_def=true release_date = parsed_data.get('release_date', '')
2198
+ block_count=4 cstruct_size=1 is_def=true if not ram:
2199
+ block_count=5 cstruct_size=1 is_def=true ram = parsed_data.get('ram', '')
2200
+ block_count=4 cstruct_size=1 is_def=true if not ssd:
2201
+ block_count=5 cstruct_size=1 is_def=true ssd = parsed_data.get('ssd', '')
2202
+ block_count=3 cstruct_size=1 is_def=true if store is None:
2203
+ block_count=4 cstruct_size=1 is_def=true store = kwargs.get('store')
2204
+ compo c_name=kwargs
2205
+ block_count=3 cstruct_size=1 is_def=true if price is None:
2206
+ block_count=4 cstruct_size=1 is_def=true price = kwargs.get('price')
2207
+ compo c_name=kwargs
2208
+ block_count=3 cstruct_size=1 is_def=true if not model_number:
2209
+ block_count=4 cstruct_size=1 is_def=true model_number = kwargs.get('model_number', '')
2210
+ compo c_name=kwargs
2211
+ block_count=3 cstruct_size=1 is_def=true if not release_date:
2212
+ block_count=4 cstruct_size=1 is_def=true release_date = kwargs.get('release_date', '')
2213
+ compo c_name=kwargs
2214
+ block_count=3 cstruct_size=1 is_def=true if not ram:
2215
+ block_count=4 cstruct_size=1 is_def=true ram = kwargs.get('ram', '')
2216
+ compo c_name=kwargs
2217
+ block_count=3 cstruct_size=1 is_def=true if not ssd:
2218
+ block_count=4 cstruct_size=1 is_def=true ssd = kwargs.get('ssd', '')
2219
+ compo c_name=kwargs
2220
+ block_count=3 cstruct_size=1 is_def=true if not name or not store or not price:
2221
+ block_count=4 cstruct_size=1 is_def=true return (
2222
+ block_count=5 cstruct_size=1 is_def=true f'Error: Required arguments missing. Name: {name}, Store: {store}, Price: {price}'
2223
+ block_count=5 cstruct_size=1 is_def=true )
2224
+ block_count=3 cstruct_size=1 is_def=true if not url or not description:
2225
+ block_count=4 cstruct_size=1 is_def=true return (
2226
+ block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL or description is missing. URL: '{url}', Description: '{description}'"
2227
+ block_count=5 cstruct_size=1 is_def=true )
2228
+ block_count=3 cstruct_size=1 is_def=true if not url.startswith('http://') and not url.startswith('https://'
2229
+ compo c_name=url
2230
+ block_count=4 cstruct_size=1 is_def=true ):
2231
+ block_count=4 cstruct_size=1 is_def=true return (
2232
+ block_count=5 cstruct_size=1 is_def=true f"Skipped saving product '{name}': URL must start with 'http://' or 'https://'. URL: '{url}'"
2233
+ block_count=5 cstruct_size=1 is_def=true )
2234
+ block_count=3 cstruct_size=1 is_def=true if isinstance(price, (int, float)):
2235
+ block_count=4 cstruct_size=1 is_def=true price = str(price)
2236
+ block_count=3 cstruct_size=1 is_def=true price_val = parse_price_val(price)
2237
+ block_count=3 cstruct_size=1 is_def=true if price_val == float('inf'):
2238
+ block_count=4 cstruct_size=1 is_def=true if not re.search('\\d', str(price)):
2239
+ compo c_name=re
2240
+ block_count=5 cstruct_size=1 is_def=true return (
2241
+ block_count=6 cstruct_size=1 is_def=true f"Skipped saving product '{name}': Price info is missing or invalid ('{price}')."
2242
+ block_count=6 cstruct_size=1 is_def=true )
2243
+ block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
2244
+ compo c_name=sqlite3
2245
+ block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
2246
+ compo c_name=conn
2247
+ block_count=3 cstruct_size=1 is_def=true cursor.execute(
2248
+ compo c_name=cursor
2249
+ block_count=4 cstruct_size=1 is_def=true 'SELECT id, store, price, url FROM products WHERE name = ?',
2250
+ block_count=4 cstruct_size=1 is_def=true (name,))
2251
+ block_count=3 cstruct_size=1 is_def=true rows = cursor.fetchall()
2252
+ compo c_name=cursor
2253
+ block_count=3 cstruct_size=1 is_def=true items = []
2254
+ block_count=3 cstruct_size=1 is_def=true for r in rows:
2255
+ block_count=4 cstruct_size=1 is_def=true items.append({'id': r[0], 'store': r[1], 'price_str': r[2],
2256
+ compo c_name=items
2257
+ block_count=5 cstruct_size=1 is_def=true 'price_val': parse_price_val(r[2]), 'url': r[3]})
2258
+ block_count=3 cstruct_size=1 is_def=true new_price_val = parse_price_val(price)
2259
+ block_count=3 cstruct_size=1 is_def=true msg = ''
2260
+ block_count=3 cstruct_size=1 is_def=true items.sort(key=lambda x: x['price_val'])
2261
+ compo c_name=items
2262
+ block_count=3 cstruct_size=1 is_def=true current_cheapest = items[0] if items else None
2263
+ block_count=3 cstruct_size=1 is_def=true should_save = False
2264
+ block_count=3 cstruct_size=1 is_def=true should_update = False
2265
+ block_count=3 cstruct_size=1 is_def=true if not current_cheapest:
2266
+ block_count=4 cstruct_size=1 is_def=true should_save = True
2267
+ block_count=3 cstruct_size=1 is_def=true elif new_price_val < current_cheapest['price_val']:
2268
+ block_count=4 cstruct_size=1 is_def=true should_save = True
2269
+ block_count=4 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (name,))
2270
+ compo c_name=cursor
2271
+ block_count=4 cstruct_size=1 is_def=true msg_prefix = (
2272
+ 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})."
2273
+ block_count=5 cstruct_size=1 is_def=true )
2274
+ block_count=3 cstruct_size=1 is_def=true elif new_price_val == current_cheapest['price_val']:
2275
+ block_count=4 cstruct_size=1 is_def=true if store == current_cheapest['store']:
2276
+ block_count=5 cstruct_size=1 is_def=true should_update = True
2277
+ block_count=5 cstruct_size=1 is_def=true msg_prefix = f'Updated info for {name} at {store}.'
2278
+ block_count=4 cstruct_size=1 is_def=true else:
2279
+ block_count=5 cstruct_size=1 is_def=true msg = (
2280
+ block_count=6 cstruct_size=1 is_def=true f"Product {name} exists with same price at {current_cheapest['store']}. Keeping existing."
2281
+ block_count=6 cstruct_size=1 is_def=true )
2282
+ block_count=3 cstruct_size=1 is_def=true elif store == current_cheapest['store']:
2283
+ block_count=4 cstruct_size=1 is_def=true should_update = True
2284
+ block_count=4 cstruct_size=1 is_def=true msg_prefix = (
2285
+ block_count=5 cstruct_size=1 is_def=true f"Price increased for {name} at {store}: {current_cheapest['price_str']} -> {price}."
2286
+ block_count=5 cstruct_size=1 is_def=true )
2287
+ block_count=3 cstruct_size=1 is_def=true else:
2288
+ block_count=4 cstruct_size=1 is_def=true msg = (
2289
+ 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})."
2290
+ block_count=5 cstruct_size=1 is_def=true )
2291
+ block_count=3 cstruct_size=1 is_def=true if should_save:
2292
+ block_count=4 cstruct_size=1 is_def=true cursor.execute(
2293
+ compo c_name=cursor
2294
+ block_count=5 cstruct_size=1 is_def=true """
2295
+ block_count=5 cstruct_size=1 is_def=true INSERT INTO products (name, store, price, url, description, model_number, release_date, ram, ssd, updated_at)
2296
+ block_count=5 cstruct_size=1 is_def=true VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
2297
+ block_count=4 cstruct_size=1 is_def=true """
2298
+ block_count=5 cstruct_size=1 is_def=true , (name, store, price, url, description, model_number,
2299
+ block_count=5 cstruct_size=1 is_def=true release_date, ram, ssd))
2300
+ block_count=4 cstruct_size=1 is_def=true if not msg:
2301
+ block_count=5 cstruct_size=1 is_def=true msg = f'Saved product: {name} from {store} for {price}.'
2302
+ block_count=4 cstruct_size=1 is_def=true else:
2303
+ block_count=5 cstruct_size=1 is_def=true msg = msg_prefix
2304
+ block_count=4 cstruct_size=1 is_def=true email_subject = f'Product Saved: {name}'
2305
+ block_count=4 cstruct_size=1 is_def=true email_body = f"""Action: Saved (New or Cheaper)
2306
+ block_count=0 cstruct_size=1 is_def=true Name: {name}
2307
+ end of test_script.SaveProductTool
2308
+ block_count=0 cstruct_size=0 is_def=false Store: {store}
2309
+ block_count=0 cstruct_size=0 is_def=false Price: {price}
2310
+ block_count=0 cstruct_size=0 is_def=false URL: {url}
2311
+ block_count=0 cstruct_size=0 is_def=false Model: {model_number}
2312
+ block_count=0 cstruct_size=0 is_def=false Release: {release_date}
2313
+ block_count=0 cstruct_size=0 is_def=false RAM: {ram}
2314
+ block_count=0 cstruct_size=0 is_def=false SSD: {ssd}
2315
+ block_count=0 cstruct_size=0 is_def=false Description: {description}
2316
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
2317
+ block_count=4 cstruct_size=0 is_def=false send_email_notification(email_subject, email_body)
2318
+ block_count=3 cstruct_size=0 is_def=false if should_update:
2319
+ block_count=4 cstruct_size=0 is_def=false cursor.execute(
2320
+ compo c_name=cursor
2321
+ block_count=5 cstruct_size=0 is_def=false 'SELECT price, url, description, model_number, release_date, ram, ssd FROM products WHERE id = ?'
2322
+ block_count=5 cstruct_size=0 is_def=false , (current_cheapest['id'],))
2323
+ block_count=4 cstruct_size=0 is_def=false curr_row = cursor.fetchone()
2324
+ compo c_name=cursor
2325
+ block_count=4 cstruct_size=0 is_def=false curr_price_str = curr_row[0]
2326
+ block_count=4 cstruct_size=0 is_def=false curr_url = curr_row[1]
2327
+ block_count=4 cstruct_size=0 is_def=false curr_desc = curr_row[2]
2328
+ block_count=4 cstruct_size=0 is_def=false curr_model = curr_row[3]
2329
+ block_count=4 cstruct_size=0 is_def=false curr_release = curr_row[4]
2330
+ block_count=4 cstruct_size=0 is_def=false curr_ram = curr_row[5] if len(curr_row) > 5 else ''
2331
+ block_count=4 cstruct_size=0 is_def=false curr_ssd = curr_row[6] if len(curr_row) > 6 else ''
2332
+ block_count=4 cstruct_size=0 is_def=false final_model = model_number if model_number else curr_model
2333
+ block_count=4 cstruct_size=0 is_def=false final_release = release_date if release_date else curr_release
2334
+ block_count=4 cstruct_size=0 is_def=false final_ram = ram if ram else curr_ram
2335
+ block_count=4 cstruct_size=0 is_def=false final_ssd = ssd if ssd else curr_ssd
2336
+ block_count=4 cstruct_size=0 is_def=false if (price != curr_price_str or url != curr_url or
2337
+ block_count=5 cstruct_size=0 is_def=false description != curr_desc or final_model != curr_model or
2338
+ block_count=5 cstruct_size=0 is_def=false final_release != curr_release or final_ram != curr_ram or
2339
+ block_count=5 cstruct_size=0 is_def=false final_ssd != curr_ssd):
2340
+ block_count=5 cstruct_size=0 is_def=false cursor.execute(
2341
+ compo c_name=cursor
2342
+ block_count=6 cstruct_size=0 is_def=false """
2343
+ block_count=6 cstruct_size=0 is_def=false UPDATE products
2344
+ block_count=6 cstruct_size=0 is_def=false SET price = ?, url = ?, description = ?, model_number = ?, release_date = ?, ram = ?, ssd = ?, store = ?, updated_at = CURRENT_TIMESTAMP
2345
+ block_count=6 cstruct_size=0 is_def=false WHERE id = ?
2346
+ block_count=5 cstruct_size=0 is_def=false """
2347
+ block_count=6 cstruct_size=0 is_def=false , (price, url, description, final_model,
2348
+ block_count=6 cstruct_size=0 is_def=false final_release, final_ram, final_ssd, store,
2349
+ block_count=6 cstruct_size=0 is_def=false current_cheapest['id']))
2350
+ block_count=5 cstruct_size=0 is_def=false if not msg:
2351
+ block_count=6 cstruct_size=0 is_def=false msg = f'Updated product {name} info.'
2352
+ block_count=5 cstruct_size=0 is_def=false else:
2353
+ block_count=6 cstruct_size=0 is_def=false msg = msg_prefix
2354
+ block_count=5 cstruct_size=0 is_def=false email_subject = f'Product Updated: {name}'
2355
+ block_count=5 cstruct_size=0 is_def=false email_body = f"""Action: Updated Info
2356
+ block_count=0 cstruct_size=0 is_def=false Name: {name}
2357
+ block_count=0 cstruct_size=0 is_def=false Store: {store}
2358
+ block_count=0 cstruct_size=0 is_def=false Price: {price}
2359
+ block_count=0 cstruct_size=0 is_def=false URL: {url}
2360
+ block_count=0 cstruct_size=0 is_def=false Model: {final_model}
2361
+ block_count=0 cstruct_size=0 is_def=false Release: {final_release}
2362
+ block_count=0 cstruct_size=0 is_def=false RAM: {final_ram}
2363
+ block_count=0 cstruct_size=0 is_def=false SSD: {final_ssd}
2364
+ block_count=0 cstruct_size=0 is_def=false Description: {description}
2365
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
2366
+ block_count=5 cstruct_size=0 is_def=false send_email_notification(email_subject, email_body)
2367
+ block_count=4 cstruct_size=0 is_def=false else:
2368
+ block_count=5 cstruct_size=0 is_def=false msg = f'No changes for {name} at {store}.'
2369
+ block_count=3 cstruct_size=0 is_def=false if should_save or should_update:
2370
+ block_count=4 cstruct_size=0 is_def=false cursor.execute('SELECT id, price FROM products WHERE name = ?',
2371
+ compo c_name=cursor
2372
+ block_count=5 cstruct_size=0 is_def=false (name,))
2373
+ block_count=4 cstruct_size=0 is_def=false rows = cursor.fetchall()
2374
+ compo c_name=cursor
2375
+ block_count=4 cstruct_size=0 is_def=false if len(rows) > 1:
2376
+ block_count=5 cstruct_size=0 is_def=false rows_parsed = []
2377
+ block_count=5 cstruct_size=0 is_def=false for r in rows:
2378
+ block_count=6 cstruct_size=0 is_def=false rows_parsed.append({'id': r[0], 'val':
2379
+ block_count=7 cstruct_size=0 is_def=false parse_price_val(r[1])})
2380
+ block_count=5 cstruct_size=0 is_def=false rows_parsed.sort(key=lambda x: x['val'])
2381
+ block_count=5 cstruct_size=0 is_def=false winner = rows_parsed[0]
2382
+ block_count=5 cstruct_size=0 is_def=false for loser in rows_parsed[1:]:
2383
+ block_count=6 cstruct_size=0 is_def=false cursor.execute('DELETE FROM products WHERE id = ?',
2384
+ compo c_name=cursor
2385
+ block_count=7 cstruct_size=0 is_def=false (loser['id'],))
2386
+ block_count=5 cstruct_size=0 is_def=false msg += ' (Cleaned up duplicate records)'
2387
+ block_count=3 cstruct_size=0 is_def=false conn.commit()
2388
+ compo c_name=conn
2389
+ block_count=3 cstruct_size=0 is_def=false conn.close()
2390
+ compo c_name=conn
2391
+ block_count=3 cstruct_size=0 is_def=false return msg
2392
+ block_count=2 cstruct_size=0 is_def=false except Exception as e:
2393
+ block_count=3 cstruct_size=0 is_def=false return f'Error saving product: {str(e)}'
2394
+ block_count=0 cstruct_size=0 is_def=false class UpdatePricesInput(BaseModel):
2395
+ class_name=test_script.UpdatePricesInput
2396
+ base_name=BaseModel
2397
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
2398
+ compo c_name=Field
2399
+ block_count=0 cstruct_size=1 is_def=false class UpdatePricesTool(BaseTool):
2400
+ end of test_script.UpdatePricesInput
2401
+ class_name=test_script.UpdatePricesTool
2402
+ base_name=BaseTool
2403
+ block_count=1 cstruct_size=1 is_def=false name = 'update_prices'
2404
+ block_count=1 cstruct_size=1 is_def=false description = (
2405
+ 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.'
2406
+ block_count=2 cstruct_size=1 is_def=false )
2407
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = UpdatePricesInput
2408
+ block_count=1 cstruct_size=1 is_def=false def _fetch_page_content(self, url: str) ->(bool, str, str):
2409
+ block_count=2 cstruct_size=1 is_def=true """
2410
+ block_count=2 cstruct_size=1 is_def=true URLにアクセスし、(成功したか, 理由/エラー, HTMLテキスト) を返す
2411
+ block_count=2 cstruct_size=1 is_def=true """
2412
+ block_count=2 cstruct_size=1 is_def=true if not url or not url.startswith('http'):
2413
+ compo c_name=url
2414
+ block_count=3 cstruct_size=1 is_def=true return False, 'Invalid URL', ''
2415
+ block_count=2 cstruct_size=1 is_def=true try:
2416
+ block_count=3 cstruct_size=1 is_def=true req = urllib.request.Request(url, headers={'User-Agent':
2417
+ compo c_name=urllib
2418
+ compo c_name=Request
2419
+ 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'
2420
+ block_count=4 cstruct_size=1 is_def=true , 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8'})
2421
+ block_count=3 cstruct_size=1 is_def=true with urllib.request.urlopen(req, timeout=15) as response:
2422
+ compo c_name=urllib
2423
+ block_count=4 cstruct_size=1 is_def=true html_content = response.read().decode('utf-8', errors='ignore')
2424
+ compo c_name=response
2425
+ block_count=4 cstruct_size=1 is_def=true bot_keywords = ['ロボットではありません', 'アクセスが制限されています', 'キャプチャ',
2426
+ block_count=5 cstruct_size=1 is_def=true 'CAPTCHA', 'Are you a human?',
2427
+ block_count=5 cstruct_size=1 is_def=true 'Please verify you are a human', 'Incapsula', 'Cloudflare']
2428
+ block_count=4 cstruct_size=1 is_def=true html_lower = html_content.lower()
2429
+ block_count=4 cstruct_size=1 is_def=true for kw in bot_keywords:
2430
+ block_count=5 cstruct_size=1 is_def=true if kw.lower() in html_lower:
2431
+ compo c_name=kw
2432
+ block_count=6 cstruct_size=1 is_def=true return False, 'Bot Challenge Detected', ''
2433
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<script.*?>.*?</script>', '',
2434
+ compo c_name=re
2435
+ block_count=5 cstruct_size=1 is_def=true html_content, flags=re.DOTALL | re.IGNORECASE)
2436
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<style.*?>.*?</style>', '', clean_text,
2437
+ compo c_name=re
2438
+ block_count=5 cstruct_size=1 is_def=true flags=re.DOTALL | re.IGNORECASE)
2439
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<img[^>]+alt="([^"]*)"[^>]*>', ' \\1 ',
2440
+ compo c_name=re
2441
+ block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
2442
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub("<img[^>]+alt='([^']*)'[^>]*>", ' \\1 ',
2443
+ compo c_name=re
2444
+ block_count=5 cstruct_size=1 is_def=true clean_text, flags=re.IGNORECASE)
2445
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('<.*?>', ' ', clean_text)
2446
+ compo c_name=re
2447
+ block_count=4 cstruct_size=1 is_def=true clean_text = re.sub('\\s+', ' ', clean_text).strip()
2448
+ compo c_name=re
2449
+ block_count=4 cstruct_size=1 is_def=true if len(clean_text) > 10000:
2450
+ block_count=5 cstruct_size=1 is_def=true clean_text = clean_text[:10000]
2451
+ block_count=4 cstruct_size=1 is_def=true return True, 'Success', clean_text
2452
+ block_count=2 cstruct_size=1 is_def=true except urllib.error.HTTPError as e:
2453
+ compo c_name=urllib
2454
+ block_count=3 cstruct_size=1 is_def=true if e.code == 404:
2455
+ block_count=4 cstruct_size=1 is_def=true return False, '404 Not Found', ''
2456
+ block_count=3 cstruct_size=1 is_def=true elif e.code == 410:
2457
+ block_count=4 cstruct_size=1 is_def=true return False, '410 Gone', ''
2458
+ block_count=3 cstruct_size=1 is_def=true elif e.code in [500, 502, 503, 504]:
2459
+ block_count=4 cstruct_size=1 is_def=true return False, f'Retryable Server Error ({e.code})', ''
2460
+ block_count=3 cstruct_size=1 is_def=true elif e.code == 403:
2461
+ block_count=4 cstruct_size=1 is_def=true return False, '403 Forbidden (Possible Bot Block)', ''
2462
+ block_count=3 cstruct_size=1 is_def=true else:
2463
+ block_count=4 cstruct_size=1 is_def=true return False, f'HTTP Error {e.code}', ''
2464
+ block_count=2 cstruct_size=1 is_def=true except urllib.error.URLError as e:
2465
+ compo c_name=urllib
2466
+ block_count=3 cstruct_size=1 is_def=true return False, f'URL Error: {e.reason}', ''
2467
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
2468
+ block_count=3 cstruct_size=1 is_def=true return False, f'Connection Error: {e}', ''
2469
+ block_count=1 cstruct_size=1 is_def=true def _delete_product(self, product: dict, reason: str):
2470
+ block_count=2 cstruct_size=1 is_def=true try:
2471
+ block_count=3 cstruct_size=1 is_def=true conn = sqlite3.connect(DB_NAME)
2472
+ compo c_name=sqlite3
2473
+ block_count=3 cstruct_size=1 is_def=true cursor = conn.cursor()
2474
+ compo c_name=conn
2475
+ block_count=3 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (product[
2476
+ compo c_name=cursor
2477
+ block_count=4 cstruct_size=1 is_def=true 'id'],))
2478
+ block_count=3 cstruct_size=1 is_def=true conn.commit()
2479
+ compo c_name=conn
2480
+ block_count=3 cstruct_size=1 is_def=true conn.close()
2481
+ compo c_name=conn
2482
+ block_count=3 cstruct_size=1 is_def=true msg = f"Deleted {product['name']} at {product['store']} ({reason})"
2483
+ block_count=3 cstruct_size=1 is_def=true print(f' {msg}')
2484
+ block_count=3 cstruct_size=1 is_def=true email_subject = f"Product Deleted: {product['name']}"
2485
+ block_count=3 cstruct_size=1 is_def=true email_body = f"""Action: Deleted (Unavailable/Not Found)
2486
+ block_count=0 cstruct_size=1 is_def=true Name: {product['name']}
2487
+ end of test_script.UpdatePricesTool
2488
+ block_count=0 cstruct_size=0 is_def=false Store: {product['store']}
2489
+ block_count=0 cstruct_size=0 is_def=false URL: {product['url']}
2490
+ block_count=0 cstruct_size=0 is_def=false Reason: {reason}
2491
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
2492
+ block_count=3 cstruct_size=0 is_def=false send_email_notification(email_subject, email_body)
2493
+ block_count=3 cstruct_size=0 is_def=false return True
2494
+ block_count=2 cstruct_size=0 is_def=false except Exception as e:
2495
+ block_count=3 cstruct_size=0 is_def=false print(f' Error deleting product: {e}')
2496
+ block_count=3 cstruct_size=0 is_def=false return False
2497
+ block_count=1 cstruct_size=0 is_def=false def _run(self, query: str='', **kwargs):
2498
+ block_count=2 cstruct_size=0 is_def=true print('\n--- Starting Price Update (Direct URL Access) ---')
2499
+ block_count=2 cstruct_size=0 is_def=true products = get_all_products()
2500
+ block_count=2 cstruct_size=0 is_def=true if not products:
2501
+ block_count=3 cstruct_size=0 is_def=true return 'No products in database to update.'
2502
+ block_count=2 cstruct_size=0 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
2503
+ compo c_name=os
2504
+ block_count=2 cstruct_size=0 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
2505
+ compo c_name=ChatGoogleGenerativeAI
2506
+ block_count=2 cstruct_size=0 is_def=true updated_count = 0
2507
+ block_count=2 cstruct_size=0 is_def=true deleted_count = 0
2508
+ block_count=2 cstruct_size=0 is_def=true for p in products:
2509
+ block_count=3 cstruct_size=0 is_def=true name = p['name']
2510
+ block_count=3 cstruct_size=0 is_def=true store = p['store']
2511
+ block_count=3 cstruct_size=0 is_def=true url = p['url']
2512
+ block_count=3 cstruct_size=0 is_def=true print(f"Checking: {name} at {store} (ID: {p['id']})")
2513
+ block_count=3 cstruct_size=0 is_def=true if not url:
2514
+ block_count=4 cstruct_size=0 is_def=true print(f' [Warning] No URL for this product. Skipping.')
2515
+ block_count=4 cstruct_size=0 is_def=true continue
2516
+ block_count=3 cstruct_size=0 is_def=true success, access_reason, page_text = self._fetch_page_content(url)
2517
+ block_count=3 cstruct_size=0 is_def=true if not success:
2518
+ block_count=4 cstruct_size=0 is_def=true if ('404 Not Found' in access_reason or '410 Gone' in
2519
+ block_count=5 cstruct_size=0 is_def=true access_reason or 'Invalid URL' in access_reason):
2520
+ block_count=5 cstruct_size=0 is_def=true print(
2521
+ block_count=6 cstruct_size=0 is_def=true f' [Info] URL is dead ({access_reason}). Deleting product.'
2522
+ block_count=6 cstruct_size=0 is_def=true )
2523
+ block_count=5 cstruct_size=0 is_def=true if self._delete_product(p, access_reason):
2524
+ block_count=6 cstruct_size=0 is_def=true deleted_count += 1
2525
+ block_count=4 cstruct_size=0 is_def=true else:
2526
+ block_count=5 cstruct_size=0 is_def=true print(
2527
+ block_count=6 cstruct_size=0 is_def=true f' [Warning] Skipping update due to temporary/access error: {access_reason}'
2528
+ block_count=6 cstruct_size=0 is_def=true )
2529
+ block_count=4 cstruct_size=0 is_def=true continue
2530
+ block_count=3 cstruct_size=0 is_def=true prompt = f"""
2531
+ block_count=3 cstruct_size=0 is_def=true 以下のテキストは、ある商品のウェブページから抽出した内容です。
2532
+ block_count=3 cstruct_size=0 is_def=true このページの内容を分析し、以下のタスクを行ってください。
2533
+ block_count=3 cstruct_size=0 is_def=true 対象商品名: {name}
2534
+ block_count=3 cstruct_size=0 is_def=true 対象店舗: {store}
2535
+ block_count=3 cstruct_size=0 is_def=true 現在の価格: {p['price']}
2536
+ block_count=3 cstruct_size=0 is_def=true 抽出テキスト:
2537
+ block_count=3 cstruct_size=0 is_def=true {page_text}
2538
+ block_count=3 cstruct_size=0 is_def=true タスク:
2539
+ block_count=3 cstruct_size=0 is_def=true 1. このページで対象商品が現在も「販売中」かつ「在庫がある」か判定してください。
2540
+ block_count=3 cstruct_size=0 is_def=true ※「販売終了」「お探しのページは見つかりません」「在庫なし」「在庫切れ」「取り扱いできません」「該当の商品がありません」などの明確な記載(テキストや画像の代替テキスト(alt属性)含む)がある場合は is_unavailable を true にしてください。
2541
+ block_count=3 cstruct_size=0 is_def=true ※商品とは無関係な別商品の在庫情報に騙されないでください。
2542
+ block_count=3 cstruct_size=0 is_def=true 2. 販売中である場合、最新の「価格」「詳細情報・スペック(description)」「型番(model_number)」「発売日(release_date)」「メモリ容量(ram)」「SSD容量(ssd)」を抽出してください。発売日は数字とハイフンのみの日付にしてください(例: 2023-10-01)。型番は日付ではなくメーカー名やシリーズ名を含む英数字の文字列を抽出してください。もし発売日と混同されるような表記や数字とハイフンのみであれば、型番として抽出しないでください。
2543
+ block_count=3 cstruct_size=0 is_def=true 見つからない項目は空文字("")にしてください。
2544
+ block_count=3 cstruct_size=0 is_def=true ※注意: メモリ(RAM)やSSDなどの容量を抽出する際は、「最大〇〇GB」「〇〇GBまで増設可能」といった拡張上限の数値は対象外とし、必ず「標準搭載の容量」を抽出・記載してください。
2545
+ block_count=3 cstruct_size=0 is_def=true ※RAM容量の単位はGBに統一してください(例: 16384MB -> 16GB)。また、RAM容量やSSD容量に複数候補がある場合は、/(スラッシュ)区切りで保存してください(例: 256GB/512GB/1TB)。
2546
+ block_count=3 cstruct_size=0 is_def=true JSON形式で返してください。
2547
+ block_count=3 cstruct_size=0 is_def=true 出力例:
2548
+ block_count=3 cstruct_size=0 is_def=true {{
2549
+ block_count=4 cstruct_size=0 is_def=true "is_unavailable": false,
2550
+ block_count=4 cstruct_size=0 is_def=true "unavailability_reason": "",
2551
+ block_count=4 cstruct_size=0 is_def=true "price": "10,500円",
2552
+ block_count=4 cstruct_size=0 is_def=true "description": "最新モデル、送料無料",
2553
+ block_count=4 cstruct_size=0 is_def=true "model_number": "ABC-123",
2554
+ block_count=4 cstruct_size=0 is_def=true "release_date": "2023-10-01",
2555
+ block_count=4 cstruct_size=0 is_def=true "ram": "16GB",
2556
+ block_count=4 cstruct_size=0 is_def=true "ssd": "512GB"
2557
+ block_count=3 cstruct_size=0 is_def=true }}
2558
+ block_count=3 cstruct_size=0 is_def=true """
2559
+ block_count=3 cstruct_size=0 is_def=true try:
2560
+ block_count=4 cstruct_size=0 is_def=true response = llm.invoke(prompt)
2561
+ compo c_name=llm
2562
+ block_count=4 cstruct_size=0 is_def=true content = response.content
2563
+ compo c_name=response
2564
+ block_count=4 cstruct_size=0 is_def=true if '```json' in content:
2565
+ block_count=5 cstruct_size=0 is_def=true content = content.split('```json')[1].split('```')[0]
2566
+ compo c_name=content
2567
+ block_count=4 cstruct_size=0 is_def=true elif '```' in content:
2568
+ block_count=5 cstruct_size=0 is_def=true content = content.split('```')[1].split('```')[0]
2569
+ compo c_name=content
2570
+ block_count=4 cstruct_size=0 is_def=true content = content.strip()
2571
+ compo c_name=content
2572
+ block_count=4 cstruct_size=0 is_def=true content = re.sub('\\\\(?![/"\\\\bfnrtu])', '\\\\\\\\', content)
2573
+ compo c_name=re
2574
+ block_count=4 cstruct_size=0 is_def=true try:
2575
+ block_count=5 cstruct_size=0 is_def=true result_data = json.loads(content, strict=False)
2576
+ compo c_name=json
2577
+ block_count=4 cstruct_size=0 is_def=true except json.JSONDecodeError as e:
2578
+ block_count=5 cstruct_size=0 is_def=true print(
2579
+ block_count=6 cstruct_size=0 is_def=true f' [Warning] Failed to parse JSON: {e}. Attempting further cleanup.'
2580
+ block_count=6 cstruct_size=0 is_def=true )
2581
+ block_count=5 cstruct_size=0 is_def=true content = content.replace('\\', '')
2582
+ compo c_name=content
2583
+ block_count=5 cstruct_size=0 is_def=true result_data = json.loads(content, strict=False)
2584
+ compo c_name=json
2585
+ block_count=4 cstruct_size=0 is_def=true is_unavailable = result_data.get('is_unavailable', False)
2586
+ block_count=4 cstruct_size=0 is_def=true unavailability_reason = result_data.get('unavailability_reason'
2587
+ block_count=5 cstruct_size=0 is_def=true , 'ページ内に在庫なし・販売終了の記載あり')
2588
+ block_count=4 cstruct_size=0 is_def=true if is_unavailable:
2589
+ block_count=5 cstruct_size=0 is_def=true print(
2590
+ block_count=6 cstruct_size=0 is_def=true f' [Info] LLM determined product is unavailable. Reason: {unavailability_reason}. Deleting product.'
2591
+ block_count=6 cstruct_size=0 is_def=true )
2592
+ block_count=5 cstruct_size=0 is_def=true if self._delete_product(p, unavailability_reason):
2593
+ block_count=6 cstruct_size=0 is_def=true deleted_count += 1
2594
+ block_count=4 cstruct_size=0 is_def=true else:
2595
+ block_count=5 cstruct_size=0 is_def=true new_price = result_data.get('price', '')
2596
+ block_count=5 cstruct_size=0 is_def=true new_desc = result_data.get('description', '')
2597
+ block_count=5 cstruct_size=0 is_def=true new_model = result_data.get('model_number', '')
2598
+ block_count=5 cstruct_size=0 is_def=true new_release = result_data.get('release_date', '')
2599
+ block_count=5 cstruct_size=0 is_def=true new_ram = result_data.get('ram', '')
2600
+ block_count=5 cstruct_size=0 is_def=true new_ssd = result_data.get('ssd', '')
2601
+ block_count=5 cstruct_size=0 is_def=true if not new_price:
2602
+ block_count=6 cstruct_size=0 is_def=true print(
2603
+ block_count=7 cstruct_size=0 is_def=true f' [Warning] Could not extract price from page. Skipping update.'
2604
+ block_count=7 cstruct_size=0 is_def=true )
2605
+ block_count=6 cstruct_size=0 is_def=true continue
2606
+ block_count=5 cstruct_size=0 is_def=true final_desc = new_desc if new_desc else p['description']
2607
+ block_count=5 cstruct_size=0 is_def=true final_model = new_model if new_model else p.get(
2608
+ block_count=6 cstruct_size=0 is_def=true 'model_number', '')
2609
+ block_count=5 cstruct_size=0 is_def=true final_release = new_release if new_release else p.get(
2610
+ block_count=6 cstruct_size=0 is_def=true 'release_date', '')
2611
+ block_count=5 cstruct_size=0 is_def=true final_ram = new_ram if new_ram else p.get('ram', '')
2612
+ block_count=5 cstruct_size=0 is_def=true final_ssd = new_ssd if new_ssd else p.get('ssd', '')
2613
+ block_count=5 cstruct_size=0 is_def=true changes = []
2614
+ block_count=5 cstruct_size=0 is_def=true new_price_val = parse_price_val(new_price)
2615
+ block_count=5 cstruct_size=0 is_def=true old_price_val = parse_price_val(p['price'])
2616
+ block_count=5 cstruct_size=0 is_def=true if new_price_val != old_price_val:
2617
+ block_count=6 cstruct_size=0 is_def=true changes.append(f"Price ({p['price']} -> {new_price})")
2618
+ compo c_name=changes
2619
+ block_count=5 cstruct_size=0 is_def=true else:
2620
+ block_count=6 cstruct_size=0 is_def=true new_price = p['price']
2621
+ block_count=5 cstruct_size=0 is_def=true old_model = p.get('model_number', '')
2622
+ block_count=5 cstruct_size=0 is_def=true if not is_similar_model(final_model, old_model):
2623
+ block_count=6 cstruct_size=0 is_def=true changes.append(f'Model ({old_model} -> {final_model})')
2624
+ compo c_name=changes
2625
+ block_count=5 cstruct_size=0 is_def=true else:
2626
+ block_count=6 cstruct_size=0 is_def=true final_model = old_model
2627
+ block_count=5 cstruct_size=0 is_def=true old_release = p.get('release_date', '')
2628
+ block_count=5 cstruct_size=0 is_def=true if parse_date_val(final_release) != parse_date_val(
2629
+ block_count=6 cstruct_size=0 is_def=true old_release):
2630
+ block_count=6 cstruct_size=0 is_def=true changes.append(
2631
+ compo c_name=changes
2632
+ block_count=7 cstruct_size=0 is_def=true f'Release Date ({old_release} -> {final_release})')
2633
+ block_count=5 cstruct_size=0 is_def=true else:
2634
+ block_count=6 cstruct_size=0 is_def=true final_release = old_release
2635
+ block_count=5 cstruct_size=0 is_def=true old_ram = p.get('ram', '')
2636
+ block_count=5 cstruct_size=0 is_def=true if extract_alphanumeric(final_ram) != extract_alphanumeric(
2637
+ block_count=6 cstruct_size=0 is_def=true old_ram):
2638
+ block_count=6 cstruct_size=0 is_def=true changes.append(f'RAM ({old_ram} -> {final_ram})')
2639
+ compo c_name=changes
2640
+ block_count=5 cstruct_size=0 is_def=true else:
2641
+ block_count=6 cstruct_size=0 is_def=true final_ram = old_ram
2642
+ block_count=5 cstruct_size=0 is_def=true old_ssd = p.get('ssd', '')
2643
+ block_count=5 cstruct_size=0 is_def=true if extract_alphanumeric(final_ssd) != extract_alphanumeric(
2644
+ block_count=6 cstruct_size=0 is_def=true old_ssd):
2645
+ block_count=6 cstruct_size=0 is_def=true changes.append(f'SSD ({old_ssd} -> {final_ssd})')
2646
+ compo c_name=changes
2647
+ block_count=5 cstruct_size=0 is_def=true else:
2648
+ block_count=6 cstruct_size=0 is_def=true final_ssd = old_ssd
2649
+ block_count=5 cstruct_size=0 is_def=true if changes:
2650
+ block_count=6 cstruct_size=0 is_def=true try:
2651
+ block_count=7 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
2652
+ compo c_name=sqlite3
2653
+ block_count=7 cstruct_size=0 is_def=true cursor = conn.cursor()
2654
+ compo c_name=conn
2655
+ block_count=7 cstruct_size=0 is_def=true cursor.execute(
2656
+ compo c_name=cursor
2657
+ block_count=8 cstruct_size=0 is_def=true """
2658
+ block_count=8 cstruct_size=0 is_def=true UPDATE products
2659
+ block_count=8 cstruct_size=0 is_def=true SET price = ?, description = ?, model_number = ?, release_date = ?, ram = ?, ssd = ?, updated_at = CURRENT_TIMESTAMP
2660
+ block_count=8 cstruct_size=0 is_def=true WHERE id = ?
2661
+ block_count=7 cstruct_size=0 is_def=true """
2662
+ block_count=8 cstruct_size=0 is_def=true , (new_price, final_desc, final_model,
2663
+ block_count=8 cstruct_size=0 is_def=true final_release, final_ram, final_ssd, p['id']))
2664
+ block_count=7 cstruct_size=0 is_def=true conn.commit()
2665
+ compo c_name=conn
2666
+ block_count=7 cstruct_size=0 is_def=true if cursor.rowcount > 0:
2667
+ compo c_name=cursor
2668
+ block_count=8 cstruct_size=0 is_def=true updated_count += 1
2669
+ block_count=8 cstruct_size=0 is_def=true changes_str = ', '.join(changes)
2670
+ block_count=8 cstruct_size=0 is_def=true msg = (
2671
+ block_count=9 cstruct_size=0 is_def=true f'Updated {name} at {store}. Changes: {changes_str}'
2672
+ block_count=9 cstruct_size=0 is_def=true )
2673
+ block_count=8 cstruct_size=0 is_def=true print(f' {msg}')
2674
+ block_count=8 cstruct_size=0 is_def=true email_subject = f'Product Updated: {name}'
2675
+ block_count=8 cstruct_size=0 is_def=true email_body = f"""Action: Updated Info (Direct URL Check)
2676
+ block_count=0 cstruct_size=0 is_def=true Name: {name}
2677
+ block_count=0 cstruct_size=0 is_def=false Store: {store}
2678
+ block_count=0 cstruct_size=0 is_def=false URL: {url}
2679
+ block_count=0 cstruct_size=0 is_def=false Changed Fields:
2680
+ block_count=0 cstruct_size=0 is_def=false {changes_str}
2681
+ block_count=0 cstruct_size=0 is_def=false --- Current Data ---
2682
+ block_count=0 cstruct_size=0 is_def=false Price: {new_price}
2683
+ block_count=0 cstruct_size=0 is_def=false Model: {final_model}
2684
+ block_count=0 cstruct_size=0 is_def=false Release: {final_release}
2685
+ block_count=0 cstruct_size=0 is_def=false RAM: {final_ram}
2686
+ block_count=0 cstruct_size=0 is_def=false SSD: {final_ssd}
2687
+ block_count=0 cstruct_size=0 is_def=false Description: {final_desc}
2688
+ block_count=0 cstruct_size=0 is_def=false Message: {msg}"""
2689
+ block_count=8 cstruct_size=0 is_def=false send_email_notification(email_subject,
2690
+ block_count=9 cstruct_size=0 is_def=false email_body)
2691
+ block_count=7 cstruct_size=0 is_def=false conn.close()
2692
+ compo c_name=conn
2693
+ block_count=6 cstruct_size=0 is_def=false except Exception as e:
2694
+ block_count=7 cstruct_size=0 is_def=false print(f' Error updating {name} at {store}: {e}')
2695
+ block_count=5 cstruct_size=0 is_def=false else:
2696
+ block_count=6 cstruct_size=0 is_def=false print(f' No spec/price changes for {name} at {store}.'
2697
+ block_count=7 cstruct_size=0 is_def=false )
2698
+ block_count=3 cstruct_size=0 is_def=false except Exception as e:
2699
+ block_count=4 cstruct_size=0 is_def=false print(f' Error processing LLM response for {name}: {e}')
2700
+ block_count=3 cstruct_size=0 is_def=false time.sleep(1)
2701
+ compo c_name=time
2702
+ block_count=2 cstruct_size=0 is_def=false return (
2703
+ block_count=3 cstruct_size=0 is_def=false f'Price update complete. Updated {updated_count} items, Deleted {deleted_count} unavailable items.'
2704
+ block_count=3 cstruct_size=0 is_def=false )
2705
+ block_count=0 cstruct_size=0 is_def=false class SearchProductsInput(BaseModel):
2706
+ class_name=test_script.SearchProductsInput
2707
+ base_name=BaseModel
2708
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
2709
+ compo c_name=Field
2710
+ block_count=2 cstruct_size=1 is_def=false 'Natural language query to search products in the database')
2711
+ block_count=0 cstruct_size=1 is_def=false class SearchProductsTool(BaseTool):
2712
+ end of test_script.SearchProductsInput
2713
+ class_name=test_script.SearchProductsTool
2714
+ base_name=BaseTool
2715
+ block_count=1 cstruct_size=1 is_def=false name = 'search_products'
2716
+ block_count=1 cstruct_size=1 is_def=false description = (
2717
+ 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')."
2718
+ block_count=2 cstruct_size=1 is_def=false )
2719
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = SearchProductsInput
2720
+ block_count=1 cstruct_size=1 is_def=false def _run(self, query: str, **kwargs):
2721
+ block_count=2 cstruct_size=1 is_def=true print(f'\n--- Searching Database: {query} ---')
2722
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
2723
+ compo c_name=os
2724
+ block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
2725
+ compo c_name=ChatGoogleGenerativeAI
2726
+ block_count=2 cstruct_size=1 is_def=true prompt = f"""
2727
+ block_count=2 cstruct_size=1 is_def=true Analyze the user's search query for products and extract search criteria.
2728
+ block_count=2 cstruct_size=1 is_def=true User Query: {query}
2729
+ block_count=2 cstruct_size=1 is_def=true Return a JSON object with the following keys:
2730
+ block_count=2 cstruct_size=1 is_def=true - keyword_groups: List of LISTS of keywords. Each inner list represents synonyms (OR condition), and all outer lists must be satisfied (AND condition).
2731
+ block_count=2 cstruct_size=1 is_def=true - Example for "Cheap Mouse": [["mouse", "マウス"]] (Price is handled by sort_by)
2732
+ block_count=2 cstruct_size=1 is_def=true - Example for "16GB Memory": [["16GB", "16G", "16ギガ"], ["memory", "メモリ"]] -> "memory" is often redundant if "16GB" is unique, so prefer specific specs.
2733
+ block_count=2 cstruct_size=1 is_def=true - Example for "32GB PC": [["32GB", "32G"], ["PC", "パソコン", "computer"]]
2734
+ block_count=2 cstruct_size=1 is_def=true - exclude_keywords: List of keywords that MUST NOT appear in the product info (name, description, url).
2735
+ block_count=2 cstruct_size=1 is_def=true - Example for "No SSD": ["SSD"]
2736
+ block_count=2 cstruct_size=1 is_def=true - Example for "exclude memory info": ["memory", "メモリ"]
2737
+ block_count=2 cstruct_size=1 is_def=true - empty_fields: List of field names that must be empty or null (e.g. for "no description", "desc is empty", "url not set").
2738
+ block_count=2 cstruct_size=1 is_def=true - Valid values: "name", "price", "store", "url", "description"
2739
+ block_count=2 cstruct_size=1 is_def=true - sort_by: "price_asc" (cheapest), "price_desc" (expensive), or null (relevance)
2740
+ block_count=2 cstruct_size=1 is_def=true - max_price: integer or null
2741
+ block_count=2 cstruct_size=1 is_def=true - min_price: integer or null
2742
+ block_count=2 cstruct_size=1 is_def=true Important Rules for Keywords extraction:
2743
+ block_count=2 cstruct_size=1 is_def=true 1. Exclude Metadata Field Names: NEVER include words that refer to database columns like "URL", "url", "price", "name", "title", "description", "store" in `keyword_groups`.
2744
+ block_count=2 cstruct_size=1 is_def=true - CORRECT: "URL with example" -> [["example"]]
2745
+ block_count=2 cstruct_size=1 is_def=true - WRONG: "URL with example" -> [["URL"], ["example"]]
2746
+ block_count=2 cstruct_size=1 is_def=true - CORRECT: "URLにexampleが含まれる" -> [["example"]]
2747
+ block_count=2 cstruct_size=1 is_def=true 2. Exclude Action Verbs: Do not include "search", "find", "探して", "検索", "教えて".
2748
+ block_count=2 cstruct_size=1 is_def=true 3. Exclude General Terms: Do not include "product", "item", "thing", "もの", "商品".
2749
+ block_count=2 cstruct_size=1 is_def=true 4. If the query implies a category (e.g. "PC"), include it as a keyword group.
2750
+ block_count=2 cstruct_size=1 is_def=true Example JSON:
2751
+ block_count=2 cstruct_size=1 is_def=true {{
2752
+ block_count=3 cstruct_size=1 is_def=true "keyword_groups": [["mouse", "マウス"]],
2753
+ block_count=3 cstruct_size=1 is_def=true "exclude_keywords": [],
2754
+ block_count=3 cstruct_size=1 is_def=true "empty_fields": [],
2755
+ block_count=3 cstruct_size=1 is_def=true "sort_by": "price_asc",
2756
+ block_count=3 cstruct_size=1 is_def=true "max_price": 5000,
2757
+ block_count=3 cstruct_size=1 is_def=true "min_price": null
2758
+ block_count=2 cstruct_size=1 is_def=true }}
2759
+ block_count=2 cstruct_size=1 is_def=true """
2760
+ block_count=2 cstruct_size=1 is_def=true try:
2761
+ block_count=3 cstruct_size=1 is_def=true response = llm.invoke(prompt)
2762
+ compo c_name=llm
2763
+ block_count=3 cstruct_size=1 is_def=true content = response.content.strip()
2764
+ compo c_name=response
2765
+ block_count=3 cstruct_size=1 is_def=true if '```json' in content:
2766
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
2767
+ compo c_name=content
2768
+ block_count=3 cstruct_size=1 is_def=true elif '```' in content:
2769
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
2770
+ compo c_name=content
2771
+ block_count=3 cstruct_size=1 is_def=true criteria = json.loads(content)
2772
+ compo c_name=json
2773
+ block_count=3 cstruct_size=1 is_def=true print(f' Search Criteria: {criteria}')
2774
+ block_count=3 cstruct_size=1 is_def=true all_products = get_all_products()
2775
+ block_count=3 cstruct_size=1 is_def=true filtered_products = []
2776
+ block_count=3 cstruct_size=1 is_def=true keyword_groups = criteria.get('keyword_groups', [])
2777
+ compo c_name=criteria
2778
+ block_count=3 cstruct_size=1 is_def=true exclude_keywords = criteria.get('exclude_keywords', [])
2779
+ compo c_name=criteria
2780
+ block_count=3 cstruct_size=1 is_def=true empty_fields = criteria.get('empty_fields', [])
2781
+ compo c_name=criteria
2782
+ block_count=3 cstruct_size=1 is_def=true sort_by = criteria.get('sort_by')
2783
+ compo c_name=criteria
2784
+ block_count=3 cstruct_size=1 is_def=true max_p = criteria.get('max_price')
2785
+ compo c_name=criteria
2786
+ block_count=3 cstruct_size=1 is_def=true min_p = criteria.get('min_price')
2787
+ compo c_name=criteria
2788
+ block_count=3 cstruct_size=1 is_def=true for p in all_products:
2789
+ block_count=4 cstruct_size=1 is_def=true text_to_search = (p['name'] + ' ' + (p['description'] or ''
2790
+ block_count=5 cstruct_size=1 is_def=true ) + ' ' + (p['url'] or '')).lower()
2791
+ block_count=4 cstruct_size=1 is_def=true if empty_fields:
2792
+ block_count=5 cstruct_size=1 is_def=true is_empty_match = True
2793
+ block_count=5 cstruct_size=1 is_def=true for field in empty_fields:
2794
+ block_count=6 cstruct_size=1 is_def=true val = p.get(field)
2795
+ block_count=6 cstruct_size=1 is_def=true if val and str(val).strip():
2796
+ block_count=7 cstruct_size=1 is_def=true is_empty_match = False
2797
+ block_count=7 cstruct_size=1 is_def=true break
2798
+ block_count=5 cstruct_size=1 is_def=true if not is_empty_match:
2799
+ block_count=6 cstruct_size=1 is_def=true continue
2800
+ block_count=4 cstruct_size=1 is_def=true if exclude_keywords:
2801
+ block_count=5 cstruct_size=1 is_def=true should_exclude = False
2802
+ block_count=5 cstruct_size=1 is_def=true for k in exclude_keywords:
2803
+ block_count=6 cstruct_size=1 is_def=true if k.lower() in text_to_search:
2804
+ block_count=7 cstruct_size=1 is_def=true should_exclude = True
2805
+ block_count=7 cstruct_size=1 is_def=true break
2806
+ block_count=5 cstruct_size=1 is_def=true if should_exclude:
2807
+ block_count=6 cstruct_size=1 is_def=true continue
2808
+ block_count=4 cstruct_size=1 is_def=true if keyword_groups:
2809
+ block_count=5 cstruct_size=1 is_def=true all_groups_match = True
2810
+ block_count=5 cstruct_size=1 is_def=true for group in keyword_groups:
2811
+ block_count=6 cstruct_size=1 is_def=true group_match = False
2812
+ block_count=6 cstruct_size=1 is_def=true for k in group:
2813
+ block_count=7 cstruct_size=1 is_def=true if k.lower() in text_to_search:
2814
+ block_count=8 cstruct_size=1 is_def=true group_match = True
2815
+ block_count=8 cstruct_size=1 is_def=true break
2816
+ block_count=6 cstruct_size=1 is_def=true if not group_match:
2817
+ block_count=7 cstruct_size=1 is_def=true all_groups_match = False
2818
+ block_count=7 cstruct_size=1 is_def=true break
2819
+ block_count=5 cstruct_size=1 is_def=true if not all_groups_match:
2820
+ block_count=6 cstruct_size=1 is_def=true continue
2821
+ block_count=4 cstruct_size=1 is_def=true price_val = parse_price_val(p['price'])
2822
+ block_count=4 cstruct_size=1 is_def=true if max_p is not None and price_val > max_p:
2823
+ block_count=5 cstruct_size=1 is_def=true continue
2824
+ block_count=4 cstruct_size=1 is_def=true if min_p is not None and price_val < min_p:
2825
+ block_count=5 cstruct_size=1 is_def=true continue
2826
+ block_count=4 cstruct_size=1 is_def=true p['price_val'] = price_val
2827
+ block_count=4 cstruct_size=1 is_def=true filtered_products.append(p)
2828
+ block_count=3 cstruct_size=1 is_def=true if sort_by == 'price_asc':
2829
+ block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'])
2830
+ block_count=3 cstruct_size=1 is_def=true elif sort_by == 'price_desc':
2831
+ block_count=4 cstruct_size=1 is_def=true filtered_products.sort(key=lambda x: x['price_val'],
2832
+ block_count=5 cstruct_size=1 is_def=true reverse=True)
2833
+ block_count=3 cstruct_size=1 is_def=true if not filtered_products:
2834
+ block_count=4 cstruct_size=1 is_def=true return 'No products found matching your criteria.'
2835
+ block_count=3 cstruct_size=1 is_def=true result_str = f'Found {len(filtered_products)} products:\n'
2836
+ block_count=3 cstruct_size=1 is_def=true for p in filtered_products[:10]:
2837
+ block_count=4 cstruct_size=1 is_def=true result_str += (
2838
+ block_count=5 cstruct_size=1 is_def=true f"- [ID: {p['id']}] {p['name']} ({p['price']}) @ {p['store']}\n"
2839
+ block_count=5 cstruct_size=1 is_def=true )
2840
+ block_count=4 cstruct_size=1 is_def=true if p['description']:
2841
+ block_count=5 cstruct_size=1 is_def=true result_str += f" Desc: {p['description'][:100]}...\n"
2842
+ block_count=4 cstruct_size=1 is_def=true if p['url']:
2843
+ block_count=5 cstruct_size=1 is_def=true result_str += f" URL: {p['url']}\n"
2844
+ block_count=4 cstruct_size=1 is_def=true result_str += '\n'
2845
+ block_count=3 cstruct_size=1 is_def=true return result_str
2846
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
2847
+ block_count=3 cstruct_size=1 is_def=true return f'Error executing search: {e}'
2848
+ block_count=0 cstruct_size=1 is_def=true class FindSimilarProductsInput(BaseModel):
2849
+ end of test_script.SearchProductsTool
2850
+ class_name=test_script.FindSimilarProductsInput
2851
+ base_name=BaseModel
2852
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description='Optional query', default='')
2853
+ compo c_name=Field
2854
+ block_count=0 cstruct_size=1 is_def=false class FindSimilarProductsTool(BaseTool):
2855
+ end of test_script.FindSimilarProductsInput
2856
+ class_name=test_script.FindSimilarProductsTool
2857
+ base_name=BaseTool
2858
+ block_count=1 cstruct_size=1 is_def=false name = 'find_similar_products'
2859
+ block_count=1 cstruct_size=1 is_def=false description = (
2860
+ 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.'
2861
+ block_count=2 cstruct_size=1 is_def=false )
2862
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = FindSimilarProductsInput
2863
+ block_count=1 cstruct_size=1 is_def=false agent_logs: str = ''
2864
+ block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
2865
+ block_count=2 cstruct_size=1 is_def=true print('\n--- Starting Similar Product Search ---')
2866
+ block_count=2 cstruct_size=1 is_def=true products = get_all_products()
2867
+ block_count=2 cstruct_size=1 is_def=true if not products:
2868
+ block_count=3 cstruct_size=1 is_def=true return 'No products in database to base search on.'
2869
+ block_count=2 cstruct_size=1 is_def=true target_products = []
2870
+ block_count=2 cstruct_size=1 is_def=true if query:
2871
+ block_count=3 cstruct_size=1 is_def=true print(f'Filtering products with query: {query}')
2872
+ block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
2873
+ compo c_name=query
2874
+ block_count=3 cstruct_size=1 is_def=true for p in products:
2875
+ block_count=4 cstruct_size=1 is_def=true if query_lower in p['name'].lower() or p['description'
2876
+ block_count=5 cstruct_size=1 is_def=true ] and query_lower in p['description'].lower():
2877
+ block_count=5 cstruct_size=1 is_def=true target_products.append(p)
2878
+ block_count=2 cstruct_size=1 is_def=true else:
2879
+ block_count=3 cstruct_size=1 is_def=true target_products = products
2880
+ block_count=2 cstruct_size=1 is_def=true if not target_products:
2881
+ block_count=3 cstruct_size=1 is_def=true return f"No products found matching query '{query}'."
2882
+ block_count=2 cstruct_size=1 is_def=true unique_products = {}
2883
+ block_count=2 cstruct_size=1 is_def=true for p in target_products:
2884
+ block_count=3 cstruct_size=1 is_def=true if p['name'] not in unique_products:
2885
+ block_count=4 cstruct_size=1 is_def=true unique_products[p['name']] = p
2886
+ block_count=2 cstruct_size=1 is_def=true target_names = list(unique_products.keys())
2887
+ block_count=2 cstruct_size=1 is_def=true print(
2888
+ block_count=3 cstruct_size=1 is_def=true f'Found {len(target_names)} target products to find similar items for.'
2889
+ block_count=3 cstruct_size=1 is_def=true )
2890
+ block_count=2 cstruct_size=1 is_def=true logs_context = ''
2891
+ block_count=2 cstruct_size=1 is_def=true if self.agent_logs:
2892
+ compo c_name=self
2893
+ block_count=3 cstruct_size=1 is_def=true logs_context = f'\n参考情報 (過去の検索履歴):\n{self.agent_logs}\n'
2894
+ block_count=2 cstruct_size=1 is_def=true search = get_search_tool_func()
2895
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
2896
+ compo c_name=os
2897
+ block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0)
2898
+ compo c_name=ChatGoogleGenerativeAI
2899
+ block_count=2 cstruct_size=1 is_def=true save_tool = SaveProductTool()
2900
+ compo c_name=SaveProductTool
2901
+ block_count=2 cstruct_size=1 is_def=true cached_similar_items = []
2902
+ block_count=2 cstruct_size=1 is_def=true max_targets = 5
2903
+ block_count=2 cstruct_size=1 is_def=true if len(target_names) > max_targets:
2904
+ block_count=3 cstruct_size=1 is_def=true print(f'Limiting search to first {max_targets} products.')
2905
+ block_count=3 cstruct_size=1 is_def=true target_names = target_names[:max_targets]
2906
+ block_count=2 cstruct_size=1 is_def=true for name in target_names:
2907
+ block_count=3 cstruct_size=1 is_def=true product_data = unique_products[name]
2908
+ block_count=3 cstruct_size=1 is_def=true description = product_data.get('description', '')
2909
+ block_count=3 cstruct_size=1 is_def=true print(f'Searching for similar items to: {name}')
2910
+ block_count=3 cstruct_size=1 is_def=true search_query = f'{name} 類似商品 おすすめ 比較 スペック'
2911
+ block_count=3 cstruct_size=1 is_def=true try:
2912
+ block_count=4 cstruct_size=1 is_def=true search_results = search.run(search_query)
2913
+ compo c_name=search
2914
+ block_count=3 cstruct_size=1 is_def=true except Exception as e:
2915
+ block_count=4 cstruct_size=1 is_def=true print(f'Search failed for {name}: {e}')
2916
+ block_count=4 cstruct_size=1 is_def=true continue
2917
+ block_count=3 cstruct_size=1 is_def=true prompt = f"""
2918
+ block_count=3 cstruct_size=1 is_def=true 以下の検索結果に基づいて、"{name}" に類似した、または競合する製品を抽出してください。
2919
+ block_count=3 cstruct_size=1 is_def=true データベースに既に存在する "{name}" は除外してください。
2920
+ block_count=3 cstruct_size=1 is_def=true 【重要】選定基準:
2921
+ block_count=3 cstruct_size=1 is_def=true 基準となる商品情報:
2922
+ block_count=3 cstruct_size=1 is_def=true 名前: {name}
2923
+ block_count=3 cstruct_size=1 is_def=true 詳細: {description}
2924
+ block_count=3 cstruct_size=1 is_def=true 上記の基準商品と比較して、「スペック(CPU、メモリ、ストレージ、機能など)が同等かそれ以上」の製品のみを厳選してください。
2925
+ block_count=3 cstruct_size=1 is_def=true 基準商品より明らかにスペックが劣る製品(例: 古い世代のCPU、少ないメモリ、低い解像度など)は絶対に含めないでください。
2926
+ block_count=3 cstruct_size=1 is_def=true 価格が安くてもスペックが低いものは除外します。
2927
+ block_count=3 cstruct_size=1 is_def=true {logs_context}
2928
+ block_count=3 cstruct_size=1 is_def=true 検索結果:
2929
+ block_count=3 cstruct_size=1 is_def=true {search_results}
2930
+ block_count=3 cstruct_size=1 is_def=true タスク:
2931
+ block_count=3 cstruct_size=1 is_def=true 条件に合う製品の 名前、価格、販売店舗、URL、簡単な説明、型番(model_number)、発売日(release_date) を抽出してJSONリストで返してください。型番や発売日が不明な場合は空文字列にしてください。
2932
+ block_count=3 cstruct_size=1 is_def=true 重要:
2933
+ block_count=3 cstruct_size=1 is_def=true - URLと詳細情報は必須です。URLは必ず http または https で始まる有効なものにしてください。これらが見つからない、または取得できない場合は、その商品はスキップしてください。
2934
+ block_count=3 cstruct_size=1 is_def=true - 価格が不明な場合、または商品名や価格に(例)などと記載されている場合もスキップしてください。
2935
+ block_count=3 cstruct_size=1 is_def=true - 【必須条件】検索結果のスニペットやページ内に「販売終了」「お探しのページは見つかりません」「404 Not Found」「この商品は現在お取り扱いできません」のいずれかが含まれている場合は、その商品は「無効」とみなし、絶対にリストに含めないでください(スキップしてください)。
2936
+ block_count=3 cstruct_size=1 is_def=true - メモリ(RAM)やSSDなどの容量を説明に含める際、「最大〇〇GB」「〇〇GBまで増設可能」といった拡張上限の数値は対象外とし、必ず「標準搭載の容量」を抽出してください。
2937
+ block_count=3 cstruct_size=1 is_def=true JSON出力例:
2938
+ block_count=3 cstruct_size=1 is_def=true [
2939
+ block_count=4 cstruct_size=1 is_def=true {{
2940
+ block_count=5 cstruct_size=1 is_def=true "name": "競合商品A",
2941
+ block_count=5 cstruct_size=1 is_def=true "store": "Amazon",
2942
+ block_count=5 cstruct_size=1 is_def=true "price": "5,000円",
2943
+ block_count=5 cstruct_size=1 is_def=true "url": "https://www.amazon.co.jp/...",
2944
+ block_count=5 cstruct_size=1 is_def=true "description": "商品Aの類似品。機能X搭載。",
2945
+ block_count=5 cstruct_size=1 is_def=true "model_number": "XYZ-999",
2946
+ block_count=5 cstruct_size=1 is_def=true "release_date": "2024-01-15"
2947
+ block_count=4 cstruct_size=1 is_def=true }}
2948
+ block_count=3 cstruct_size=1 is_def=true ]
2949
+ block_count=3 cstruct_size=1 is_def=true """
2950
+ block_count=3 cstruct_size=1 is_def=true try:
2951
+ block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt)
2952
+ compo c_name=llm
2953
+ block_count=4 cstruct_size=1 is_def=true content = response.content
2954
+ compo c_name=response
2955
+ block_count=4 cstruct_size=1 is_def=true if '```json' in content:
2956
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
2957
+ compo c_name=content
2958
+ block_count=4 cstruct_size=1 is_def=true elif '```' in content:
2959
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
2960
+ compo c_name=content
2961
+ block_count=4 cstruct_size=1 is_def=true items = json.loads(content)
2962
+ compo c_name=json
2963
+ block_count=4 cstruct_size=1 is_def=true if isinstance(items, list):
2964
+ block_count=5 cstruct_size=1 is_def=true for item in items:
2965
+ block_count=6 cstruct_size=1 is_def=true if item.get('name') == name:
2966
+ compo c_name=item
2967
+ block_count=7 cstruct_size=1 is_def=true continue
2968
+ block_count=6 cstruct_size=1 is_def=true cached_similar_items.append(item)
2969
+ block_count=6 cstruct_size=1 is_def=true print(
2970
+ block_count=7 cstruct_size=1 is_def=true f" Cached: {item.get('name')} ({item.get('price')})"
2971
+ block_count=7 cstruct_size=1 is_def=true )
2972
+ block_count=3 cstruct_size=1 is_def=true except Exception as e:
2973
+ block_count=4 cstruct_size=1 is_def=true print(f'Error processing similar items for {name}: {e}')
2974
+ block_count=3 cstruct_size=1 is_def=true time.sleep(1)
2975
+ compo c_name=time
2976
+ block_count=2 cstruct_size=1 is_def=true if not cached_similar_items:
2977
+ block_count=3 cstruct_size=1 is_def=true return 'No similar products found.'
2978
+ block_count=2 cstruct_size=1 is_def=true print(
2979
+ block_count=3 cstruct_size=1 is_def=true f'\nCached {len(cached_similar_items)} items. Selecting top 3 recommendations...'
2980
+ block_count=3 cstruct_size=1 is_def=true )
2981
+ block_count=2 cstruct_size=1 is_def=true selection_prompt = f"""
2982
+ block_count=2 cstruct_size=1 is_def=true 以下の類似商品リストから、最もおすすめの製品を最大3つ選んでください。
2983
+ block_count=2 cstruct_size=1 is_def=true 選定基準:
2984
+ block_count=2 cstruct_size=1 is_def=true 1. 元の商品と同等かそれ以上の性能・品質であること。
2985
+ block_count=2 cstruct_size=1 is_def=true 2. 価格と性能のバランスが良いこと。
2986
+ block_count=2 cstruct_size=1 is_def=true 3. 詳細情報が豊富であること。
2987
+ block_count=2 cstruct_size=1 is_def=true {logs_context}
2988
+ block_count=2 cstruct_size=1 is_def=true 候補リスト:
2989
+ block_count=2 cstruct_size=1 is_def=true {json.dumps(cached_similar_items, ensure_ascii=False, indent=2)}
2990
+ block_count=2 cstruct_size=1 is_def=true タスク:
2991
+ block_count=2 cstruct_size=1 is_def=true 選定した3つの商品をJSONリスト形式で返してください。形式は入力と同じです。
2992
+ block_count=2 cstruct_size=1 is_def=true """
2993
+ block_count=2 cstruct_size=1 is_def=true added_count = 0
2994
+ block_count=2 cstruct_size=1 is_def=true try:
2995
+ block_count=3 cstruct_size=1 is_def=true response = llm.invoke(selection_prompt)
2996
+ compo c_name=llm
2997
+ block_count=3 cstruct_size=1 is_def=true content = response.content
2998
+ compo c_name=response
2999
+ block_count=3 cstruct_size=1 is_def=true if '```json' in content:
3000
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
3001
+ compo c_name=content
3002
+ block_count=3 cstruct_size=1 is_def=true elif '```' in content:
3003
+ block_count=4 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
3004
+ compo c_name=content
3005
+ block_count=3 cstruct_size=1 is_def=true top_picks = json.loads(content)
3006
+ compo c_name=json
3007
+ block_count=3 cstruct_size=1 is_def=true if isinstance(top_picks, list):
3008
+ block_count=4 cstruct_size=1 is_def=true for item in top_picks:
3009
+ block_count=5 cstruct_size=1 is_def=true print(f" Saving recommendation: {item.get('name')}")
3010
+ block_count=5 cstruct_size=1 is_def=true res = save_tool._run(name=item.get('name'), store=item.
3011
+ block_count=6 cstruct_size=1 is_def=true get('store', 'Unknown'), price=item.get('price'),
3012
+ block_count=6 cstruct_size=1 is_def=true url=item.get('url', ''), description=item.get(
3013
+ block_count=6 cstruct_size=1 is_def=true 'description', ''), model_number=item.get(
3014
+ block_count=6 cstruct_size=1 is_def=true 'model_number', ''), release_date=item.get(
3015
+ block_count=6 cstruct_size=1 is_def=true 'release_date', ''))
3016
+ block_count=5 cstruct_size=1 is_def=true print(f' -> {res}')
3017
+ block_count=5 cstruct_size=1 is_def=true added_count += 1
3018
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
3019
+ block_count=3 cstruct_size=1 is_def=true return f'Error selecting top recommendations: {e}'
3020
+ block_count=2 cstruct_size=1 is_def=true return (
3021
+ block_count=3 cstruct_size=1 is_def=true f'Similar product search complete. Added {added_count} recommended items.'
3022
+ block_count=3 cstruct_size=1 is_def=true )
3023
+ block_count=0 cstruct_size=1 is_def=true class CompareProductsInput(BaseModel):
3024
+ end of test_script.FindSimilarProductsTool
3025
+ class_name=test_script.CompareProductsInput
3026
+ base_name=BaseModel
3027
+ block_count=1 cstruct_size=1 is_def=false query: str = Field(description=
3028
+ compo c_name=Field
3029
+ block_count=2 cstruct_size=1 is_def=false "Optional category or query to filter products for comparison (e.g., 'laptop', 'monitor')."
3030
+ block_count=2 cstruct_size=1 is_def=false , default='')
3031
+ block_count=0 cstruct_size=1 is_def=false class CompareProductsTool(BaseTool):
3032
+ end of test_script.CompareProductsInput
3033
+ class_name=test_script.CompareProductsTool
3034
+ base_name=BaseTool
3035
+ block_count=1 cstruct_size=1 is_def=false name = 'compare_products'
3036
+ block_count=1 cstruct_size=1 is_def=false description = (
3037
+ 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.'
3038
+ block_count=2 cstruct_size=1 is_def=false )
3039
+ block_count=1 cstruct_size=1 is_def=false args_schema: Type[BaseModel] = CompareProductsInput
3040
+ block_count=1 cstruct_size=1 is_def=false def _run(self, query: str='', **kwargs):
3041
+ block_count=2 cstruct_size=1 is_def=true print(f'\n--- Generating Product Comparison: {query} ---')
3042
+ block_count=2 cstruct_size=1 is_def=true products = get_all_products()
3043
+ block_count=2 cstruct_size=1 is_def=true if not products:
3044
+ block_count=3 cstruct_size=1 is_def=true return 'No products found in database.'
3045
+ block_count=2 cstruct_size=1 is_def=true target_products = []
3046
+ block_count=2 cstruct_size=1 is_def=true if query:
3047
+ block_count=3 cstruct_size=1 is_def=true query_lower = query.lower()
3048
+ compo c_name=query
3049
+ block_count=3 cstruct_size=1 is_def=true for p in products:
3050
+ block_count=4 cstruct_size=1 is_def=true text = (p['name'] + ' ' + (p['description'] or '')).lower()
3051
+ block_count=4 cstruct_size=1 is_def=true if query_lower in text:
3052
+ block_count=5 cstruct_size=1 is_def=true target_products.append(p)
3053
+ block_count=2 cstruct_size=1 is_def=true else:
3054
+ block_count=3 cstruct_size=1 is_def=true target_products = products
3055
+ block_count=2 cstruct_size=1 is_def=true if not target_products:
3056
+ block_count=3 cstruct_size=1 is_def=true return f"No products found matching '{query}'."
3057
+ block_count=2 cstruct_size=1 is_def=true CHUNK_SIZE = 5
3058
+ block_count=2 cstruct_size=1 is_def=true print(
3059
+ block_count=3 cstruct_size=1 is_def=true f'Step 1: Extracting specs from {len(target_products)} products in chunks of {CHUNK_SIZE}...'
3060
+ block_count=3 cstruct_size=1 is_def=true )
3061
+ block_count=2 cstruct_size=1 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
3062
+ compo c_name=os
3063
+ block_count=2 cstruct_size=1 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
3064
+ compo c_name=ChatGoogleGenerativeAI
3065
+ block_count=3 cstruct_size=1 is_def=true max_output_tokens=8192)
3066
+ block_count=2 cstruct_size=1 is_def=true extracted_specs = []
3067
+ block_count=2 cstruct_size=1 is_def=true try:
3068
+ block_count=3 cstruct_size=1 is_def=true for i in range(0, len(target_products), CHUNK_SIZE):
3069
+ block_count=4 cstruct_size=1 is_def=true chunk = target_products[i:i + CHUNK_SIZE]
3070
+ block_count=4 cstruct_size=1 is_def=true print(
3071
+ block_count=5 cstruct_size=1 is_def=true f' Processing chunk {i // CHUNK_SIZE + 1} ({len(chunk)} items)...'
3072
+ block_count=5 cstruct_size=1 is_def=true )
3073
+ block_count=4 cstruct_size=1 is_def=true prompt_extract = f"""
3074
+ block_count=4 cstruct_size=1 is_def=true 以下の製品リストから、各製品の主要スペック情報を抽出してください。
3075
+ block_count=4 cstruct_size=1 is_def=true 製品リスト:
3076
+ block_count=4 cstruct_size=1 is_def=true {json.dumps([{k: v for k, v in p.items() if k != 'updated_at'} for p in chunk], ensure_ascii=False, indent=2)}
3077
+ block_count=4 cstruct_size=1 is_def=true タスク:
3078
+ block_count=4 cstruct_size=1 is_def=true 各製品について以下の情報を抽出し、JSONリスト形式で出力してください:
3079
+ block_count=4 cstruct_size=1 is_def=true 1. id: 元の製品ID (必須)
3080
+ block_count=4 cstruct_size=1 is_def=true 2. name: 製品名
3081
+ block_count=4 cstruct_size=1 is_def=true 3. price: 価格 (そのまま)
3082
+ block_count=4 cstruct_size=1 is_def=true 4. url: 製品ページのURL
3083
+ block_count=4 cstruct_size=1 is_def=true 5. ram: メモリ容量 (例: "16GB", "8GB", 不明なら "-") ※「最大〇〇GB」「増設可能」は無視し、標準搭載量のみを抽出すること。
3084
+ block_count=4 cstruct_size=1 is_def=true 6. ssd: ストレージ容量 (例: "512GB", "1TB", 不明なら "-") ※「最大〇〇GB」「増設可能」は無視し、標準搭載量のみを抽出すること。
3085
+ block_count=4 cstruct_size=1 is_def=true 7. cpu: プロセッサ (例: "Core i5", "M2", 不明なら "-")
3086
+ block_count=4 cstruct_size=1 is_def=true 8. os: OSの種類 (例: "Windows 11", "macOS", "ChromeOS", 不明なら "-")
3087
+ block_count=4 cstruct_size=1 is_def=true 9. model_number: 型番 (不明なら "-")
3088
+ block_count=4 cstruct_size=1 is_def=true 10. release_date: 発売日 (不明なら "-")
3089
+ block_count=4 cstruct_size=1 is_def=true 出力はJSONのみとし、Markdownコードブロックで囲ってください。
3090
+ block_count=4 cstruct_size=1 is_def=true """
3091
+ block_count=4 cstruct_size=1 is_def=true response = llm.invoke(prompt_extract)
3092
+ compo c_name=llm
3093
+ block_count=4 cstruct_size=1 is_def=true content = response.content
3094
+ compo c_name=response
3095
+ block_count=4 cstruct_size=1 is_def=true if '```json' in content:
3096
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```json')[1].split('```')[0]
3097
+ compo c_name=content
3098
+ block_count=4 cstruct_size=1 is_def=true elif '```' in content:
3099
+ block_count=5 cstruct_size=1 is_def=true content = content.split('```')[1].split('```')[0]
3100
+ compo c_name=content
3101
+ block_count=4 cstruct_size=1 is_def=true content = content.strip()
3102
+ compo c_name=content
3103
+ block_count=4 cstruct_size=1 is_def=true try:
3104
+ block_count=5 cstruct_size=1 is_def=true chunk_data = json.loads(content)
3105
+ compo c_name=json
3106
+ block_count=4 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
3107
+ block_count=5 cstruct_size=1 is_def=true print(
3108
+ block_count=6 cstruct_size=1 is_def=true f' [Warning] Failed to parse JSON in chunk {i // CHUNK_SIZE + 1}. Attempting aggressive recovery.'
3109
+ block_count=6 cstruct_size=1 is_def=true )
3110
+ block_count=5 cstruct_size=1 is_def=true print(f' Error detail: {e}')
3111
+ block_count=5 cstruct_size=1 is_def=true try:
3112
+ block_count=6 cstruct_size=1 is_def=true start_idx = content.find('[')
3113
+ compo c_name=content
3114
+ block_count=6 cstruct_size=1 is_def=true if start_idx != -1:
3115
+ block_count=7 cstruct_size=1 is_def=true clean_content = content[start_idx:]
3116
+ block_count=7 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
3117
+ block_count=8 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
3118
+ block_count=8 cstruct_size=1 is_def=true if last_brace != -1:
3119
+ block_count=9 cstruct_size=1 is_def=true clean_content = clean_content[:
3120
+ block_count=10 cstruct_size=1 is_def=true last_brace + 1] + ']'
3121
+ block_count=8 cstruct_size=1 is_def=true else:
3122
+ block_count=9 cstruct_size=1 is_def=true clean_content += ']'
3123
+ block_count=7 cstruct_size=1 is_def=true chunk_data = json.loads(clean_content)
3124
+ compo c_name=json
3125
+ block_count=6 cstruct_size=1 is_def=true else:
3126
+ block_count=7 cstruct_size=1 is_def=true raise ValueError('No JSON list found.')
3127
+ compo c_name=ValueError
3128
+ block_count=5 cstruct_size=1 is_def=true except Exception as e2:
3129
+ block_count=6 cstruct_size=1 is_def=true print(
3130
+ block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover JSON even after aggressive repair: {e2}'
3131
+ block_count=7 cstruct_size=1 is_def=true )
3132
+ block_count=6 cstruct_size=1 is_def=true continue
3133
+ block_count=4 cstruct_size=1 is_def=true if isinstance(chunk_data, list):
3134
+ block_count=5 cstruct_size=1 is_def=true extracted_specs.extend(chunk_data)
3135
+ block_count=4 cstruct_size=1 is_def=true time.sleep(1)
3136
+ compo c_name=time
3137
+ block_count=3 cstruct_size=1 is_def=true print(
3138
+ block_count=4 cstruct_size=1 is_def=true f'Step 2: Ranking {len(extracted_specs)} products based on extracted specs...'
3139
+ block_count=4 cstruct_size=1 is_def=true )
3140
+ block_count=3 cstruct_size=1 is_def=true prompt_rank = f"""
3141
+ block_count=3 cstruct_size=1 is_def=true 以下の製品の主要スペック一覧を分析し、価格と性能のバランスに基づいて全体の中からランキングを付けてください。
3142
+ block_count=3 cstruct_size=1 is_def=true 製品スペックリスト:
3143
+ block_count=3 cstruct_size=1 is_def=true {json.dumps(extracted_specs, ensure_ascii=False, indent=2)}
3144
+ block_count=3 cstruct_size=1 is_def=true タスク:
3145
+ block_count=3 cstruct_size=1 is_def=true 各製品に対して、以下の3つの情報のみを含むJSONリスト形式で出力してください:
3146
+ block_count=3 cstruct_size=1 is_def=true 1. id: 元の製品ID (必須)
3147
+ block_count=3 cstruct_size=1 is_def=true 2. note: 詳細なコメント (推奨理由、メリット・デメリット、他の製品と比較した際の特徴などを具体的に記述してください。例: "同価格帯の中で最もCPU性能が高く、動画編集に適している", "価格は安いがメモリが少ないため、軽作業向け")
3148
+ block_count=3 cstruct_size=1 is_def=true 3. rank: 全体の中での「おすすめ順位」 (1から始まる連番)
3149
+ block_count=3 cstruct_size=1 is_def=true 重要: 出力トークンを節約するため、nameやprice, url, specs(ram/ssd/cpu/os)等の再出力は絶対にしないでください。「id」「note」「rank」の3つだけを出力してください。
3150
+ block_count=3 cstruct_size=1 is_def=true 出力はJSONのみとし、Markdownコードブロックで囲ってください。
3151
+ block_count=3 cstruct_size=1 is_def=true リストの並び順は、rank(1位から順番)にしてください。
3152
+ block_count=3 cstruct_size=1 is_def=true """
3153
+ block_count=3 cstruct_size=1 is_def=true response_rank = llm.invoke(prompt_rank)
3154
+ compo c_name=llm
3155
+ block_count=3 cstruct_size=1 is_def=true content_rank = response_rank.content
3156
+ block_count=3 cstruct_size=1 is_def=true if '```json' in content_rank:
3157
+ block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```json')[1].split('```')[0]
3158
+ block_count=3 cstruct_size=1 is_def=true elif '```' in content_rank:
3159
+ block_count=4 cstruct_size=1 is_def=true content_rank = content_rank.split('```')[1].split('```')[0]
3160
+ block_count=3 cstruct_size=1 is_def=true content_rank = content_rank.strip()
3161
+ block_count=3 cstruct_size=1 is_def=true try:
3162
+ block_count=4 cstruct_size=1 is_def=true ranking_data = json.loads(content_rank)
3163
+ compo c_name=json
3164
+ block_count=3 cstruct_size=1 is_def=true except json.JSONDecodeError as e:
3165
+ block_count=4 cstruct_size=1 is_def=true print(
3166
+ block_count=5 cstruct_size=1 is_def=true f' [Warning] Failed to parse ranking JSON. Error: {e}. Attempting recovery.'
3167
+ block_count=5 cstruct_size=1 is_def=true )
3168
+ block_count=4 cstruct_size=1 is_def=true start_idx = content_rank.find('[')
3169
+ block_count=4 cstruct_size=1 is_def=true if start_idx != -1:
3170
+ block_count=5 cstruct_size=1 is_def=true clean_content = content_rank[start_idx:]
3171
+ block_count=5 cstruct_size=1 is_def=true if not clean_content.rstrip().endswith(']'):
3172
+ block_count=6 cstruct_size=1 is_def=true last_brace = clean_content.rfind('}')
3173
+ block_count=6 cstruct_size=1 is_def=true if last_brace != -1:
3174
+ block_count=7 cstruct_size=1 is_def=true clean_content = clean_content[:last_brace + 1
3175
+ block_count=8 cstruct_size=1 is_def=true ] + ']'
3176
+ block_count=6 cstruct_size=1 is_def=true else:
3177
+ block_count=7 cstruct_size=1 is_def=true clean_content += ']'
3178
+ block_count=5 cstruct_size=1 is_def=true try:
3179
+ block_count=6 cstruct_size=1 is_def=true ranking_data = json.loads(clean_content)
3180
+ compo c_name=json
3181
+ block_count=5 cstruct_size=1 is_def=true except Exception as e2:
3182
+ block_count=6 cstruct_size=1 is_def=true print(
3183
+ block_count=7 cstruct_size=1 is_def=true f' [Error] Could not recover ranking JSON: {e2}'
3184
+ block_count=7 cstruct_size=1 is_def=true )
3185
+ block_count=6 cstruct_size=1 is_def=true return (
3186
+ block_count=7 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
3187
+ block_count=7 cstruct_size=1 is_def=true )
3188
+ block_count=4 cstruct_size=1 is_def=true else:
3189
+ block_count=5 cstruct_size=1 is_def=true return (
3190
+ block_count=6 cstruct_size=1 is_def=true 'Error generating comparison: Invalid JSON format returned by LLM.'
3191
+ block_count=6 cstruct_size=1 is_def=true )
3192
+ block_count=3 cstruct_size=1 is_def=true spec_dict = {item['id']: item for item in extracted_specs}
3193
+ block_count=3 cstruct_size=1 is_def=true target_dict = {item['id']: item for item in target_products}
3194
+ block_count=3 cstruct_size=1 is_def=true final_comparison_data = []
3195
+ block_count=3 cstruct_size=1 is_def=true for rank_item in ranking_data:
3196
+ block_count=4 cstruct_size=1 is_def=true p_id = rank_item.get('id')
3197
+ block_count=4 cstruct_size=1 is_def=true if p_id in spec_dict:
3198
+ block_count=5 cstruct_size=1 is_def=true merged = spec_dict[p_id].copy()
3199
+ block_count=5 cstruct_size=1 is_def=true merged['rank'] = rank_item.get('rank')
3200
+ block_count=5 cstruct_size=1 is_def=true merged['note'] = rank_item.get('note', '')
3201
+ block_count=5 cstruct_size=1 is_def=true if p_id in target_dict:
3202
+ block_count=6 cstruct_size=1 is_def=true merged['updated_at'] = target_dict[p_id].get(
3203
+ block_count=7 cstruct_size=1 is_def=true 'updated_at', '')
3204
+ block_count=5 cstruct_size=1 is_def=true final_comparison_data.append(merged)
3205
+ block_count=3 cstruct_size=1 is_def=true final_comparison_data.sort(key=lambda x: x.get('rank', 9999))
3206
+ block_count=3 cstruct_size=1 is_def=true for item in final_comparison_data:
3207
+ block_count=4 cstruct_size=1 is_def=true if 'model_number' not in item or item['model_number'] is None:
3208
+ block_count=5 cstruct_size=1 is_def=true item['model_number'] = ''
3209
+ block_count=4 cstruct_size=1 is_def=true if 'release_date' not in item or item['release_date'] is None:
3210
+ block_count=5 cstruct_size=1 is_def=true item['release_date'] = ''
3211
+ block_count=3 cstruct_size=1 is_def=true from datetime import datetime
3212
+ block_count=3 cstruct_size=1 is_def=true output_data = {'updated_at': datetime.now().strftime(
3213
+ compo c_name=datetime
3214
+ block_count=4 cstruct_size=1 is_def=true '%Y-%m-%d %H:%M:%S'), 'products': final_comparison_data}
3215
+ block_count=3 cstruct_size=1 is_def=true output_file = 'product_comparison.json'
3216
+ block_count=3 cstruct_size=1 is_def=true with open(output_file, 'w', encoding='utf-8') as f:
3217
+ block_count=4 cstruct_size=1 is_def=true json.dump(output_data, f, ensure_ascii=False, indent=2)
3218
+ compo c_name=json
3219
+ block_count=3 cstruct_size=1 is_def=true return (
3220
+ 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.'
3221
+ block_count=4 cstruct_size=1 is_def=true )
3222
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
3223
+ block_count=3 cstruct_size=1 is_def=true return f'Error generating comparison: {e}'
3224
+ block_count=0 cstruct_size=1 is_def=true def display_products():
3225
+ end of test_script.CompareProductsTool
3226
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
3227
+ compo c_name=sqlite3
3228
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
3229
+ compo c_name=conn
3230
+ block_count=1 cstruct_size=0 is_def=true cursor.execute('SELECT id, name, store, price FROM products')
3231
+ compo c_name=cursor
3232
+ block_count=1 cstruct_size=0 is_def=true rows = cursor.fetchall()
3233
+ compo c_name=cursor
3234
+ block_count=1 cstruct_size=0 is_def=true conn.close()
3235
+ compo c_name=conn
3236
+ block_count=1 cstruct_size=0 is_def=true if not rows:
3237
+ block_count=2 cstruct_size=0 is_def=true print('\nNo products saved yet.')
3238
+ block_count=2 cstruct_size=0 is_def=true return
3239
+ block_count=1 cstruct_size=0 is_def=true def parse_price_sort(p_str):
3240
+ block_count=2 cstruct_size=0 is_def=true nums = re.findall('\\d+', str(p_str).replace(',', ''))
3241
+ compo c_name=re
3242
+ block_count=2 cstruct_size=0 is_def=true return int(''.join(nums)) if nums else float('inf')
3243
+ block_count=1 cstruct_size=0 is_def=true rows.sort(key=lambda x: (x[1], parse_price_sort(x[3])))
3244
+ compo c_name=rows
3245
+ block_count=1 cstruct_size=0 is_def=false print('\n--- All Saved Products ---')
3246
+ block_count=1 cstruct_size=0 is_def=false print(f"{'ID':<5} {'Name':<40} {'Store':<20} {'Price':<15}")
3247
+ block_count=1 cstruct_size=0 is_def=false print('-' * 85)
3248
+ block_count=1 cstruct_size=0 is_def=false for row in rows:
3249
+ block_count=2 cstruct_size=0 is_def=false name_disp = row[1][:37] + '..' if len(row[1]) > 39 else row[1]
3250
+ block_count=2 cstruct_size=0 is_def=false store_disp = row[2][:18] + '..' if len(row[2]) > 20 else row[2]
3251
+ block_count=2 cstruct_size=0 is_def=false print(f'{row[0]:<5} {name_disp:<40} {store_disp:<20} {row[3]:<15}')
3252
+ block_count=1 cstruct_size=0 is_def=false print('-' * 85)
3253
+ block_count=0 cstruct_size=0 is_def=false def show_product_details(product_id):
3254
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
3255
+ compo c_name=sqlite3
3256
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
3257
+ compo c_name=conn
3258
+ block_count=1 cstruct_size=0 is_def=true cursor.execute(
3259
+ compo c_name=cursor
3260
+ block_count=2 cstruct_size=0 is_def=true 'SELECT id, name, store, price, url, description, model_number, release_date FROM products WHERE id = ?'
3261
+ block_count=2 cstruct_size=0 is_def=true , (product_id,))
3262
+ block_count=1 cstruct_size=0 is_def=true row = cursor.fetchone()
3263
+ compo c_name=cursor
3264
+ block_count=1 cstruct_size=0 is_def=true conn.close()
3265
+ compo c_name=conn
3266
+ block_count=1 cstruct_size=0 is_def=true if not row:
3267
+ block_count=2 cstruct_size=0 is_def=true print(f'\nProduct with ID {product_id} not found.')
3268
+ block_count=2 cstruct_size=0 is_def=true return
3269
+ block_count=1 cstruct_size=0 is_def=true print('\n--- Product Details ---')
3270
+ block_count=1 cstruct_size=0 is_def=true print(f'ID: {row[0]}')
3271
+ block_count=1 cstruct_size=0 is_def=true print(f'Name: {row[1]}')
3272
+ block_count=1 cstruct_size=0 is_def=true print(f'Store: {row[2]}')
3273
+ block_count=1 cstruct_size=0 is_def=true print(f'Price: {row[3]}')
3274
+ block_count=1 cstruct_size=0 is_def=true print(f"Model: {row[6] if len(row) > 6 else ''}")
3275
+ block_count=1 cstruct_size=0 is_def=true print(f"Release: {row[7] if len(row) > 7 else ''}")
3276
+ block_count=1 cstruct_size=0 is_def=true print(f'URL: {row[4]}')
3277
+ block_count=1 cstruct_size=0 is_def=true print(f'Description: {row[5]}')
3278
+ block_count=1 cstruct_size=0 is_def=true print('-' * 30)
3279
+ block_count=0 cstruct_size=0 is_def=true def delete_product_records(identifiers: List[str]):
3280
+ block_count=1 cstruct_size=0 is_def=true conn = sqlite3.connect(DB_NAME)
3281
+ compo c_name=sqlite3
3282
+ block_count=1 cstruct_size=0 is_def=true cursor = conn.cursor()
3283
+ compo c_name=conn
3284
+ block_count=1 cstruct_size=0 is_def=true deleted_count = 0
3285
+ block_count=1 cstruct_size=0 is_def=true errors = []
3286
+ block_count=1 cstruct_size=0 is_def=true print(f'\nAttempting to delete: {identifiers}')
3287
+ block_count=1 cstruct_size=0 is_def=true for identifier in identifiers:
3288
+ block_count=2 cstruct_size=0 is_def=true try:
3289
+ block_count=3 cstruct_size=0 is_def=true if identifier.isdigit():
3290
+ compo c_name=identifier
3291
+ block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE id = ?', (int(
3292
+ compo c_name=cursor
3293
+ block_count=5 cstruct_size=0 is_def=true identifier),))
3294
+ block_count=3 cstruct_size=0 is_def=true else:
3295
+ block_count=4 cstruct_size=0 is_def=true cursor.execute('DELETE FROM products WHERE name = ?', (
3296
+ compo c_name=cursor
3297
+ block_count=5 cstruct_size=0 is_def=true identifier,))
3298
+ block_count=3 cstruct_size=0 is_def=true if cursor.rowcount > 0:
3299
+ compo c_name=cursor
3300
+ block_count=4 cstruct_size=0 is_def=true deleted_count += cursor.rowcount
3301
+ compo c_name=cursor
3302
+ block_count=4 cstruct_size=0 is_def=true print(f' Deleted: {identifier}')
3303
+ block_count=3 cstruct_size=0 is_def=true else:
3304
+ block_count=4 cstruct_size=0 is_def=true errors.append(f'No product found with ID/Name: {identifier}')
3305
+ compo c_name=errors
3306
+ block_count=2 cstruct_size=0 is_def=true except Exception as e:
3307
+ block_count=3 cstruct_size=0 is_def=true errors.append(f'Error deleting {identifier}: {e}')
3308
+ compo c_name=errors
3309
+ block_count=1 cstruct_size=0 is_def=true conn.commit()
3310
+ compo c_name=conn
3311
+ block_count=1 cstruct_size=0 is_def=true conn.close()
3312
+ compo c_name=conn
3313
+ block_count=1 cstruct_size=0 is_def=true print(f'\nTotal deleted: {deleted_count}')
3314
+ block_count=1 cstruct_size=0 is_def=true if errors:
3315
+ block_count=2 cstruct_size=0 is_def=true print('Errors/Warnings:')
3316
+ block_count=2 cstruct_size=0 is_def=true for err in errors:
3317
+ block_count=3 cstruct_size=0 is_def=true print(f' - {err}')
3318
+ block_count=0 cstruct_size=0 is_def=true def main():
3319
+ block_count=1 cstruct_size=0 is_def=true if not os.getenv('GOOGLE_API_KEY'):
3320
+ compo c_name=os
3321
+ block_count=2 cstruct_size=0 is_def=true print('Error: GOOGLE_API_KEY not found.')
3322
+ block_count=2 cstruct_size=0 is_def=true return
3323
+ block_count=1 cstruct_size=0 is_def=true provider = os.getenv('SEARCH_PROVIDER', 'serpapi')
3324
+ compo c_name=os
3325
+ block_count=1 cstruct_size=0 is_def=true if provider == 'serpapi' and not os.getenv('SERPAPI_API_KEY'):
3326
+ compo c_name=os
3327
+ block_count=2 cstruct_size=0 is_def=true print('Error: SERPAPI_API_KEY not found.')
3328
+ block_count=2 cstruct_size=0 is_def=true return
3329
+ block_count=1 cstruct_size=0 is_def=true elif provider == 'tavily_api' and not os.getenv('TAVILY_API_KEY'):
3330
+ compo c_name=os
3331
+ block_count=2 cstruct_size=0 is_def=true print('Error: TAVILY_API_KEY not found for Tavily search.')
3332
+ block_count=2 cstruct_size=0 is_def=true return
3333
+ block_count=1 cstruct_size=0 is_def=true elif provider == 'browser_use':
3334
+ block_count=2 cstruct_size=0 is_def=true pass
3335
+ block_count=1 cstruct_size=0 is_def=true model_name = os.getenv('MODEL_NAME', 'gemini-2.0-flash')
3336
+ compo c_name=os
3337
+ block_count=1 cstruct_size=0 is_def=true print(f'Using model: {model_name}')
3338
+ block_count=1 cstruct_size=0 is_def=true print(f'Using Search Provider: {provider}')
3339
+ block_count=1 cstruct_size=0 is_def=true llm = ChatGoogleGenerativeAI(model=model_name, temperature=0,
3340
+ compo c_name=ChatGoogleGenerativeAI
3341
+ block_count=2 cstruct_size=0 is_def=true max_retries=10)
3342
+ block_count=1 cstruct_size=0 is_def=true search = get_search_tool_func()
3343
+ block_count=1 cstruct_size=0 is_def=true search_tool = Tool(name='google_search', description=
3344
+ compo c_name=Tool
3345
+ block_count=2 cstruct_size=0 is_def=true 'Search Google for recent results.', func=search.run)
3346
+ block_count=1 cstruct_size=0 is_def=true startup_logs = get_all_agent_logs()
3347
+ block_count=1 cstruct_size=0 is_def=true print(f'Loaded {len(startup_logs)} characters of agent logs.')
3348
+ block_count=1 cstruct_size=0 is_def=true save_tool = SaveProductTool()
3349
+ compo c_name=SaveProductTool
3350
+ block_count=1 cstruct_size=0 is_def=true db_search_tool = SearchProductsTool()
3351
+ compo c_name=SearchProductsTool
3352
+ block_count=1 cstruct_size=0 is_def=true update_tool = UpdatePricesTool()
3353
+ compo c_name=UpdatePricesTool
3354
+ block_count=1 cstruct_size=0 is_def=true similar_tool = FindSimilarProductsTool(agent_logs=startup_logs)
3355
+ compo c_name=FindSimilarProductsTool
3356
+ block_count=1 cstruct_size=0 is_def=true compare_tool = CompareProductsTool()
3357
+ compo c_name=CompareProductsTool
3358
+ block_count=1 cstruct_size=0 is_def=true tools = [search_tool, save_tool, db_search_tool, update_tool,
3359
+ block_count=2 cstruct_size=0 is_def=true similar_tool, compare_tool]
3360
+ block_count=1 cstruct_size=0 is_def=true prompt = ChatPromptTemplate.from_messages([('system',
3361
+ compo c_name=ChatPromptTemplate
3362
+ block_count=2 cstruct_size=0 is_def=true """あなたは、商品の検索、保存、価格更新、類似商品検索、比較表作成を行う有能なアシスタントです。
3363
+ block_count=2 cstruct_size=0 is_def=true 利用可能なツール:
3364
+ block_count=2 cstruct_size=0 is_def=true 1. google_search: インターネット上の商品情報の検索に使用します。
3365
+ block_count=2 cstruct_size=0 is_def=true 2. save_product: 商品情報をデータベースに保存します。
3366
+ block_count=2 cstruct_size=0 is_def=true 3. search_products: データベース内に保存された商品を自然言語で検索します(例:「安いもの」「メモリが多いもの」)。
3367
+ block_count=2 cstruct_size=0 is_def=true 4. update_prices: データベース内の全商品の価格を最新の状態に更新します。
3368
+ block_count=2 cstruct_size=0 is_def=true 5. find_similar_products: データベース内の商品に類似した商品を探して追加します。
3369
+ block_count=2 cstruct_size=0 is_def=true 6. compare_products: データベース内の商品の比較表(RAM, SSD, 価格など)を作成し、おすすめ順に並べます。
3370
+ block_count=2 cstruct_size=0 is_def=true 重要: 検索を行って商品が見つかった場合は、必ず `save_product` ツールを使用して、見つかった各商品をデータベースに保存してください。
3371
+ block_count=2 cstruct_size=0 is_def=true 保存する際は、商品名、価格、店舗名、詳細、URLを含めてください。
3372
+ block_count=2 cstruct_size=0 is_def=true 【重要】URLと詳細情報(description)は保存において必須項目です。
3373
+ block_count=2 cstruct_size=0 is_def=true 特にURLは `http://` または `https://` で始まる有効な形式である必要があります。
3374
+ block_count=2 cstruct_size=0 is_def=true これらが取得できない場合やURLが無効な場合は、その商品は保存しないでください。
3375
+ block_count=2 cstruct_size=0 is_def=true 検索結果から情報を抽出する際は、これらの項目を必ず探してください。
3376
+ block_count=2 cstruct_size=0 is_def=true また、可能であれば「型番(model_number)」と「発売日(release_date)」も抽出・保存してください。
3377
+ block_count=2 cstruct_size=0 is_def=true 【無効な商品の保存禁止】
3378
+ block_count=2 cstruct_size=0 is_def=true 検索結果のスニペットや実際のページ内に、以下のいずれかの文言が含まれている商品は、現在利用できない無効な商品です。これらは絶対に `save_product` でデータベースに保存しないでください。
3379
+ block_count=2 cstruct_size=0 is_def=true - 「販売終了」
3380
+ block_count=2 cstruct_size=0 is_def=true - 「お探しのページは見つかりません」
3381
+ block_count=2 cstruct_size=0 is_def=true - 「404 Not Found」
3382
+ block_count=2 cstruct_size=0 is_def=true - 「この商品は現在お取り扱いできません」
3383
+ block_count=2 cstruct_size=0 is_def=true 価格情報が曖昧な場合(例:「10万円以下」)でも、上記の無効条件に該当せず、URLと詳細情報があれば `save_product` を使用して保存してください。
3384
+ block_count=2 cstruct_size=0 is_def=true その際、priceフィールドには見つかったテキスト(例:「10万円以下」)を入力してください。
3385
+ block_count=2 cstruct_size=0 is_def=true ユーザーの指示に従って適切なツールを使用してください。
3386
+ block_count=2 cstruct_size=0 is_def=true 「価格を更新して」と言われたら update_prices を使用してください。
3387
+ block_count=2 cstruct_size=0 is_def=true 「類似商品を探して」と言われたら find_similar_products を使用してください。
3388
+ block_count=2 cstruct_size=0 is_def=true """
3389
+ block_count=2 cstruct_size=0 is_def=true ), MessagesPlaceholder(variable_name='chat_history'), ('human',
3390
+ compo c_name=MessagesPlaceholder
3391
+ block_count=2 cstruct_size=0 is_def=true '{input}'), ('placeholder', '{agent_scratchpad}')])
3392
+ block_count=1 cstruct_size=0 is_def=true agent = create_tool_calling_agent(llm, tools, prompt)
3393
+ block_count=1 cstruct_size=0 is_def=true agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True,
3394
+ compo c_name=AgentExecutor
3395
+ block_count=2 cstruct_size=0 is_def=true return_intermediate_steps=True)
3396
+ block_count=1 cstruct_size=0 is_def=true print('Advanced AI Agent initialized.')
3397
+ block_count=1 cstruct_size=0 is_def=true print(
3398
+ block_count=2 cstruct_size=0 is_def=true "Commands: 'list', 'show <ID>', 'update', 'similar', 'delete <ID/Name> ...', 'quit'"
3399
+ block_count=2 cstruct_size=0 is_def=true )
3400
+ block_count=1 cstruct_size=0 is_def=true chat_history = []
3401
+ block_count=1 cstruct_size=0 is_def=true while True:
3402
+ block_count=2 cstruct_size=0 is_def=true try:
3403
+ block_count=3 cstruct_size=0 is_def=true try:
3404
+ block_count=4 cstruct_size=0 is_def=true user_input = input('\nEnter command or search query: ')
3405
+ block_count=3 cstruct_size=0 is_def=true except EOFError:
3406
+ block_count=4 cstruct_size=0 is_def=true print('\nEOF detected. Exiting...')
3407
+ block_count=4 cstruct_size=0 is_def=true break
3408
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() in ['quit', 'exit']:
3409
+ block_count=4 cstruct_size=0 is_def=true break
3410
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'list':
3411
+ block_count=4 cstruct_size=0 is_def=true display_products()
3412
+ block_count=4 cstruct_size=0 is_def=true continue
3413
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'update':
3414
+ block_count=4 cstruct_size=0 is_def=true result = update_tool._run()
3415
+ block_count=4 cstruct_size=0 is_def=true print(result)
3416
+ block_count=4 cstruct_size=0 is_def=true continue
3417
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower() == 'similar':
3418
+ block_count=4 cstruct_size=0 is_def=true result = similar_tool._run()
3419
+ block_count=4 cstruct_size=0 is_def=true print(result)
3420
+ block_count=4 cstruct_size=0 is_def=true continue
3421
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('compare'):
3422
+ block_count=4 cstruct_size=0 is_def=true parts = user_input.split(maxsplit=1)
3423
+ block_count=4 cstruct_size=0 is_def=true query = parts[1] if len(parts) > 1 else ''
3424
+ block_count=4 cstruct_size=0 is_def=true result = compare_tool._run(query)
3425
+ block_count=4 cstruct_size=0 is_def=true print(result)
3426
+ block_count=4 cstruct_size=0 is_def=true continue
3427
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('search_products '):
3428
+ block_count=4 cstruct_size=0 is_def=true query = user_input[16:].strip()
3429
+ block_count=4 cstruct_size=0 is_def=true if query:
3430
+ block_count=5 cstruct_size=0 is_def=true result = db_search_tool._run(query)
3431
+ block_count=5 cstruct_size=0 is_def=true print(result)
3432
+ block_count=4 cstruct_size=0 is_def=true else:
3433
+ block_count=5 cstruct_size=0 is_def=true print('Usage: search_products <query>')
3434
+ block_count=4 cstruct_size=0 is_def=true continue
3435
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('show '):
3436
+ block_count=4 cstruct_size=0 is_def=true parts = user_input.split()
3437
+ block_count=4 cstruct_size=0 is_def=true if len(parts) > 1 and parts[1].isdigit():
3438
+ block_count=5 cstruct_size=0 is_def=true show_product_details(int(parts[1]))
3439
+ block_count=4 cstruct_size=0 is_def=true else:
3440
+ block_count=5 cstruct_size=0 is_def=true print('Usage: show <product_id>')
3441
+ block_count=4 cstruct_size=0 is_def=true continue
3442
+ block_count=3 cstruct_size=0 is_def=true if user_input.lower().startswith('delete '):
3443
+ block_count=4 cstruct_size=0 is_def=true try:
3444
+ block_count=5 cstruct_size=0 is_def=true parts = shlex.split(user_input)
3445
+ compo c_name=shlex
3446
+ block_count=5 cstruct_size=0 is_def=true if len(parts) > 1:
3447
+ block_count=6 cstruct_size=0 is_def=true identifiers = parts[1:]
3448
+ block_count=6 cstruct_size=0 is_def=true delete_product_records(identifiers)
3449
+ block_count=5 cstruct_size=0 is_def=true else:
3450
+ block_count=6 cstruct_size=0 is_def=true print('Usage: delete <ID/Name> ...')
3451
+ block_count=4 cstruct_size=0 is_def=true except ValueError as e:
3452
+ block_count=5 cstruct_size=0 is_def=true print(f'Error parsing command: {e}')
3453
+ block_count=4 cstruct_size=0 is_def=true continue
3454
+ block_count=3 cstruct_size=0 is_def=true if user_input:
3455
+ block_count=4 cstruct_size=0 is_def=true print(f'\nProcessing: {user_input}...\n')
3456
+ block_count=4 cstruct_size=0 is_def=true result = agent_executor.invoke({'input': user_input,
3457
+ block_count=5 cstruct_size=0 is_def=true 'chat_history': chat_history})
3458
+ block_count=4 cstruct_size=0 is_def=true chat_history.append(HumanMessage(content=user_input))
3459
+ compo c_name=HumanMessage
3460
+ block_count=4 cstruct_size=0 is_def=true if isinstance(result['output'], str):
3461
+ block_count=5 cstruct_size=0 is_def=true chat_history.append(AIMessage(content=result['output']))
3462
+ compo c_name=AIMessage
3463
+ block_count=4 cstruct_size=0 is_def=true if 'intermediate_steps' in result:
3464
+ block_count=5 cstruct_size=0 is_def=true save_agent_log(user_input, result['intermediate_steps'])
3465
+ block_count=4 cstruct_size=0 is_def=true display_products()
3466
+ block_count=2 cstruct_size=0 is_def=true except KeyboardInterrupt:
3467
+ block_count=3 cstruct_size=0 is_def=true print('\nExiting...')
3468
+ block_count=3 cstruct_size=0 is_def=true break
3469
+ block_count=2 cstruct_size=0 is_def=true except Exception as e:
3470
+ block_count=3 cstruct_size=0 is_def=true print(f'An error occurred: {e}')
3471
+ block_count=0 cstruct_size=0 is_def=true if __name__ == '__main__':
3472
+ block_count=1 cstruct_size=0 is_def=false main()
3473
+ endf of ./test_script.py
3474
+ ./user_code.py
3475
+
3476
+ |python3 lib/del_comment.py ./user_code.py > /tmp/pylint20260323-801-8ryfar
3477
+ block_count=0 cstruct_size=0 is_def=false import json
3478
+ block_count=0 cstruct_size=0 is_def=false import sqlite3
3479
+ block_count=0 cstruct_size=0 is_def=false import re
3480
+ block_count=0 cstruct_size=0 is_def=false from pydantic import BaseModel, Field
3481
+ block_count=0 cstruct_size=0 is_def=false class SaveProductTool:
3482
+ class_name=user_code.SaveProductTool
3483
+ base_name=
3484
+ block_count=1 cstruct_size=1 is_def=false def _run(self, name: str, store: str=None, price: str=None, url: str='',
3485
+ block_count=2 cstruct_size=1 is_def=true description: str='', model_number: str='', release_date: str='',
3486
+ block_count=2 cstruct_size=1 is_def=true ram: str='', ssd: str='', **kwargs):
3487
+ block_count=2 cstruct_size=1 is_def=true try:
3488
+ block_count=3 cstruct_size=1 is_def=true if should_save:
3489
+ block_count=4 cstruct_size=1 is_def=true cursor.execute(
3490
+ compo c_name=cursor
3491
+ block_count=5 cstruct_size=1 is_def=true """
3492
+ block_count=5 cstruct_size=1 is_def=true INSERT INTO products (name, store, price, url, description, model_number, release_date, ram, ssd, updated_at)
3493
+ block_count=5 cstruct_size=1 is_def=true VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
3494
+ block_count=4 cstruct_size=1 is_def=true """
3495
+ block_count=5 cstruct_size=1 is_def=true , (name, store, price, url, description, model_number,
3496
+ block_count=5 cstruct_size=1 is_def=true release_date, ram, ssd))
3497
+ block_count=4 cstruct_size=1 is_def=true if not msg:
3498
+ block_count=5 cstruct_size=1 is_def=true msg = f'Saved product: {name} from {store} for {price}.'
3499
+ block_count=4 cstruct_size=1 is_def=true else:
3500
+ block_count=5 cstruct_size=1 is_def=true msg = msg_prefix
3501
+ block_count=3 cstruct_size=1 is_def=true if should_update:
3502
+ block_count=4 cstruct_size=1 is_def=true if price != curr_price_str:
3503
+ block_count=5 cstruct_size=1 is_def=true cursor.execute(
3504
+ compo c_name=cursor
3505
+ block_count=6 cstruct_size=1 is_def=true """
3506
+ block_count=6 cstruct_size=1 is_def=true UPDATE products
3507
+ block_count=6 cstruct_size=1 is_def=true SET price = ?, url = ?, description = ?, model_number = ?, release_date = ?, ram = ?, ssd = ?, store = ?, updated_at = CURRENT_TIMESTAMP
3508
+ block_count=6 cstruct_size=1 is_def=true WHERE id = ?
3509
+ block_count=5 cstruct_size=1 is_def=true """
3510
+ block_count=6 cstruct_size=1 is_def=true , (price, url, description, final_model,
3511
+ block_count=6 cstruct_size=1 is_def=true final_release, final_ram, final_ssd, store,
3512
+ block_count=6 cstruct_size=1 is_def=true current_cheapest['id']))
3513
+ block_count=5 cstruct_size=1 is_def=true if not msg:
3514
+ block_count=6 cstruct_size=1 is_def=true msg = f'Updated product {name} info.'
3515
+ block_count=5 cstruct_size=1 is_def=true else:
3516
+ block_count=6 cstruct_size=1 is_def=true msg = msg_prefix
3517
+ block_count=4 cstruct_size=1 is_def=true else:
3518
+ block_count=5 cstruct_size=1 is_def=true msg = f'No changes for {name} at {store}.'
3519
+ block_count=3 cstruct_size=1 is_def=true if should_save or should_update:
3520
+ block_count=4 cstruct_size=1 is_def=true cursor.execute('SELECT id, price FROM products WHERE name = ?',
3521
+ compo c_name=cursor
3522
+ block_count=5 cstruct_size=1 is_def=true (name,))
3523
+ block_count=4 cstruct_size=1 is_def=true rows = cursor.fetchall()
3524
+ compo c_name=cursor
3525
+ block_count=4 cstruct_size=1 is_def=true if len(rows) > 1:
3526
+ block_count=5 cstruct_size=1 is_def=true rows_parsed = []
3527
+ block_count=5 cstruct_size=1 is_def=true for r in rows:
3528
+ block_count=6 cstruct_size=1 is_def=true rows_parsed.append({'id': r[0], 'val':
3529
+ block_count=7 cstruct_size=1 is_def=true parse_price_val(r[1])})
3530
+ block_count=5 cstruct_size=1 is_def=true rows_parsed.sort(key=lambda x: x['val'])
3531
+ block_count=5 cstruct_size=1 is_def=true winner = rows_parsed[0]
3532
+ block_count=5 cstruct_size=1 is_def=true for loser in rows_parsed[1:]:
3533
+ block_count=6 cstruct_size=1 is_def=true cursor.execute('DELETE FROM products WHERE id = ?',
3534
+ compo c_name=cursor
3535
+ block_count=7 cstruct_size=1 is_def=true (loser['id'],))
3536
+ block_count=5 cstruct_size=1 is_def=true msg += ' (Cleaned up duplicate records)'
3537
+ block_count=3 cstruct_size=1 is_def=true conn.commit()
3538
+ compo c_name=conn
3539
+ block_count=3 cstruct_size=1 is_def=true conn.close()
3540
+ compo c_name=conn
3541
+ block_count=3 cstruct_size=1 is_def=true return msg
3542
+ block_count=2 cstruct_size=1 is_def=true except Exception as e:
3543
+ block_count=3 cstruct_size=1 is_def=true return f'Error saving product: {str(e)}'
3544
+ endf of ./user_code.py
3545
+ end of user_code.SaveProductTool
3546
+ m=agent.SaveProductTool
3547
+ m=agent.SaveProductTool
3548
+ m=test_script.SaveProductTool
3549
+ m=test_script.SaveProductTool
3550
+ m=user_code.SaveProductTool
3551
+ m=user_code.SaveProductTool
3552
+ m=agent.TavilySearchWrapper
3553
+ m=agent.TavilySearchWrapper
3554
+ m=test_script.TavilySearchWrapper
3555
+ m=test_script.TavilySearchWrapper
3556
+ m=agent.BrowserUseSearchWrapper
3557
+ m=agent.BrowserUseSearchWrapper
3558
+ m=test_script.BrowserUseSearchWrapper
3559
+ m=test_script.BrowserUseSearchWrapper
3560
+ m=agent.SaveProductTool
3561
+ m=agent.SaveProductTool
3562
+ m=test_script.SaveProductTool
3563
+ m=test_script.SaveProductTool
3564
+ m=user_code.SaveProductTool
3565
+ m=user_code.SaveProductTool
3566
+ m=agent.SearchProductsTool
3567
+ m=agent.SearchProductsTool
3568
+ m=test_script.SearchProductsTool
3569
+ m=test_script.SearchProductsTool
3570
+ m=agent.UpdatePricesTool
3571
+ m=agent.UpdatePricesTool
3572
+ m=test_script.UpdatePricesTool
3573
+ m=test_script.UpdatePricesTool
3574
+ m=agent.FindSimilarProductsTool
3575
+ m=agent.FindSimilarProductsTool
3576
+ m=test_script.FindSimilarProductsTool
3577
+ m=test_script.FindSimilarProductsTool
3578
+ m=agent.CompareProductsTool
3579
+ m=agent.CompareProductsTool
3580
+ m=test_script.CompareProductsTool
3581
+ m=test_script.CompareProductsTool
3582
+ m=agent.SaveProductTool
3583
+ m=agent.SaveProductTool
3584
+ m=test_script.SaveProductTool
3585
+ m=test_script.SaveProductTool
3586
+ m=user_code.SaveProductTool
3587
+ m=user_code.SaveProductTool
3588
+ m=agent.TavilySearchWrapper
3589
+ m=agent.TavilySearchWrapper
3590
+ m=test_script.TavilySearchWrapper
3591
+ m=test_script.TavilySearchWrapper
3592
+ m=agent.BrowserUseSearchWrapper
3593
+ m=agent.BrowserUseSearchWrapper
3594
+ m=test_script.BrowserUseSearchWrapper
3595
+ m=test_script.BrowserUseSearchWrapper
3596
+ m=agent.SaveProductTool
3597
+ m=agent.SaveProductTool
3598
+ m=test_script.SaveProductTool
3599
+ m=test_script.SaveProductTool
3600
+ m=user_code.SaveProductTool
3601
+ m=user_code.SaveProductTool
3602
+ m=agent.SearchProductsTool
3603
+ m=agent.SearchProductsTool
3604
+ m=test_script.SearchProductsTool
3605
+ m=test_script.SearchProductsTool
3606
+ m=agent.UpdatePricesTool
3607
+ m=agent.UpdatePricesTool
3608
+ m=test_script.UpdatePricesTool
3609
+ m=test_script.UpdatePricesTool
3610
+ m=agent.FindSimilarProductsTool
3611
+ m=agent.FindSimilarProductsTool
3612
+ m=test_script.FindSimilarProductsTool
3613
+ m=test_script.FindSimilarProductsTool
3614
+ m=agent.CompareProductsTool
3615
+ m=agent.CompareProductsTool
3616
+ m=test_script.CompareProductsTool
3617
+ m=test_script.CompareProductsTool