Richard Zhang commited on
Commit
0e8b82d
·
1 Parent(s): 35bf4aa

Your commit message

Browse files
Files changed (2) hide show
  1. app.py +108 -50
  2. utils.py +815 -0
app.py CHANGED
@@ -1,64 +1,122 @@
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
 
 
 
3
 
4
- """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
6
- """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
 
 
 
 
9
 
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
 
 
19
 
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- messages.append({"role": "user", "content": message})
 
 
 
27
 
28
- response = ""
 
 
 
 
 
 
 
 
 
29
 
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- response += token
40
- yield response
 
 
 
 
41
 
 
 
 
 
 
 
 
42
 
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
61
 
 
 
 
 
62
 
 
 
 
 
 
 
 
 
 
 
 
63
  if __name__ == "__main__":
64
- demo.launch()
 
1
  import gradio as gr
2
+ from datetime import datetime
3
+ import json
4
+ import difflib # For better product name matching
5
+ from utils import get_product_by_name, get_products_by_category, get_products_and_category
6
 
7
+ # Dummy moderation function (replace with actual API)
8
+ def openai_moderation_check(text):
9
+ return {"flagged": False} # Assume no flagging for now
 
10
 
11
+ # Dummy AI response function (replace with actual API call to generate responses)
12
+ def get_completion_from_messages(messages):
13
+ return "This is a placeholder response from the model." # Mock response for now
14
 
15
+ # Function to log conversation to a JSON file
16
+ def log_conversation(user_input, ai_response, metadata=None, history=None):
17
+ log_entry = {
18
+ "timestamp": str(datetime.now()),
19
+ "user_input": user_input,
20
+ "ai_response": ai_response,
21
+ "metadata": metadata,
22
+ "history": history
23
+ }
24
+ with open("conversation_log.json", "a") as log_file:
25
+ log_file.write(json.dumps(log_entry) + "\n")
26
 
27
+ # Function to handle user queries using utils.py functions
28
+ def handle_user_query(user_input, all_messages=[], debug=False):
29
+ # Step 1: Moderation check on user input
30
+ moderation_result = openai_moderation_check(user_input)
31
+ if moderation_result["flagged"]:
32
+ return "Sorry, your request is non-compliant.", all_messages
33
+
34
+ # Step 2: Extract products and categories from user input
35
+ products_and_category = get_products_and_category()
36
+
37
+ # Debugging: Print the structure of products_and_category
38
+ if debug:
39
+ print("Products and Categories Structure:", products_and_category)
40
+
41
+ product_name = None
42
+ category_name = None
43
 
44
+ # Flatten the product list to check for partial matches
45
+ all_products = []
46
+ for category, products in products_and_category.items():
47
+ all_products.extend(products)
48
 
49
+ # Use difflib to find the closest match to the user input
50
+ product_matches = difflib.get_close_matches(user_input, all_products, n=1, cutoff=0.4)
51
+ if product_matches:
52
+ product_name = product_matches[0]
53
+
54
+ # Check for category match if no product match is found
55
+ if not product_name:
56
+ category_matches = difflib.get_close_matches(user_input, products_and_category.keys(), n=1, cutoff=0.4)
57
+ if category_matches:
58
+ category_name = category_matches[0]
59
 
60
+ # Step 3: Generate a response
61
+ if product_name:
62
+ product_info = get_product_by_name(product_name)
63
+ if product_info:
64
+ response = f"Product: {product_info['name']}\n" \
65
+ f"Category: {product_info['category']}\n" \
66
+ f"Price: ${product_info['price']}\n" \
67
+ f"Rating: {product_info['rating']} stars\n" \
68
+ f"Features: {', '.join(product_info['features'])}\n" \
69
+ f"Description: {product_info['description']}"
70
+ return response, all_messages
71
+ else:
72
+ return "Sorry, I couldn't find the product you're asking about.", all_messages
73
+
74
+ elif category_name:
75
+ products_in_category = get_products_by_category(category_name)
76
+ if products_in_category:
77
+ response = f"Category: {category_name}\n"
78
+ for product in products_in_category:
79
+ response += f"Product: {product['name']}\nPrice: ${product['price']}\n\n"
80
+ return response.strip(), all_messages
81
+ else:
82
+ return "Sorry, I couldn't find products in that category.", all_messages
83
+
84
+ else:
85
+ return "Please provide the name of a product or category you'd like to know about.", all_messages
86
 
87
+ # Chatbot logic to handle conversation
88
+ def handle_chat(user_input, history):
89
+ response, updated_history = handle_user_query(user_input, history)
90
+ history.append((user_input, response)) # Append the latest user input and response to the history
91
+ log_conversation(user_input, response) # Log the interaction
92
+ return response, history
93
 
94
+ # Gradio chatbot UI setup
95
+ def chatbot_ui():
96
+ with gr.Blocks() as app:
97
+ gr.Markdown("# Store Assistant Chatbot")
98
+ chatbot = gr.Chatbot(label="Chat with Store Assistant")
99
+ message_input = gr.Textbox(label="Ask about products!")
100
+ clear_btn = gr.Button("Clear Chat")
101
 
