taspol commited on
Commit
a79ae89
·
1 Parent(s): b6dfe53

fix: dependency

Browse files
Files changed (1) hide show
  1. utils/llm_caller.py +229 -229
utils/llm_caller.py CHANGED
@@ -55,254 +55,254 @@ class LLMCaller:
55
  print(f"Error calling LLM: {e}")
56
  return f"Error: Unable to get LLM response - {str(e)}"
57
 
58
- async def query_with_rag(self, plan_request: PlanRequest, collection_name: Optional[str] = None) -> 'PlanResponse':
59
- """
60
- Perform RAG query using PlanRequest, embed query, search Qdrant, and generate complete PlanResponse via LLM
61
- """
62
- print(plan_request)
63
- try:
64
- # 1. Create query string from PlanRequest - updated for new fields
65
- destination = plan_request.destination or plan_request.destination_place or "unknown destination"
66
- duration = plan_request.duration or plan_request.trip_duration_days or 1
67
- budget = plan_request.trip_price or 0
68
-
69
- query_text = f"Trip from {plan_request.start_place} to {destination}"
70
-
71
- # Add new fields to query
72
- if plan_request.travelDates:
73
- query_text += f" on {plan_request.travelDates}"
74
- if duration:
75
- query_text += f" for {duration} days"
76
- if budget:
77
- query_text += f" with budget {budget}"
78
- if plan_request.theme:
79
- query_text += f" {plan_request.theme} themed trip"
80
- if plan_request.interests:
81
- query_text += f" interested in {', '.join(plan_request.interests)}"
82
- if plan_request.budgetTier:
83
- query_text += f" {plan_request.budgetTier} budget tier"
84
-
85
- # 2. Generate embedding for the query
86
- query_embedding = self.embedding_model.encode(query_text, normalize_embeddings=True).tolist()
87
-
88
- # 3. Search Qdrant for similar content
89
- collection = collection_name or self.collection_name
90
- top_k = plan_request.top_k or self.top_k
91
-
92
- search_results = self.qdrant.search(
93
- collection_name=collection,
94
- query_vector=query_embedding,
95
- limit=top_k,
96
- with_payload=True
97
- )
98
-
99
- # 4. Convert search results to RetrievedItem format
100
- retrieved_data = []
101
- context_text = ""
102
-
103
- print(f"Search results: {search_results['result']}")
104
- for result in search_results['result']:
105
- retrieved_item = RetrievedItem(
106
- place_id=result.get('id', 'Unknown'),
107
- place_name=result['payload'].get("place_name", "Unknown"),
108
- score=result['score'],
109
  )
110
- retrieved_data.append(retrieved_item)
111
- context_text += f"\n{result['payload'].get('text', '')}"
 
 
112
 
