minhvtt commited on
Commit
011fe00
·
verified ·
1 Parent(s): 7659fe2

Update scenario_handlers/event_recommendation.py

Browse files
scenario_handlers/event_recommendation.py CHANGED
@@ -1,261 +1,269 @@
1
- """
2
- Event Recommendation Scenario Handler
3
- Recommends events based on user's vibe/mood with RAG integration
4
- """
5
- from typing import Dict, Any
6
- from .base_handler import BaseScenarioHandler
7
-
8
-
9
- class EventRecommendationHandler(BaseScenarioHandler):
10
- """
11
- Handle event recommendation flow
12
-
13
- Steps:
14
- 1. Ask for vibe/mood (Chill, Sôi động, Hài, Workshop)
15
- 2. Search events matching vibe → RAG
16
- 3. Show event list, ask which to see details
17
- 4. Ask what info needed (price, lineup, location, time)
18
- 5-8. Show specific info → RAG
19
- 9. Ask if want to save event to email
20
- 10. Collect email + send summary
21
- 11-12. End scenario
22
- """
23
-
24
- def start(self, initial_data: Dict = None) -> Dict[str, Any]:
25
- """Start event recommendation flow"""
26
- return {
27
- "message": "Hello! 👋 Bạn muốn tìm sự kiện theo vibe gì nè? Chill – Sôi động – Hài – Workshop?",
28
- "new_state": {
29
- "active_scenario": "event_recommendation",
30
- "scenario_step": 1,
31
- "scenario_data": initial_data or {}
32
- }
33
- }
34
-
35
- def next_step(self, current_step: int, user_input: str, scenario_data: Dict) -> Dict[str, Any]:
36
- """Process user input and advance scenario"""
37
-
38
- # Get expected input type for this step
39
- expected_type = self._get_expected_type(current_step)
40
-
41
- # Check for unexpected input (off-topic questions)
42
- unexpected = self.handle_unexpected_input(user_input, expected_type, current_step)
43
- if unexpected:
44
- return unexpected
45
-
46
- # ===== STEP 1: Collect interest tag =====
47
- if current_step == 1:
48
- scenario_data['interest_tag'] = user_input
49
-
50
- return {
51
- "message": f"Mình hiểu rồi! Để mình tìm sự kiện hợp vibe **{user_input}** nha",
52
- "new_state": {
53
- "active_scenario": "event_recommendation",
54
- "scenario_step": 2,
55
- "scenario_data": scenario_data
56
- },
57
- "scenario_active": True
58
- }
59
-
60
- # ===== STEP 2: Execute RAG search for events =====
61
- elif current_step == 2:
62
- # Search events matching interest_tag
63
- query = f"sự kiện phù hợp với {scenario_data.get('interest_tag', 'mọi người')}"
64
- print(f"🔍 RAG Search: {query}")
65
-
66
- results = self._search_rag(query, limit=3)
67
- formatted_events = self._format_event_list(results)
68
-
69
- # Save results to scenario data
70
- scenario_data['rag_results'] = formatted_events
71
- scenario_data['available_events'] = [
72
- r['metadata'].get('event_name', f'Event {i+1}')
73
- for i, r in enumerate(results[:3])
74
- ]
75
-
76
- return {
77
- "message": f"Đây là 2–3 event hợp với bạn nè:\n{formatted_events}\n\nBạn có muốn xem chi tiết event nào không?",
78
- "new_state": {
79
- "active_scenario": "event_recommendation",
80
- "scenario_step": 3,
81
- "scenario_data": scenario_data
82
- },
83
- "scenario_active": True,
84
- "loading_message": "⏳ Bạn đợi tôi tìm 1 chút nhé..."
85
- }
86
-
87
- # ===== STEP 3: User picks event =====
88
- elif current_step == 3:
89
- scenario_data['event_name'] = user_input
90
-
91
- return {
92
- "message": "Bạn cần xem: giá – line-up – địa điểm – hay thời gian của sự kiện?",
93
- "new_state": {
94
- "active_scenario": "event_recommendation",
95
- "scenario_step": 4,
96
- "scenario_data": scenario_data
97
- },
98
- "scenario_active": True
99
- }
100
-
101
- # ===== STEP 4: Branch based on info choice =====
102
- elif current_step == 4:
103
- choice = self._detect_choice(user_input)
104
- event_name = scenario_data.get('event_name', 'sự kiện này')
105
-
106
- # Build RAG query based on choice
107
- query_map = {
108
- 'price': f"giá vé {event_name}",
109
- 'lineup': f"lineup nghệ sĩ {event_name}",
110
- 'location': f"địa điểm tổ chức {event_name}",
111
- 'time': f"thời gian lịch diễn {event_name}"
112
- }
113
-
114
- query = query_map.get(choice, query_map['price'])
115
- print(f"🔍 RAG Search: {query}")
116
-
117
- results = self._search_rag(query)
118
- formatted_info = self._format_rag_results(results)
119
-
120
- # Build response message
121
- message_map = {
122
- 'price': f"Giá vé event {event_name} nè:\n{formatted_info}",
123
- 'lineup': f"Lineup / nghệ sĩ của event {event_name} là:\n{formatted_info}",
124
- 'location': f"Địa điểm tổ chức event {event_name}:\n{formatted_info}",
125
- 'time': f"Thời gian / lịch diễn của event {event_name}:\n{formatted_info}"
126
- }
127
-
128
- return {
129
- "message": message_map.get(choice, message_map['price']),
130
- "new_state": {
131
- "active_scenario": "event_recommendation",
132
- "scenario_step": 9, # Skip to email step
133
- "scenario_data": scenario_data
134
- },
135
- "scenario_active": True,
136
- "loading_message": "⏳ Bạn đợi tôi tìm 1 chút nhé..."
137
- }
138
-
139
- # ===== STEP 9: Ask if want to save event to email =====
140
- elif current_step == 9:
141
- choice = self._detect_yes_no(user_input)
142
-
143
- if choice == 'yes':
144
- return {
145
- "message": "Cho mình xin email để gửi bản tóm tắt event kèm link mua vé?",
146
- "new_state": {
147
- "active_scenario": "event_recommendation",
148
- "scenario_step": 10,
149
- "scenario_data": scenario_data
150
- },
151
- "scenario_active": True
152
- }
153
- else:
154
- return {
155
- "message": "Okie, bạn cần event theo vibe khác không nè? 😄",
156
- "new_state": None,
157
- "scenario_active": False,
158
- "end_scenario": True
159
- }
160
-
161
- # ===== STEP 10: Collect email and send summary =====
162
- elif current_step == 10:
163
- email = user_input.strip()
164
-
165
- if not self._validate_email(email):
166
- return {
167
- "message": "Email này có vẻ không đúng định dạng. Bạn nhập lại giúp mình nhé? (Ví dụ: name@example.com)",
168
- "new_state": None, # Stay at same step
169
- "scenario_active": True
170
- }
171
-
172
- # Save lead
173
- scenario_data['email'] = email
174
-
175
- try:
176
- self.lead_storage.save_lead(
177
- event_name=scenario_data.get('event_name', 'Unknown Event'),
178
- email=email,
179
- interests={
180
- "vibe": scenario_data.get('interest_tag'),
181
- "wants_event_summary": True
182
- },
183
- session_id=scenario_data.get('session_id')
184
- )
185
- print(f"📧 Lead saved: {email}")
186
- except Exception as e:
187
- print(f"⚠️ Error saving lead: {e}")
188
-
189
- return {
190
- "message": "Đã gửi email cho bạn nha! ✨",
191
- "new_state": None,
192
- "scenario_active": False,
193
- "end_scenario": True,
194
- "action": "send_event_summary_email"
195
- }
196
-
197
- # Fallback - unknown step
198
- return {
199
- "message": "Xin lỗi, có lỗi xảy ra. Bạn muốn bắt đầu lại không?",
200
- "new_state": None,
201
- "scenario_active": False,
202
- "end_scenario": True
203
- }
204
-
205
- def _get_expected_type(self, step: int) -> str:
206
- """Get expected input type for each step"""
207
- type_map = {
208
- 1: 'interest_tag',
209
- 2: None, # Auto-advance after RAG
210
- 3: 'event_name',
211
- 4: 'choice',
212
- 9: 'choice',
213
- 10: 'email'
214
- }
215
- return type_map.get(step, 'text')
216
-
217
- def _format_event_list(self, results: list) -> str:
218
- """Format event search results as numbered list"""
219
- if not results or len(results) == 0:
220
- return "Hiện tại chưa có event phù hợp 😢\nBạn thử vibe khác nhé!"
221
-
222
- events = []
223
- for i, r in enumerate(results[:3], 1):
224
- metadata = r.get('metadata', {})
225
- name = metadata.get('event_name', f'Event {i}')
226
- date = metadata.get('date', 'TBA')
227
- location = metadata.get('location', '')
228
-
229
- event_str = f"{i}. **{name}**"
230
- if date != 'TBA':
231
- event_str += f" ({date})"
232
- if location:
233
- event_str += f" - {location}"
234
-
235
- events.append(event_str)
236
-
237
- return "\n".join(events)
238
-
239
- def _detect_choice(self, user_input: str) -> str:
240
- """Detect what info user wants to see"""
241
- input_lower = user_input.lower()
242
-
243
- if any(k in input_lower for k in ['giá', 'price', 'vé', 'ticket', 'bao nhiêu']):
244
- return 'price'
245
- elif any(k in input_lower for k in ['lineup', 'line-up', 'nghệ sĩ', 'artist', 'performer']):
246
- return 'lineup'
247
- elif any(k in input_lower for k in ['địa điểm', 'location', 'ở đâu', 'where', 'chỗ']):
248
- return 'location'
249
- elif any(k in input_lower for k in ['thời gian', 'time', 'khi nào', 'when', 'lịch', 'date']):
250
- return 'time'
251
- else:
252
- return 'price' # Default
253
-
254
- def _detect_yes_no(self, user_input: str) -> str:
255
- """Detect yes/no response"""
256
- input_lower = user_input.lower()
257
-
258
- if any(k in input_lower for k in ['', 'yes', 'ok', 'được', 'ừ', 'oke']):
259
- return 'yes'
260
- else:
261
- return 'no'
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Event Recommendation Scenario Handler
3
+ Recommends events based on user's vibe/mood with RAG integration
4
+ """
5
+ from typing import Dict, Any
6
+ from .base_handler import BaseScenarioHandler
7
+
8
+
9
+ class EventRecommendationHandler(BaseScenarioHandler):
10
+ """
11
+ Handle event recommendation flow
12
+
13
+ Steps:
14
+ 1. Ask for vibe/mood (Chill, Sôi động, Hài, Workshop)
15
+ 2. Search events matching vibe → RAG
16
+ 3. Show event list, ask which to see details
17
+ 4. Ask what info needed (price, lineup, location, time)
18
+ 5-8. Show specific info → RAG
19
+ 9. Ask if want to save event to email
20
+ 10. Collect email + send summary
21
+ 11-12. End scenario
22
+ """
23
+
24
+ def start(self, initial_data: Dict = None) -> Dict[str, Any]:
25
+ """Start event recommendation flow"""
26
+ return {
27
+ "message": "Hello! 👋 Bạn muốn tìm sự kiện theo vibe gì nè? Chill – Sôi động – Hài – Workshop?",
28
+ "new_state": {
29
+ "active_scenario": "event_recommendation",
30
+ "scenario_step": 1,
31
+ "scenario_data": initial_data or {}
32
+ }
33
+ }
34
+
35
+ def next_step(self, current_step: int, user_input: str, scenario_data: Dict) -> Dict[str, Any]:
36
+ """Process user input and advance scenario"""
37
+
38
+ # Get expected input type for this step
39
+ expected_type = self._get_expected_type(current_step)
40
+
41
+ # Check for unexpected input (off-topic questions)
42
+ unexpected = self.handle_unexpected_input(user_input, expected_type, current_step)
43
+ if unexpected:
44
+ return unexpected
45
+
46
+ # ===== STEP 1: Collect interest tag =====
47
+ if current_step == 1:
48
+ scenario_data['interest_tag'] = user_input
49
+
50
+ return {
51
+ "message": f"Mình hiểu rồi! Để mình tìm sự kiện hợp vibe **{user_input}** nha",
52
+ "new_state": {
53
+ "active_scenario": "event_recommendation",
54
+ "scenario_step": 2,
55
+ "scenario_data": scenario_data
56
+ },
57
+ "scenario_active": True
58
+ }
59
+
60
+ # ===== STEP 2: Execute RAG search for events =====
61
+ elif current_step == 2:
62
+ # Search events matching interest_tag
63
+ query = f"sự kiện phù hợp với {scenario_data.get('interest_tag', 'mọi người')}"
64
+ print(f"🔍 RAG Search: {query}")
65
+
66
+ results = self._search_rag(query, limit=3)
67
+ formatted_events = self._format_event_list(results)
68
+
69
+ # Save results to scenario data
70
+ scenario_data['rag_results'] = formatted_events
71
+ scenario_data['available_events'] = [
72
+ r['metadata'].get('event_name', f'Event {i+1}')
73
+ for i, r in enumerate(results[:3])
74
+ ]
75
+
76
+ return {
77
+ "message": f"Đây là 2–3 event hợp với bạn nè:\n{formatted_events}\n\nBạn có muốn xem chi tiết event nào không?",
78
+ "new_state": {
79
+ "active_scenario": "event_recommendation",
80
+ "scenario_step": 3,
81
+ "scenario_data": scenario_data
82
+ },
83
+ "scenario_active": True,
84
+ "loading_message": "⏳ Bạn đợi tôi tìm 1 chút nhé..."
85
+ }
86
+
87
+ # ===== STEP 3: User picks event =====
88
+ elif current_step == 3:
89
+ scenario_data['event_name'] = user_input
90
+
91
+ return {
92
+ "message": "Bạn cần xem: giá – line-up – địa điểm – hay thời gian của sự kiện?",
93
+ "new_state": {
94
+ "active_scenario": "event_recommendation",
95
+ "scenario_step": 4,
96
+ "scenario_data": scenario_data
97
+ },
98
+ "scenario_active": True
99
+ }
100
+
101
+ # ===== STEP 4: Branch based on info choice =====
102
+ elif current_step == 4:
103
+ choice = self._detect_choice(user_input)
104
+ event_name = scenario_data.get('event_name', 'sự kiện này')
105
+
106
+ # Build RAG query based on choice
107
+ query_map = {
108
+ 'price': f"giá vé {event_name}",
109
+ 'lineup': f"lineup nghệ sĩ {event_name}",
110
+ 'location': f"địa điểm tổ chức {event_name}",
111
+ 'time': f"thời gian lịch diễn {event_name}"
112
+ }
113
+
114
+ query = query_map.get(choice, query_map['price'])
115
+ print(f"🔍 RAG Search: {query}")
116
+
117
+ results = self._search_rag(query)
118
+ formatted_info = self._format_rag_results(results)
119
+
120
+ # Build response message
121
+ message_map = {
122
+ 'price': f"Giá vé event {event_name} nè:\n{formatted_info}",
123
+ 'lineup': f"Lineup / nghệ sĩ của event {event_name} là:\n{formatted_info}",
124
+ 'location': f"Địa điểm tổ chức event {event_name}:\n{formatted_info}",
125
+ 'time': f"Thời gian / lịch diễn của event {event_name}:\n{formatted_info}"
126
+ }
127
+
128
+ return {
129
+ "message": message_map.get(choice, message_map['price']),
130
+ "new_state": {
131
+ "active_scenario": "event_recommendation",
132
+ "scenario_step": 9, # Skip to email step
133
+ "scenario_data": scenario_data
134
+ },
135
+ "scenario_active": True,
136
+ "loading_message": "⏳ Bạn đợi tôi tìm 1 chút nhé..."
137
+ }
138
+
139
+ # ===== STEP 9: Ask if want to save event to email =====
140
+ elif current_step == 9:
141
+ choice = self._detect_yes_no(user_input)
142
+
143
+ if choice == 'yes':
144
+ return {
145
+ "message": "Cho mình xin email để gửi bản tóm tắt event kèm link mua vé?",
146
+ "new_state": {
147
+ "active_scenario": "event_recommendation",
148
+ "scenario_step": 10,
149
+ "scenario_data": scenario_data
150
+ },
151
+ "scenario_active": True
152
+ }
153
+ else:
154
+ return {
155
+ "message": "Okie, bạn cần event theo vibe khác không nè? 😄",
156
+ "new_state": None,
157
+ "scenario_active": False,
158
+ "end_scenario": True
159
+ }
160
+
161
+ # ===== STEP 10: Collect email and send summary =====
162
+ elif current_step == 10:
163
+ email = user_input.strip()
164
+
165
+ if not self._validate_email(email):
166
+ return {
167
+ "message": "Email này có vẻ không đúng định dạng. Bạn nhập lại giúp mình nhé? (Ví dụ: name@example.com)",
168
+ "new_state": None, # Stay at same step
169
+ "scenario_active": True
170
+ }
171
+
172
+ # Save lead
173
+ scenario_data['email'] = email
174
+
175
+ try:
176
+ self.lead_storage.save_lead(
177
+ event_name=scenario_data.get('event_name', 'Unknown Event'),
178
+ email=email,
179
+ interests={
180
+ "vibe": scenario_data.get('interest_tag'),
181
+ "wants_event_summary": True
182
+ },
183
+ session_id=scenario_data.get('session_id')
184
+ )
185
+ print(f"📧 Lead saved: {email}")
186
+ except Exception as e:
187
+ print(f"⚠️ Error saving lead: {e}")
188
+
189
+ return {
190
+ "message": "Đã gửi email cho bạn nha! ✨",
191
+ "new_state": None,
192
+ "scenario_active": False,
193
+ "end_scenario": True,
194
+ "action": "send_event_summary_email"
195
+ }
196
+
197
+ # Fallback - unknown step
198
+ return {
199
+ "message": "Xin lỗi, có lỗi xảy ra. Bạn muốn bắt đầu lại không?",
200
+ "new_state": None,
201
+ "scenario_active": False,
202
+ "end_scenario": True
203
+ }
204
+
205
+ def _get_expected_type(self, step: int) -> str:
206
+ """Get expected input type for each step"""
207
+ type_map = {
208
+ 1: 'interest_tag',
209
+ 2: None, # Auto-advance after RAG
210
+ 3: 'event_name',
211
+ 4: 'choice',
212
+ 9: 'choice',
213
+ 10: 'email'
214
+ }
215
+ return type_map.get(step, 'text')
216
+
217
+ def _format_event_list(self, results: list) -> str:
218
+ """Format event search results as numbered list"""
219
+ print(f"🔍 DEBUG: RAG returned {len(results)} results")
220
+
221
+ if not results or len(results) == 0:
222
+ return "Hiện tại chưa có event phù hợp 😢\nBạn thử vibe khác nhé!"
223
+
224
+ # Debug: Print first result to see structure
225
+ if len(results) > 0:
226
+ print(f"🔍 DEBUG: First result metadata: {results[0].get('metadata', {})}")
227
+
228
+ events = []
229
+ for i, r in enumerate(results[:3], 1):
230
+ metadata = r.get('metadata', {})
231
+ name = metadata.get('event_name', f'Event {i}')
232
+ date = metadata.get('date', 'TBA')
233
+ location = metadata.get('location', '')
234
+
235
+ print(f"🔍 DEBUG: Event {i}: name={name}, date={date}, location={location}")
236
+
237
+ event_str = f"{i}. **{name}**"
238
+ if date != 'TBA':
239
+ event_str += f" ({date})"
240
+ if location:
241
+ event_str += f" - {location}"
242
+
243
+ events.append(event_str)
244
+
245
+ return "\n".join(events)
246
+
247
+ def _detect_choice(self, user_input: str) -> str:
248
+ """Detect what info user wants to see"""
249
+ input_lower = user_input.lower()
250
+
251
+ if any(k in input_lower for k in ['giá', 'price', 'vé', 'ticket', 'bao nhiêu']):
252
+ return 'price'
253
+ elif any(k in input_lower for k in ['lineup', 'line-up', 'nghệ sĩ', 'artist', 'performer']):
254
+ return 'lineup'
255
+ elif any(k in input_lower for k in ['địa điểm', 'location', 'ở đâu', 'where', 'chỗ']):
256
+ return 'location'
257
+ elif any(k in input_lower for k in ['thời gian', 'time', 'khi nào', 'when', 'lịch', 'date']):
258
+ return 'time'
259
+ else:
260
+ return 'price' # Default
261
+
262
+ def _detect_yes_no(self, user_input: str) -> str:
263
+ """Detect yes/no response"""
264
+ input_lower = user_input.lower()
265
+
266
+ if any(k in input_lower for k in ['có', 'yes', 'ok', 'được', 'ừ', 'oke']):
267
+ return 'yes'
268
+ else:
269
+ return 'no'