python_uml_class 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec_status +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +56 -41
- data/README.md +59 -2
- data/README_JA.md +99 -0
- data/img/class.png +0 -0
- data/lib/config/setting.json +23 -2
- data/lib/config.ru +0 -2
- data/lib/create_uml_class.rb +161 -34
- data/lib/css/index.css +6 -0
- data/lib/js/main.js +24 -10
- data/lib/python_uml_class/version.rb +1 -1
- data/lib/server.rb +12 -0
- data/lib/start.rb +28 -7
- data/lib/wsserver.rb +8 -1
- data/python_uml_class.gemspec +3 -0
- data/test_run.log +3617 -0
- data/test_run.rb +7 -0
- data/test_run_new.log +3083 -0
- data/test_script.py +1674 -0
- data/user_code.py +52 -0
- metadata +51 -2
data/test_run.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
|