113
- # 5. Create detailed prompt for LLM - updated with new fields
114
- llm_prompt = f"""
115
- You are a travel planning assistant. Based on the trip request and travel context provided, generate a comprehensive trip plan in the exact JSON format specified below.
 
 
 
 
 
 
116
 
117
- Trip Request:
118
- - From: {plan_request.start_place}
119
- - To: {destination}
120
- - Travel Dates: {plan_request.travelDates or 'Flexible'}
121
- - Duration: {duration} days
122
- - Budget: {budget} ({plan_request.budgetTier or 'Not specified'} tier)
123
- - Group Size: {plan_request.groupSize} people
124
- - Interests: {', '.join(plan_request.interests) if plan_request.interests else 'Not specified'}
125
- - Theme: {plan_request.theme or 'General travel'}
126
- - Transport Preference: {plan_request.transportPref or 'Any'}
127
- - Stay Preference: {plan_request.stayPref or 'Any'}
128
- - Legacy Context: {getattr(plan_request, 'trip_context', None) or 'None'}
129
 
130
- Relevant Travel Context:
131
- {context_text}
132
-
133
- ***Don't Explain or add any additional text outside the JSON format***.
134
- Generate a response in this EXACT JSON format (no additional text before or after):
135
- {{
136
- "tripOverview": "A comprehensive 2-3 paragraph overview of the entire {duration}-day trip from {plan_request.start_place} to {destination}, highlighting the {plan_request.theme or 'travel'} theme and {', '.join(plan_request.interests) if plan_request.interests else 'general sightseeing'} interests",
137
- "trip_plan": {{
138
- "title": "Descriptive title for the {duration}-day {plan_request.theme or 'travel'} trip to {destination}",
139
- "date": "{plan_request.travelDates or 'Flexible dates'}",
140
- "timeline": [
141
- {{"t": "08:30", "detail": "Start of day activity"}},
142
- {{"t": "12:00", "detail": "Lunch break"}},
143
- {{"t": "14:00", "detail": "Afternoon activity"}},
144
- {{"t": "18:00", "detail": "Evening activity"}}
145
- ],
146
- "spots": [
147
- {{"name": "Location name", "time": "09:30 11:45", "notes": "Description and tips for this location"}},
148
- {{"name": "Another location", "time": "14:00 – 16:30", "notes": "More details and recommendations"}}
149
- ],
150
- "budget": {{
151
- "transport": estimated_transport_cost,
152
- "entrance": estimated_entrance_fees,
153
- "meals": estimated_meal_costs,
154
- "accommodation": estimated_accommodation_costs,
155
- "activities": estimated_activity_costs,
156
- "total": total_estimated_cost
157
- }},
158
- "permits": {{
159
- "needed": true_or_false,
160
- "notes": "Permit requirements and how to obtain them",
161
- "seasonal": "Seasonal considerations and restrictions"
162
- }},
163
- "safety": {{
164
- "registration": "Safety registration procedures",
165
- "checkins": "Check-in procedures and requirements",
166
- "sos": "Emergency procedures and protocols",
167
- "contacts": {{
168
- "ranger": {{"name": "Local ranger station name", "phone": "+66-XX-XXX-XXXX"}},
169
- "hospital": {{"name": "Nearest hospital name", "phone": "+66-XX-XXX-XXXX"}},
170
- "police": {{"name": "Local police station", "phone": "1155"}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  }}
172
  }}
173
  }}
174
- }}
175
- Rules:
176
- - Use the provided context to fill in details.
177
- - Ensure all fields are filled with realistic and practical information.
178
- - Use local currency for prices and consider the {plan_request.budgetTier or 'moderate'} budget tier.
179
- - Accommodate {plan_request.groupSize} people in all recommendations.
180
- - Focus on {plan_request.theme or 'general'} themed activities.
181
- - Prioritize interests: {', '.join(plan_request.interests) if plan_request.interests else 'general sightseeing'}.
182
- - Consider transport preference: {plan_request.transportPref or 'any'}.
183
- - Consider accommodation preference: {plan_request.stayPref or 'any'}.
184
- - DONT ADD ANY INLINE NOTE IN JSON
185
-
186
- ***Don't Explain or add any additional text outside the JSON format***.
187
- Ensure the JSON is valid and well-structured.
188
 
189
- Create {duration} days of detailed activities. Include realistic prices, coordinates, and practical tips. Make it specific to the destinations, theme, interests, and budget tier provided.
190
- """
191
-
192
- # 6. Call LLM to generate structured trip plan
193
- llm_response = await self.basic_query(user_prompt=llm_prompt, max_tokens=24048)
194
- print(f"LLM Response: {llm_response}")
195
-
196
- # 7. Parse LLM response as JSON
197
- try:
198
- # Clean the response and parse JSON
199
- json_str = llm_response.strip()
200
- if json_str.startswith("```json"):
201
- json_str = json_str[7:]
202
- if json_str.endswith("```"):
203
- json_str = json_str[:-3]
204
 
205
- llm_data = json.loads(json_str)
206
- print(f"LLM Data: {llm_data}")
 
207
 