102
+ # Initialize conversation history as a stateful variable
103
+ conversation_history = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ # Process user input and update the conversation
106
+ def on_user_message(user_message, history):
107
+ response, updated_history = handle_chat(user_message, history)
108
+ return "", updated_history # Removed the extra append
109
 
110
+ # Clear chat history
111
+ def clear_chat():
112
+ return [], []
113
+
114
+ # Link input to chatbot
115
+ message_input.submit(on_user_message, inputs=[message_input, conversation_history], outputs=[message_input, chatbot])
116
+ clear_btn.click(clear_chat, inputs=[], outputs=[chatbot, conversation_history])
117
+
118
+ app.launch()
119
+
120
+ # Run the chatbot interface
121
  if __name__ == "__main__":
122
+ chatbot_ui()
utils.py ADDED
@@ -0,0 +1,815 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import json
3
+ from collections import defaultdict
4
+ # Configure OpenAI KEY
5
+ import os
6
+
7
+ from dotenv import load_dotenv
8
+ from openai import OpenAI
9
+
10
+
11
+ # Load environment variables from .env file
12
+ load_dotenv()
13
+
14
+
15
+ api_key = os.getenv("OPENAI_API_KEY") # 'ollama'
16
+ model = "gpt-4o-mini" # "gpt-4o-mini"
17
+ base_url = None
18
+
19
+
20
+ products_file = 'products.json'
21
+ categories_file = 'categories.json'
22
+
23
+
24
+ delimiter = "####"
25
+
26
+ step_2_system_message_content = f"""
27
+ You will be provided with customer service a conversation. \
28
+ The most recent user query will be delimited with \
29
+ {delimiter} characters.
30
+ Output a python list of objects, where each object has \
31
+ the following format:
32
+ 'category': <one of Computers and Laptops, \
33
+ Smartphones and Accessories, \
34
+ Televisions and Home Theater Systems, \
35
+ Gaming Consoles and Accessories,
36
+ Audio Equipment, Cameras and Camcorders>,
37
+ OR
38
+ 'products': <a list of products that must \
39
+ be found in the allowed products below>
40
+
41
+ Where the categories and products must be found in \
42
+ the customer service query.
43
+ If a product is mentioned, it must be associated with \
44
+ the correct category in the allowed products list below.
45
+ If no products or categories are found, output an \
46
+ empty list.
47
+ Only list products and categories that have not already \
48
+ been mentioned and discussed in the earlier parts of \
49
+ the conversation.
50
+
51
+ Allowed products:
52
+
53
+ Computers and Laptops category:
54
+ TechPro Ultrabook
55
+ BlueWave Gaming Laptop
56
+ PowerLite Convertible
57
+ TechPro Desktop
58
+ BlueWave Chromebook
59
+
60
+ Smartphones and Accessories category:
61
+ SmartX ProPhone
62
+ MobiTech PowerCase
63
+ SmartX MiniPhone
64
+ MobiTech Wireless Charger
65
+ SmartX EarBuds
66
+
67
+ Televisions and Home Theater Systems category:
68
+ CineView 4K TV
69
+ SoundMax Home Theater
70
+ CineView 8K TV
71
+ SoundMax Soundbar
72
+ CineView OLED TV
73
+
74
+ Gaming Consoles and Accessories category:
75
+ GameSphere X
76
+ ProGamer Controller
77
+ GameSphere Y
78
+ ProGamer Racing Wheel
79
+ GameSphere VR Headset
80
+
81
+ Audio Equipment category:
82
+ AudioPhonic Noise-Canceling Headphones
83
+ WaveSound Bluetooth Speaker
84
+ AudioPhonic True Wireless Earbuds
85
+ WaveSound Soundbar
86
+ AudioPhonic Turntable
87
+
88
+ Cameras and Camcorders category:
89
+ FotoSnap DSLR Camera
90
+ ActionCam 4K
91
+ FotoSnap Mirrorless Camera
92
+ ZoomMaster Camcorder
93
+ FotoSnap Instant Camera
94
+
95
+ Only output the list of objects, with nothing else.
96
+ """
97
+
98
+ step_2_system_message = {'role':'system', 'content': step_2_system_message_content}
99
+
100
+ # sys message for response to user
101
+ step_4_system_message_content = f"""
102
+ You are a customer service assistant for a large electronic store. \
103
+ Respond in a friendly and helpful tone, with VERY concise answers. \
104
+ Make sure to ask the user relevant follow-up questions.
105
+ """
106
+
107
+ step_4_system_message = {'role':'system', 'content': step_4_system_message_content}
108
+
109
+ # sys message for evaluation
110
+ step_6_system_message_content = f"""
111
+ You are an assistant that evaluates whether \
112
+ customer service agent responses sufficiently \
113
+ answer customer questions, and also validates that \
114
+ all the facts the assistant cites from the product \
115
+ information are correct.
116
+ The conversation history, product information, user and customer \
117
+ service agent messages will be delimited by \
118
+ 3 backticks, i.e. ```.
119
+ Respond with a Y or N character, with no punctuation:
120
+ Y - if the output sufficiently answers the question \
121
+ AND the response correctly uses product information
122
+ N - otherwise
123
+
124
+ Output a single letter only.
125
+ """
126
+
127
+ step_6_system_message = {'role':'system', 'content': step_6_system_message_content}
128
+
129
+
130
+
131
+ # Set your OpenAI API key from the environment variable
132
+ #api_key = os.getenv("HYPERBOLIC_API_KEY") # 'ollama'
133
+ #model = "meta-llama/Llama-3.2-90B-Vision-Instruct" # "gpt-4o-mini"
134
+ #base_url = "https://api.hyperbolic.xyz/v1/" # ollama 'http://localhost:11434/v1/'
135
+
136
+
137
+ client = OpenAI(
138
+ base_url=base_url,
139
+ api_key=api_key
140
+ )
141
+
142
+
143
+ def get_completion_from_messages(messages,
144
+ model="gpt-4o-mini",
145
+ temperature=0,
146
+ max_tokens=500):
147
+ '''
148
+ Encapsulate a function to access LLM
149
+
150
+ Parameters:
151
+ messages: This is a list of messages, each message is a dictionary containing role and content. The role can be 'system', 'user' or 'assistant', and the content is the message of the role.
152
+ model: The model to be called, default is gpt-4o-mini (ChatGPT)
153
+ temperature: This determines the randomness of the model output, default is 0, meaning the output will be very deterministic. Increasing temperature will make the output more random.
154
+ max_tokens: This determines the maximum number of tokens in the model output.
155
+ '''
156
+ response = client.chat.completions.create(
157
+ messages=messages,
158
+ model = model,
159
+ temperature=temperature, # This determines the randomness of the model's output
160
+ max_tokens=max_tokens, # This determines the maximum number of tokens in the model's output
161
+ )
162
+
163
+ return response.choices[0].message.content
164
+
165
+
166
+ def create_categories():
167
+ categories_dict = {
168
+ 'Billing': [
169
+ 'Unsubscribe or upgrade',
170
+ 'Add a payment method',
171
+ 'Explanation for charge',
172
+ 'Dispute a charge'],
173
+ 'Technical Support':[
174
+ 'General troubleshooting'
175
+ 'Device compatibility',
176
+ 'Software updates'],
177
+ 'Account Management':[
178
+ 'Password reset'
179
+ 'Update personal information',
180
+ 'Close account',
181
+ 'Account security'],
182
+ 'General Inquiry':[
183
+ 'Product information'
184
+ 'Pricing',
185
+ 'Feedback',
186
+ 'Speak to a human']
187
+ }
188
+
189
+ with open(categories_file, 'w') as file:
190
+ json.dump(categories_dict, file)
191
+
192
+ return categories_dict
193
+
194
+
195
+ def get_categories():
196
+ with open(categories_file, 'r') as file:
197
+ categories = json.load(file)
198
+ return categories
199
+
200
+
201
+ def get_product_list():
202
+ """
203
+ 具体原理参见第四节课
204
+ """
205
+ products = get_products()
206
+ product_list = []
207
+ for product in products.keys():
208
+ product_list.append(product)
209
+
210
+ return product_list
211
+
212
+
213
+ def get_products_and_category():
214
+ """
215
+ 具体原理参见第五节课
216
+ """
217
+ products = get_products()
218
+ products_by_category = defaultdict(list)
219
+ for product_name, product_info in products.items():
220
+ category = product_info.get('category')
221
+ if category:
222
+ products_by_category[category].append(product_info.get('name'))
223
+
224
+ return dict(products_by_category)
225
+
226
+
227
+ def get_products():
228
+ with open(products_file, 'r') as file:
229
+ products = json.load(file)
230
+ return products
231
+
232
+ # Get cate and products from user
233
+ def find_category_and_product(user_input,products_and_category):
234
+ delimiter = "####"
235
+ system_message = f"""
236
+ You will be provided with customer service queries. \
237
+ The customer service query will be delimited with {delimiter} characters.
238
+ Output a python list of json objects, where each object has the following format:
239
+ 'category': <one of Computers and Laptops, Smartphones and Accessories, Televisions and Home Theater Systems, \
240
+ Gaming Consoles and Accessories, Audio Equipment, Cameras and Camcorders>,
241
+ OR
242
+ 'products': <a list of products that must be found in the allowed products below>
243
+
244
+ Where the categories and products must be found in the customer service query.
245
+ If a product is mentioned, it must be associated with the correct category in the allowed products list below.
246
+ If no products or categories are found, output an empty list.
247
+
248
+ The allowed products are provided in JSON format.
249
+ The keys of each item represent the category.
250
+ The values of each item is a list of products that are within that category.
251
+ Allowed products: {products_and_category}
252
+
253
+ """
254
+ messages = [
255
+ {'role':'system', 'content': system_message},
256
+ {'role':'user', 'content': f"{delimiter}{user_input}{delimiter}"},
257
+ ]
258
+ return get_completion_from_messages(messages)
259
+
260
+ # Add allowed products
261
+ def find_category_and_product_only(user_input,products_and_category):
262
+ delimiter = "####"
263
+ system_message = f"""
264
+ You will be provided with customer service queries. \
265
+ The customer service query will be delimited with {delimiter} characters.
266
+ Output a python list of objects, where each object has the following format:
267
+ 'category': <one of Computers and Laptops, Smartphones and Accessories, Televisions and Home Theater Systems, \
268
+ Gaming Consoles and Accessories, Audio Equipment, Cameras and Camcorders>,
269
+ OR
270
+ 'products': <a list of products that must be found in the allowed products below>
271
+
272
+ Where the categories and products must be found in the customer service query.
273
+ If a product is mentioned, it must be associated with the correct category in the allowed products list below.
274
+ If no products or categories are found, output an empty list.
275
+
276
+ Allowed products:
277
+ Computers and Laptops category:
278
+ TechPro Ultrabook
279
+ BlueWave Gaming Laptop
280
+ PowerLite Convertible
281
+ TechPro Desktop
282
+ BlueWave Chromebook
283
+
284
+ Smartphones and Accessories category:
285
+ SmartX ProPhone
286
+ MobiTech PowerCase
287
+ SmartX MiniPhone
288
+ MobiTech Wireless Charger
289
+ SmartX EarBuds
290
+
291
+ Televisions and Home Theater Systems category:
292
+ CineView 4K TV
293
+ SoundMax Home Theater
294
+ CineView 8K TV
295
+ SoundMax Soundbar
296
+ CineView OLED TV
297
+
298
+ Gaming Consoles and Accessories category:
299
+ GameSphere X
300
+ ProGamer Controller
301
+ GameSphere Y
302
+ ProGamer Racing Wheel
303
+ GameSphere VR Headset
304
+
305
+ Audio Equipment category:
306
+ AudioPhonic Noise-Canceling Headphones
307
+ WaveSound Bluetooth Speaker
308
+ AudioPhonic True Wireless Earbuds
309
+ WaveSound Soundbar
310
+ AudioPhonic Turntable
311
+
312
+ Cameras and Camcorders category:
313
+ FotoSnap DSLR Camera
314
+ ActionCam 4K
315
+ FotoSnap Mirrorless Camera
316
+ ZoomMaster Camcorder
317
+ FotoSnap Instant Camera
318
+
319
+ Only output the list of objects, nothing else.
320
+ """
321
+ messages = [
322
+ {'role':'system', 'content': system_message},
323
+ {'role':'user', 'content': f"{delimiter}{user_input}{delimiter}"},
324
+ ]
325
+ return get_completion_from_messages(messages)
326
+
327
+ # Get product from user query
328
+ def get_products_from_query(user_msg):
329
+
330
+ products_and_category = get_products_and_category()
331
+ delimiter = "####"
332
+ system_message = f"""
333
+ You will be provided with customer service queries. \
334
+ The customer service query will be delimited with {delimiter} characters.
335
+ Output a python list of json objects, where each object has the following format:
336
+ 'category': <one of Computers and Laptops, Smartphones and Accessories, Televisions and Home Theater Systems, \
337
+ Gaming Consoles and Accessories, Audio Equipment, Cameras and Camcorders>,
338
+ OR
339
+ 'products': <a list of products that must be found in the allowed products below>
340
+
341
+ Where the categories and products must be found in the customer service query.
342
+ If a product is mentioned, it must be associated with the correct category in the allowed products list below.
343
+ If no products or categories are found, output an empty list.
344
+
345
+ The allowed products are provided in JSON format.
346
+ The keys of each item represent the category.
347
+ The values of each item is a list of products that are within that category.
348
+ Allowed products: {products_and_category}
349
+
350
+ """
351
+
352
+ messages = [
353
+ {'role':'system', 'content': system_message},
354
+ {'role':'user', 'content': f"{delimiter}{user_msg}{delimiter}"},
355
+ ]
356
+ category_and_product_response = get_completion_from_messages(messages)
357
+
358
+ return category_and_product_response
359
+
360
+
361
+
362
+ def get_product_by_name(name):
363
+ products = get_products()
364
+ return products.get(name, None)
365
+
366
+ def get_products_by_category(category):
367
+ products = get_products()
368
+ return [product for product in products.values() if product["category"] == category]
369
+
370
+ def get_mentioned_product_info(data_list):
371
+ """
372
+ 具体原理参见第五、六节课
373
+ """
374
+ product_info_l = []
375
+
376
+ if data_list is None:
377
+ return product_info_l
378
+
379
+ for data in data_list:
380
+ try:
381
+ if "products" in data:
382
+ products_list = data["products"]
383
+ for product_name in products_list:
384
+ product = get_product_by_name(product_name)
385
+ if product:
386
+ product_info_l.append(product)
387
+ else:
388
+ print(f"Error: Product '{product_name}' not found")
389
+ elif "category" in data:
390
+ category_name = data["category"]
391
+ category_products = get_products_by_category(category_name)
392
+ for product in category_products:
393
+ product_info_l.append(product)
394
+ else:
395
+ print("Error: Invalid object format")
396
+ except Exception as e:
397
+ print(f"Error: {e}")
398
+
399
+ return product_info_l
400
+
401
+
402
+
403
+ def read_string_to_list(input_string):
404
+ if input_string is None:
405
+ return None
406
+
407
+ try:
408
+ input_string = input_string.replace("'", "\"") # Replace single quotes with double quotes for valid JSON
409
+ data = json.loads(input_string)
410
+ return data
411
+ except json.JSONDecodeError:
412
+ print("Error: Invalid JSON string")
413
+ return None
414
+
415
+ def generate_output_string(data_list):
416
+ output_string = ""
417
+
418
+ if data_list is None:
419
+ return output_string
420
+
421
+ for data in data_list:
422
+ try:
423
+ if "products" in data:
424
+ products_list = data["products"]
425
+ for product_name in products_list:
426
+ product = get_product_by_name(product_name)
427
+ if product:
428
+ output_string += json.dumps(product, indent=4) + "\n"
429
+ else:
430
+ print(f"Error: Product '{product_name}' not found")
431
+ elif "category" in data:
432
+ category_name = data["category"]
433
+ category_products = get_products_by_category(category_name)
434
+ for product in category_products:
435
+ output_string += json.dumps(product, indent=4) + "\n"
436
+ else:
437
+ print("Error: Invalid object format")
438
+ except Exception as e:
439
+ print(f"Error: {e}")
440
+
441
+ return output_string
442
+
443
+ # Example usage:
444
+ #product_information_for_user_message_1 = generate_output_string(category_and_product_list)
445
+ #print(product_information_for_user_message_1)
446
+ # Answer questions from customers
447
+ def answer_user_msg(user_msg,product_info):
448
+ """
449
+ 代码参见第五节课
450
+ """
451
+ delimiter = "####"
452
+ system_message = f"""
453
+ You are a customer service assistant for a large electronic store. \
454
+ Respond in a friendly and helpful tone, with concise answers. \
455
+ Make sure to ask the user relevant follow up questions.
456
+ """
457
+ # user_msg = f"""
458
+ # tell me about the smartx pro phone and the fotosnap camera, the dslr one. Also what tell me about your tvs"""
459
+ messages = [
460
+ {'role':'system', 'content': system_message},
461
+ {'role':'user', 'content': f"{delimiter}{user_msg}{delimiter}"},
462
+ {'role':'assistant', 'content': f"Relevant product information:\n{product_info}"},
463
+ ]
464
+ response = get_completion_from_messages(messages)
465
+ return response
466
+
467
+ # 创建并存入商品数据
468
+ def create_products():
469
+ """
470
+ Create products dictionary and save it to a file named products.json
471
+ """
472
+ # product information
473
+ # fun fact: all these products are fake and were generated by a language model
474
+ products = {
475
+ "TechPro Ultrabook": {
476
+ "name": "TechPro Ultrabook",
477
+ "category": "Computers and Laptops",
478
+ "brand": "TechPro",
479
+ "model_number": "TP-UB100",
480
+ "warranty": "1 year",
481
+ "rating": 4.5,
482
+ "features": ["13.3-inch display", "8GB RAM", "256GB SSD", "Intel Core i5 processor"],
483
+ "description": "A sleek and lightweight ultrabook for everyday use.",
484
+ "price": 799.99
485
+ },
486
+ "BlueWave Gaming Laptop": {
487
+ "name": "BlueWave Gaming Laptop",
488
+ "category": "Computers and Laptops",
489
+ "brand": "BlueWave",
490
+ "model_number": "BW-GL200",
491
+ "warranty": "2 years",
492
+ "rating": 4.7,
493
+ "features": ["15.6-inch display", "16GB RAM", "512GB SSD", "NVIDIA GeForce RTX 3060"],
494
+ "description": "A high-performance gaming laptop for an immersive experience.",
495
+ "price": 1199.99
496
+ },
497
+ "PowerLite Convertible": {
498
+ "name": "PowerLite Convertible",
499
+ "category": "Computers and Laptops",
500
+ "brand": "PowerLite",
501
+ "model_number": "PL-CV300",
502
+ "warranty": "1 year",
503
+ "rating": 4.3,
504
+ "features": ["14-inch touchscreen", "8GB RAM", "256GB SSD", "360-degree hinge"],
505
+ "description": "A versatile convertible laptop with a responsive touchscreen.",
506
+ "price": 699.99
507
+ },
508
+ "TechPro Desktop": {
509
+ "name": "TechPro Desktop",
510
+ "category": "Computers and Laptops",
511
+ "brand": "TechPro",
512
+ "model_number": "TP-DT500",
513
+ "warranty": "1 year",
514
+ "rating": 4.4,
515
+ "features": ["Intel Core i7 processor", "16GB RAM", "1TB HDD", "NVIDIA GeForce GTX 1660"],
516
+ "description": "A powerful desktop computer for work and play.",
517
+ "price": 999.99
518
+ },
519
+ "BlueWave Chromebook": {
520
+ "name": "BlueWave Chromebook",
521
+ "category": "Computers and Laptops",
522
+ "brand": "BlueWave",
523
+ "model_number": "BW-CB100",
524
+ "warranty": "1 year",
525
+ "rating": 4.1,
526
+ "features": ["11.6-inch display", "4GB RAM", "32GB eMMC", "Chrome OS"],
527
+ "description": "A compact and affordable Chromebook for everyday tasks.",
528
+ "price": 249.99
529
+ },
530
+ "SmartX ProPhone": {
531
+ "name": "SmartX ProPhone",
532
+ "category": "Smartphones and Accessories",
533
+ "brand": "SmartX",
534
+ "model_number": "SX-PP10",
535
+ "warranty": "1 year",
536
+ "rating": 4.6,
537
+ "features": ["6.1-inch display", "128GB storage", "12MP dual camera", "5G"],
538
+ "description": "A powerful smartphone with advanced camera features.",
539
+ "price": 899.99
540
+ },
541
+ "MobiTech PowerCase": {
542
+ "name": "MobiTech PowerCase",
543
+ "category": "Smartphones and Accessories",
544
+ "brand": "MobiTech",
545
+ "model_number": "MT-PC20",
546
+ "warranty": "1 year",
547
+ "rating": 4.3,
548
+ "features": ["5000mAh battery", "Wireless charging", "Compatible with SmartX ProPhone"],
549
+ "description": "A protective case with built-in battery for extended usage.",
550
+ "price": 59.99
551
+ },
552
+ "SmartX MiniPhone": {
553
+ "name": "SmartX MiniPhone",
554
+ "category": "Smartphones and Accessories",
555
+ "brand": "SmartX",
556
+ "model_number": "SX-MP5",
557
+ "warranty": "1 year",
558
+ "rating": 4.2,
559
+ "features": ["4.7-inch display", "64GB storage", "8MP camera", "4G"],
560
+ "description": "A compact and affordable smartphone for basic tasks.",
561
+ "price": 399.99
562
+ },
563
+ "MobiTech Wireless Charger": {
564
+ "name": "MobiTech Wireless Charger",
565
+ "category": "Smartphones and Accessories",
566
+ "brand": "MobiTech",
567
+ "model_number": "MT-WC10",
568
+ "warranty": "1 year",
569
+ "rating": 4.5,
570
+ "features": ["10W fast charging", "Qi-compatible", "LED indicator", "Compact design"],
571
+ "description": "A convenient wireless charger for a clutter-free workspace.",
572
+ "price": 29.99
573
+ },
574
+ "SmartX EarBuds": {
575
+ "name": "SmartX EarBuds",
576
+ "category": "Smartphones and Accessories",
577
+ "brand": "SmartX",
578
+ "model_number": "SX-EB20",
579
+ "warranty": "1 year",
580
+ "rating": 4.4,
581
+ "features": ["True wireless", "Bluetooth 5.0", "Touch controls", "24-hour battery life"],
582
+ "description": "Experience true wireless freedom with these comfortable earbuds.",
583
+ "price": 99.99
584
+ },
585
+
586
+ "CineView 4K TV": {
587
+ "name": "CineView 4K TV",
588
+ "category": "Televisions and Home Theater Systems",
589
+ "brand": "CineView",
590
+ "model_number": "CV-4K55",
591
+ "warranty": "2 years",
592
+ "rating": 4.8,
593
+ "features": ["55-inch display", "4K resolution", "HDR", "Smart TV"],
594
+ "description": "A stunning 4K TV with vibrant colors and smart features.",
595
+ "price": 599.99
596
+ },
597
+ "SoundMax Home Theater": {
598
+ "name": "SoundMax Home Theater",
599
+ "category": "Televisions and Home Theater Systems",
600
+ "brand": "SoundMax",
601
+ "model_number": "SM-HT100",
602
+ "warranty": "1 year",
603
+ "rating": 4.4,
604
+ "features": ["5.1 channel", "1000W output", "Wireless subwoofer", "Bluetooth"],
605
+ "description": "A powerful home theater system for an immersive audio experience.",
606
+ "price": 399.99
607
+ },
608
+ "CineView 8K TV": {
609
+ "name": "CineView 8K TV",
610
+ "category": "Televisions and Home Theater Systems",
611
+ "brand": "CineView",
612
+ "model_number": "CV-8K65",
613
+ "warranty": "2 years",
614
+ "rating": 4.9,
615
+ "features": ["65-inch display", "8K resolution", "HDR", "Smart TV"],
616
+ "description": "Experience the future of television with this stunning 8K TV.",
617
+ "price": 2999.99
618
+ },
619
+ "SoundMax Soundbar": {
620
+ "name": "SoundMax Soundbar",
621
+ "category": "Televisions and Home Theater Systems",
622
+ "brand": "SoundMax",
623
+ "model_number": "SM-SB50",
624
+ "warranty": "1 year",
625
+ "rating": 4.3,
626
+ "features": ["2.1 channel", "300W output", "Wireless subwoofer", "Bluetooth"],
627
+ "description": "Upgrade your TV's audio with this sleek and powerful soundbar.",
628
+ "price": 199.99
629
+ },
630
+ "CineView OLED TV": {
631
+ "name": "CineView OLED TV",
632
+ "category": "Televisions and Home Theater Systems",
633
+ "brand": "CineView",
634
+ "model_number": "CV-OLED55",
635
+ "warranty": "2 years",
636
+ "rating": 4.7,
637
+ "features": ["55-inch display", "4K resolution", "HDR", "Smart TV"],
638
+ "description": "Experience true blacks and vibrant colors with this OLED TV.",
639
+ "price": 1499.99
640
+ },
641
+
642
+ "GameSphere X": {
643
+ "name": "GameSphere X",
644
+ "category": "Gaming Consoles and Accessories",
645
+ "brand": "GameSphere",
646
+ "model_number": "GS-X",
647
+ "warranty": "1 year",
648
+ "rating": 4.9,
649
+ "features": ["4K gaming", "1TB storage", "Backward compatibility", "Online multiplayer"],
650
+ "description": "A next-generation gaming console for the ultimate gaming experience.",
651
+ "price": 499.99
652
+ },
653
+ "ProGamer Controller": {
654
+ "name": "ProGamer Controller",
655
+ "category": "Gaming Consoles and Accessories",
656
+ "brand": "ProGamer",
657
+ "model_number": "PG-C100",
658
+ "warranty": "1 year",
659
+ "rating": 4.2,
660
+ "features": ["Ergonomic design", "Customizable buttons", "Wireless", "Rechargeable battery"],
661
+ "description": "A high-quality gaming controller for precision and comfort.",
662
+ "price": 59.99
663
+ },
664
+ "GameSphere Y": {
665
+ "name": "GameSphere Y",
666
+ "category": "Gaming Consoles and Accessories",
667
+ "brand": "GameSphere",
668
+ "model_number": "GS-Y",
669
+ "warranty": "1 year",
670
+ "rating": 4.8,
671
+ "features": ["4K gaming", "500GB storage", "Backward compatibility", "Online multiplayer"],
672
+ "description": "A compact gaming console with powerful performance.",
673
+ "price": 399.99
674
+ },
675
+ "ProGamer Racing Wheel": {
676
+ "name": "ProGamer Racing Wheel",
677
+ "category": "Gaming Consoles and Accessories",
678
+ "brand": "ProGamer",
679
+ "model_number": "PG-RW200",
680
+ "warranty": "1 year",
681
+ "rating": 4.5,
682
+ "features": ["Force feedback", "Adjustable pedals", "Paddle shifters", "Compatible with GameSphere X"],
683
+ "description": "Enhance your racing games with this realistic racing wheel.",
684
+ "price": 249.99
685
+ },
686
+ "GameSphere VR Headset": {
687
+ "name": "GameSphere VR Headset",
688
+ "category": "Gaming Consoles and Accessories",
689
+ "brand": "GameSphere",
690
+ "model_number": "GS-VR",
691
+ "warranty": "1 year",
692
+ "rating": 4.6,
693
+ "features": ["Immersive VR experience", "Built-in headphones", "Adjustable headband", "Compatible with GameSphere X"],
694
+ "description": "Step into the world of virtual reality with this comfortable VR headset.",
695
+ "price": 299.99
696
+ },
697
+
698
+ "AudioPhonic Noise-Canceling Headphones": {
699
+ "name": "AudioPhonic Noise-Canceling Headphones",
700
+ "category": "Audio Equipment",
701
+ "brand": "AudioPhonic",
702
+ "model_number": "AP-NC100",
703
+ "warranty": "1 year",
704
+ "rating": 4.6,
705
+ "features": ["Active noise-canceling", "Bluetooth", "20-hour battery life", "Comfortable fit"],
706
+ "description": "Experience immersive sound with these noise-canceling headphones.",
707
+ "price": 199.99
708
+ },
709
+ "WaveSound Bluetooth Speaker": {
710
+ "name": "WaveSound Bluetooth Speaker",
711
+ "category": "Audio Equipment",
712
+ "brand": "WaveSound",
713
+ "model_number": "WS-BS50",
714
+ "warranty": "1 year",
715
+ "rating": 4.5,
716
+ "features": ["Portable", "10-hour battery life", "Water-resistant", "Built-in microphone"],
717
+ "description": "A compact and versatile Bluetooth speaker for music on the go.",
718
+ "price": 49.99
719
+ },
720
+ "AudioPhonic True Wireless Earbuds": {
721
+ "name": "AudioPhonic True Wireless Earbuds",
722
+ "category": "Audio Equipment",
723
+ "brand": "AudioPhonic",
724
+ "model_number": "AP-TW20",
725
+ "warranty": "1 year",
726
+ "rating": 4.4,
727
+ "features": ["True wireless", "Bluetooth 5.0", "Touch controls", "18-hour battery life"],
728
+ "description": "Enjoy music without wires with these comfortable true wireless earbuds.",
729
+ "price": 79.99
730
+ },
731
+ "WaveSound Soundbar": {
732
+ "name": "WaveSound Soundbar",
733
+ "category": "Audio Equipment",
734
+ "brand": "WaveSound",
735
+ "model_number": "WS-SB40",
736
+ "warranty": "1 year",
737
+ "rating": 4.3,
738
+ "features": ["2.0 channel", "80W output", "Bluetooth", "Wall-mountable"],
739
+ "description": "Upgrade your TV's audio with this slim and powerful soundbar.",
740
+ "price": 99.99
741
+ },
742
+ "AudioPhonic Turntable": {
743
+ "name": "AudioPhonic Turntable",
744
+ "category": "Audio Equipment",
745
+ "brand": "AudioPhonic",
746
+ "model_number": "AP-TT10",
747
+ "warranty": "1 year",
748
+ "rating": 4.2,
749
+ "features": ["3-speed", "Built-in speakers", "Bluetooth", "USB recording"],
750
+ "description": "Rediscover your vinyl collection with this modern turntable.",
751
+ "price": 149.99
752
+ },
753
+
754
+ "FotoSnap DSLR Camera": {
755
+ "name": "FotoSnap DSLR Camera",
756
+ "category": "Cameras and Camcorders",
757
+ "brand": "FotoSnap",
758
+ "model_number": "FS-DSLR200",
759
+ "warranty": "1 year",
760
+ "rating": 4.7,
761
+ "features": ["24.2MP sensor", "1080p video", "3-inch LCD", "Interchangeable lenses"],
762
+ "description": "Capture stunning photos and videos with this versatile DSLR camera.",
763
+ "price": 599.99
764
+ },
765
+ "ActionCam 4K": {
766
+ "name": "ActionCam 4K",
767
+ "category": "Cameras and Camcorders",
768
+ "brand": "ActionCam",
769
+ "model_number": "AC-4K",
770
+ "warranty": "1 year",
771
+ "rating": 4.4,
772
+ "features": ["4K video", "Waterproof", "Image stabilization", "Wi-Fi"],
773
+ "description": "Record your adventures with this rugged and compact 4K action camera.",
774
+ "price": 299.99
775
+ },
776
+ "FotoSnap Mirrorless Camera": {
777
+ "name": "FotoSnap Mirrorless Camera",
778
+ "category": "Cameras and Camcorders",
779
+ "brand": "FotoSnap",
780
+ "model_number": "FS-ML100",
781
+ "warranty": "1 year",
782
+ "rating": 4.6,
783
+ "features": ["20.1MP sensor", "4K video", "3-inch touchscreen", "Interchangeable lenses"],
784
+ "description": "A compact and lightweight mirrorless camera with advanced features.",
785
+ "price": 799.99
786
+ },
787
+ "ZoomMaster Camcorder": {
788
+ "name": "ZoomMaster Camcorder",
789
+ "category": "Cameras and Camcorders",
790
+ "brand": "ZoomMaster",
791
+ "model_number": "ZM-CM50",
792
+ "warranty": "1 year",
793
+ "rating": 4.3,
794
+ "features": ["1080p video", "30x optical zoom", "3-inch LCD", "Image stabilization"],
795
+ "description": "Capture life's moments with this easy-to-use camcorder.",
796
+ "price": 249.99
797
+ },
798
+ "FotoSnap Instant Camera": {
799
+ "name": "FotoSnap Instant Camera",
800
+ "category": "Cameras and Camcorders",
801
+ "brand": "FotoSnap",
802
+ "model_number": "FS-IC10",
803
+ "warranty": "1 year",
804
+ "rating": 4.1,
805
+ "features": ["Instant prints", "Built-in flash", "Selfie mirror", "Battery-powered"],
806
+ "description": "Create instant memories with this fun and portable instant camera.",
807
+ "price": 69.99
808
+ }
809
+ }
810
+
811
+ products_file = 'products.json'
812
+ with open(products_file, 'w') as file:
813
+ json.dump(products, file)
814
+
815
+ return products