208
- # Convert to PlanResponse structure
209
- trip_plan_data = llm_data.get("trip_plan", {})
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
- # Parse timeline
212
- timeline_data = trip_plan_data.get("timeline", [])
213
- timeline = [Timeline(t=item["t"], detail=item["detail"]) for item in timeline_data]
214
 
215
- # Parse spots
216
- spots_data = trip_plan_data.get("spots", [])
217
- spots = [Spot(name=item["name"], time=item["time"], notes=item["notes"]) for item in spots_data]
218
 
219
- # Parse budget
220
- budget_data = trip_plan_data.get("budget", {})
221
- budget = Budget(
222
- transport=budget_data.get("transport"),
223
- entrance=budget_data.get("entrance"),
224
- meals=budget_data.get("meals"),
225
- accommodation=budget_data.get("accommodation"),
226
- activities=budget_data.get("activities"),
227
- total=budget_data.get("total")
228
- )
229
 
230
- # Parse permits
231
- permits_data = trip_plan_data.get("permits", {})
232
- permits = Permits(
233
- needed=permits_data.get("needed", False),
234
- notes=permits_data.get("notes", ""),
235
- seasonal=permits_data.get("seasonal", "")
236
- ) if permits_data else None
237
 
238
- # Parse safety
239
- safety_data = trip_plan_data.get("safety", {})
240
- safety = None
241
- if safety_data:
242
- contacts_data = safety_data.get("contacts", {})
243
- contacts = SafetyContacts(
244
- ranger=Contact(**contacts_data["ranger"]) if contacts_data.get("ranger") else None,
245
- hospital=Contact(**contacts_data["hospital"]) if contacts_data.get("hospital") else None,
246
- police=Contact(**contacts_data["police"]) if contacts_data.get("police") else None
247
- )
248
- safety = Safety(
249
- registration=safety_data.get("registration", ""),
250
- checkins=safety_data.get("checkins", ""),
251
- sos=safety_data.get("sos", ""),
252
- contacts=contacts
253
- )
254
 
255
- trip_plan = TripPlan(
256
- title=trip_plan_data.get("title", ""),
257
- date=trip_plan_data.get("date", ""),
258
- timeline=timeline,
259
- spots=spots,
260
- budget=budget,
261
- permits=permits,
262
- safety=safety
263
- )
264
 
265
- return PlanResponse(
266
- tripOverview=llm_data.get("tripOverview", ""),
267
- query_params=plan_request,
268
- retrieved_data=retrieved_data,
269
- trip_plan=trip_plan,
270
- meta={
271
- "status": "success",
272
- "query_text": query_text,
273
- "results_count": len(retrieved_data),
274
- "theme": plan_request.theme,
275
- "interests": plan_request.interests,
276
- "budget_tier": plan_request.budgetTier,
277
- "group_size": plan_request.groupSize
278
- }
279
- )
280
-
281
- except json.JSONDecodeError as e:
282
- print(f"Error parsing LLM JSON response: {e}")
283
- print(f"LLM Response: {llm_response}")
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
- # Fallback: create basic response with LLM text
 
286
  return PlanResponse(
287
- tripOverview=llm_response,
288
  query_params=plan_request,
289
- retrieved_data=retrieved_data,
290
- trip_plan=TripPlan(
291
- overview="Generated plan (parsing error)",
292
- total_estimated_cost=budget,
293
- steps=[]
294
- ),
295
- meta={"status": "json_parse_error", "error": str(e)}
296
  )
297
-
298
- except Exception as e:
299
- print(f"Error in RAG query: {e}")
300
- return PlanResponse(
301
- tripOverview=f"Error generating trip plan: {str(e)}",
302
- query_params=plan_request,
303
- retrieved_data=[],
304
- trip_plan=TripPlan(overview="Error occurred", total_estimated_cost=0.0, steps=[]),
305
- meta={"status": "error", "error": str(e)}
306
- )
307
-
308
 
 
55
  print(f"Error calling LLM: {e}")
56
  return f"Error: Unable to get LLM response - {str(e)}"
57
 
58
+ async def query_with_rag(self, plan_request: PlanRequest, collection_name: Optional[str] = None) -> 'PlanResponse':
59
+ """
60
+ Perform RAG query using PlanRequest, embed query, search Qdrant, and generate complete PlanResponse via LLM
61
+ """
62
+ print(plan_request)
63
+ try:
64
+ # 1. Create query string from PlanRequest - updated for new fields
65
+ destination = plan_request.destination or plan_request.destination_place or "unknown destination"
66
+ duration = plan_request.duration or plan_request.trip_duration_days or 1
67
+ budget = plan_request.trip_price or 0
68
+
69
+ query_text = f"Trip from {plan_request.start_place} to {destination}"
70
+
71
+ # Add new fields to query
72
+ if plan_request.travelDates:
73
+ query_text += f" on {plan_request.travelDates}"
74
+ if duration:
75
+ query_text += f" for {duration} days"
76
+ if budget:
77
+ query_text += f" with budget {budget}"
78
+ if plan_request.theme:
79
+ query_text += f" {plan_request.theme} themed trip"
80
+ if plan_request.interests:
81
+ query_text += f" interested in {', '.join(plan_request.interests)}"
82
+ if plan_request.budgetTier:
83
+ query_text += f" {plan_request.budgetTier} budget tier"
84
+
85
+ # 2. Generate embedding for the query
86
+ query_embedding = self.embedding_model.encode(query_text, normalize_embeddings=True).tolist()
87
+
88
+ # 3. Search Qdrant for similar content
89
+ collection = collection_name or self.collection_name
90
+ top_k = plan_request.top_k or self.top_k
91
+
92
+ search_results = self.qdrant.search(
93
+ collection_name=collection,
94
+ query_vector=query_embedding,
95
+ limit=top_k,
96
+ with_payload=True
 
 
 
 
 
 
 
 
 
 
 
 
97
  )
98
+
99
+ # 4. Convert search results to RetrievedItem format
100
+ retrieved_data = []
101
+ context_text = ""
102
 
103
+ print(f"Search results: {search_results['result']}")
104
+ for result in search_results['result']:
105
+ retrieved_item = RetrievedItem(
106
+ place_id=result.get('id', 'Unknown'),
107
+ place_name=result['payload'].get("place_name", "Unknown"),
108
+ score=result['score'],
109
+ )
110
+ retrieved_data.append(retrieved_item)
111
+ context_text += f"\n{result['payload'].get('text', '')}"
112
 
113
+ # 5. Create detailed prompt for LLM - updated with new fields
114
+ llm_prompt = f"""
115
+ You are a travel planning assistant. Based on the trip request and travel context provided, generate a comprehensive trip plan in the exact JSON format specified below.
 
 
 
 
 
 
 
 
 
116
 
117
+ Trip Request:
118
+ - From: {plan_request.start_place}
119
+ - To: {destination}
120
+ - Travel Dates: {plan_request.travelDates or 'Flexible'}
121
+ - Duration: {duration} days
122
+ - Budget: {budget} ({plan_request.budgetTier or 'Not specified'} tier)
123
+ - Group Size: {plan_request.groupSize} people
124
+ - Interests: {', '.join(plan_request.interests) if plan_request.interests else 'Not specified'}
125
+ - Theme: {plan_request.theme or 'General travel'}
126
+ - Transport Preference: {plan_request.transportPref or 'Any'}
127
+ - Stay Preference: {plan_request.stayPref or 'Any'}
128
+ - Legacy Context: {getattr(plan_request, 'trip_context', None) or 'None'}
129
+
130
+ Relevant Travel Context:
131
+ {context_text}
132
+
133
+ ***Don't Explain or add any additional text outside the JSON format***.
134
+ Generate a response in this EXACT JSON format (no additional text before or after):
135
+ {{
136
+ "tripOverview": "A comprehensive 2-3 paragraph overview of the entire {duration}-day trip from {plan_request.start_place} to {destination}, highlighting the {plan_request.theme or 'travel'} theme and {', '.join(plan_request.interests) if plan_request.interests else 'general sightseeing'} interests",
137
+ "trip_plan": {{
138
+ "title": "Descriptive title for the {duration}-day {plan_request.theme or 'travel'} trip to {destination}",
139
+ "date": "{plan_request.travelDates or 'Flexible dates'}",
140
+ "timeline": [
141
+ {{"t": "08:30", "detail": "Start of day activity"}},
142
+ {{"t": "12:00", "detail": "Lunch break"}},
143
+ {{"t": "14:00", "detail": "Afternoon activity"}},
144
+ {{"t": "18:00", "detail": "Evening activity"}}
145
+ ],
146
+ "spots": [
147
+ {{"name": "Location name", "time": "09:30 – 11:45", "notes": "Description and tips for this location"}},
148
+ {{"name": "Another location", "time": "14:00 – 16:30", "notes": "More details and recommendations"}}
149
+ ],
150
+ "budget": {{
151
+ "transport": estimated_transport_cost,
152
+ "entrance": estimated_entrance_fees,
153
+ "meals": estimated_meal_costs,
154
+ "accommodation": estimated_accommodation_costs,
155
+ "activities": estimated_activity_costs,
156
+ "total": total_estimated_cost
157
+ }},
158
+ "permits": {{
159
+ "needed": true_or_false,
160
+ "notes": "Permit requirements and how to obtain them",
161
+ "seasonal": "Seasonal considerations and restrictions"
162
+ }},
163
+ "safety": {{
164
+ "registration": "Safety registration procedures",
165
+ "checkins": "Check-in procedures and requirements",
166
+ "sos": "Emergency procedures and protocols",
167
+ "contacts": {{
168
+ "ranger": {{"name": "Local ranger station name", "phone": "+66-XX-XXX-XXXX"}},
169
+ "hospital": {{"name": "Nearest hospital name", "phone": "+66-XX-XXX-XXXX"}},
170
+ "police": {{"name": "Local police station", "phone": "1155"}}
171
+ }}
172
  }}
173
  }}
174
  }}
175
+ Rules:
176
+ - Use the provided context to fill in details.
177
+ - Ensure all fields are filled with realistic and practical information.
178
+ - Use local currency for prices and consider the {plan_request.budgetTier or 'moderate'} budget tier.
179
+ - Accommodate {plan_request.groupSize} people in all recommendations.
180
+ - Focus on {plan_request.theme or 'general'} themed activities.
181
+ - Prioritize interests: {', '.join(plan_request.interests) if plan_request.interests else 'general sightseeing'}.
182
+ - Consider transport preference: {plan_request.transportPref or 'any'}.
183
+ - Consider accommodation preference: {plan_request.stayPref or 'any'}.
184
+ - DONT ADD ANY INLINE NOTE IN JSON
185
+
186
+ ***Don't Explain or add any additional text outside the JSON format***.
187
+ Ensure the JSON is valid and well-structured.
 
188
 
189
+ Create {duration} days of detailed activities. Include realistic prices, coordinates, and practical tips. Make it specific to the destinations, theme, interests, and budget tier provided.
190
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
+ # 6. Call LLM to generate structured trip plan
193
+ llm_response = await self.basic_query(user_prompt=llm_prompt, max_tokens=24048)
194
+ print(f"LLM Response: {llm_response}")
195
 
196
+ # 7. Parse LLM response as JSON
197
+ try:
198
+ # Clean the response and parse JSON
199
+ json_str = llm_response.strip()
200
+ if json_str.startswith("```json"):
201
+ json_str = json_str[7:]
202
+ if json_str.endswith("```"):
203
+ json_str = json_str[:-3]
204
+
205
+ llm_data = json.loads(json_str)
206
+ print(f"LLM Data: {llm_data}")
207
+
208
+ # Convert to PlanResponse structure
209
+ trip_plan_data = llm_data.get("trip_plan", {})
210
 
211
+ # Parse timeline
212
+ timeline_data = trip_plan_data.get("timeline", [])
213
+ timeline = [Timeline(t=item["t"], detail=item["detail"]) for item in timeline_data]
214
 
215
+ # Parse spots
216
+ spots_data = trip_plan_data.get("spots", [])
217
+ spots = [Spot(name=item["name"], time=item["time"], notes=item["notes"]) for item in spots_data]
218
 
219
+ # Parse budget
220
+ budget_data = trip_plan_data.get("budget", {})
221
+ budget = Budget(
222
+ transport=budget_data.get("transport"),
223
+ entrance=budget_data.get("entrance"),
224
+ meals=budget_data.get("meals"),
225
+ accommodation=budget_data.get("accommodation"),
226
+ activities=budget_data.get("activities"),
227
+ total=budget_data.get("total")
228
+ )
229
 
230
+ # Parse permits
231
+ permits_data = trip_plan_data.get("permits", {})
232
+ permits = Permits(
233
+ needed=permits_data.get("needed", False),
234
+ notes=permits_data.get("notes", ""),
235
+ seasonal=permits_data.get("seasonal", "")
236
+ ) if permits_data else None
237
 
238
+ # Parse safety
239
+ safety_data = trip_plan_data.get("safety", {})
240
+ safety = None
241
+ if safety_data:
242
+ contacts_data = safety_data.get("contacts", {})
243
+ contacts = SafetyContacts(
244
+ ranger=Contact(**contacts_data["ranger"]) if contacts_data.get("ranger") else None,
245
+ hospital=Contact(**contacts_data["hospital"]) if contacts_data.get("hospital") else None,
246
+ police=Contact(**contacts_data["police"]) if contacts_data.get("police") else None
247
+ )
248
+ safety = Safety(
249
+ registration=safety_data.get("registration", ""),
250
+ checkins=safety_data.get("checkins", ""),
251
+ sos=safety_data.get("sos", ""),
252
+ contacts=contacts
253
+ )
254
 
255
+ trip_plan = TripPlan(
256
+ title=trip_plan_data.get("title", ""),
257
+ date=trip_plan_data.get("date", ""),
258
+ timeline=timeline,
259
+ spots=spots,
260
+ budget=budget,
261
+ permits=permits,
262
+ safety=safety
263
+ )
264
 
265
+ return PlanResponse(
266
+ tripOverview=llm_data.get("tripOverview", ""),
267
+ query_params=plan_request,
268
+ retrieved_data=retrieved_data,
269
+ trip_plan=trip_plan,
270
+ meta={
271
+ "status": "success",
272
+ "query_text": query_text,
273
+ "results_count": len(retrieved_data),
274
+ "theme": plan_request.theme,
275
+ "interests": plan_request.interests,
276
+ "budget_tier": plan_request.budgetTier,
277
+ "group_size": plan_request.groupSize
278
+ }
279
+ )
280
+
281
+ except json.JSONDecodeError as e:
282
+ print(f"Error parsing LLM JSON response: {e}")
283
+ print(f"LLM Response: {llm_response}")
284
+
285
+ # Fallback: create basic response with LLM text
286
+ return PlanResponse(
287
+ tripOverview=llm_response,
288
+ query_params=plan_request,
289
+ retrieved_data=retrieved_data,
290
+ trip_plan=TripPlan(
291
+ overview="Generated plan (parsing error)",
292
+ total_estimated_cost=budget,
293
+ steps=[]
294
+ ),
295
+ meta={"status": "json_parse_error", "error": str(e)}
296
+ )
297
 
298
+ except Exception as e:
299
+ print(f"Error in RAG query: {e}")
300
  return PlanResponse(
301
+ tripOverview=f"Error generating trip plan: {str(e)}",
302
  query_params=plan_request,
303
+ retrieved_data=[],
304
+ trip_plan=TripPlan(overview="Error occurred", total_estimated_cost=0.0, steps=[]),
305
+ meta={"status": "error", "error": str(e)}
 
 
 
 
306
  )
307
+
 
 
 
 
 
 
 
 
 
 
308