ABAO77 commited on
Commit
ef0145e
·
verified ·
1 Parent(s): 68e3556

Upload 164 files

Browse files
src/apis/controllers/__pycache__/chat_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/chat_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/chat_controller.cpython-311.pyc differ
 
src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc differ
 
src/apis/controllers/__pycache__/planner_controller.cpython-311.pyc CHANGED
Binary files a/src/apis/controllers/__pycache__/planner_controller.cpython-311.pyc and b/src/apis/controllers/__pycache__/planner_controller.cpython-311.pyc differ
 
src/apis/controllers/chat_controller.py CHANGED
@@ -68,85 +68,114 @@ async def save_history(user_id, human_message, ai_message, intent):
68
 
69
 
70
  async def chat_streaming_function(user, data: Chat, background_tasks: BackgroundTasks):
71
- human_message = data.message
72
- history = data.history
73
- lat = data.lat
74
- long = data.long
75
- language = data.language
76
- logger.info(f"Language: {language}")
77
- process_history = post_process_history(history) if history is not None else None
78
- config = {
79
- "configurable": {
80
- "user_id": user["id"],
81
- "user_email": user["email"],
82
- "contact_number": user["contact_number"],
83
- "session_id": user["id"],
84
- "lat": lat,
85
- "long": long,
 
 
 
 
 
 
 
 
 
86
  }
87
- }
88
- # try:
89
- initial_input = {
90
- "messages": [("user", human_message)],
91
- "messages_history": process_history,
92
- "entry_message": None,
93
- "manual_save": False,
94
- "intent": data.intent,
95
- "language": language,
96
- "tool_name": None,
97
- "ever_leave_skill": False,
98
- }
99
- last_output_state = None
100
- temp = ""
101
- async for event in workflow.astream(
102
- input=initial_input,
103
- config=config,
104
- stream_mode=["messages", "values"],
105
- ):
106
- event_type, event_message = event
107
- if event_type == "messages":
108
- message, metadata = event_message
109
- if (
110
- isinstance(message, AIMessageChunk)
111
- and message.tool_calls
112
- and message.tool_call_chunks[0]["name"] != "ClassifyUserIntent"
113
  ):
114
- tool_name = message.tool_call_chunks[0]["name"]
115
- message_yield = json.dumps(
116
- {"type": "tool_call", "content": tool_name}, ensure_ascii=False
117
- )
118
- print(message_yield)
119
- yield message_yield + "\n"
120
- if metadata["langgraph_node"] in [
121
- "primary_assistant",
122
- "scheduling_agent",
123
- "book_hotel_agent",
124
- ]:
125
-
126
- if message.content:
127
- temp += message.content
128
- message_yield = json.dumps(
129
- {"type": "message", "content": temp}, ensure_ascii=False
130
- )
131
- print(message_yield)
132
- yield message_yield + "\n"
133
- if event_type == "values":
134
- last_output_state = event_message
135
-
136
- final_ai_output = last_output_state["messages"][-1].content
137
- final_intent = last_output_state["intent"]
138
- tool_name_important = last_output_state["tool_name"]
139
-
140
- final_response = json.dumps(
141
- {
142
- "type": "final",
143
- "content": final_ai_output,
144
- "intent": final_intent,
145
- "tool_name": tool_name_important,
146
- },
147
- ensure_ascii=False,
148
- )
149
- yield final_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  background_tasks.add_task(
152
  save_history, user["id"], human_message, final_ai_output, final_intent
@@ -156,84 +185,114 @@ async def chat_streaming_function(user, data: Chat, background_tasks: Background
156
  async def chat_streaming_no_login_function(
157
  data: Chat, background_tasks: BackgroundTasks
158
  ):
159
- human_message = data.message
160
- history = data.history
161
- lat = data.lat
162
- long = data.long
163
- language = data.language
164
- logger.info(f"Language: {language}")
165
- process_history = post_process_history(history) if history is not None else None
166
- config = {
167
- "configurable": {
168
- "user_id": None,
169
- "user_email": None,
170
- "contact_number": None,
171
- "session_id": None,
172
- "lat": lat,
173
- "long": long,
 
 
 
 
 
 
 
 
 
174
  }
175
- }
176
- initial_input = {
177
- "messages": [("user", human_message)],
178
- "messages_history": process_history,
179
- "entry_message": None,
180
- "manual_save": False,
181
- "intent": data.intent,
182
- "language": language,
183
- "tool_name": None,
184
- "ever_leave_skill": False,
185
- }
186
- last_output_state = None
187
- temp = ""
188
- async for event in workflow.astream(
189
- input=initial_input,
190
- config=config,
191
- stream_mode=["messages", "values"],
192
- ):
193
- event_type, event_message = event
194
- if event_type == "messages":
195
- message, metadata = event_message
196
- if (
197
- isinstance(message, AIMessageChunk)
198
- and message.tool_calls
199
- and message.tool_call_chunks[0]["name"] != "ClassifyUserIntent"
200
  ):
201
- tool_name = message.tool_call_chunks[0]["name"]
202
- message_yield = json.dumps(
203
- {"type": "tool_call", "content": tool_name}, ensure_ascii=False
204
- )
205
- print(message_yield)
206
- yield message_yield + "\n"
207
- if metadata["langgraph_node"] in [
208
- "primary_assistant",
209
- "scheduling_agent",
210
- "book_hotel_agent",
211
- ]:
212
-
213
- if message.content:
214
- temp += message.content
215
- message_yield = json.dumps(
216
- {"type": "message", "content": temp}, ensure_ascii=False
217
- )
218
- print(message_yield)
219
- yield message_yield + "\n"
220
- if event_type == "values":
221
- last_output_state = event_message
222
-
223
- final_ai_output = last_output_state["messages"][-1].content
224
- final_intent = last_output_state["intent"]
225
- tool_name_important = last_output_state["tool_name"]
226
-
227
- final_response = json.dumps(
228
- {
229
- "type": "final",
230
- "content": final_ai_output,
231
- "intent": final_intent,
232
- "tool_name": tool_name_important,
233
- },
234
- ensure_ascii=False,
235
- )
236
- yield final_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
 
239
  async def chat_function(user, data: Chat, background_tasks: BackgroundTasks):
 
68
 
69
 
70
  async def chat_streaming_function(user, data: Chat, background_tasks: BackgroundTasks):
71
+ try:
72
+ human_message = data.message
73
+ history = data.history
74
+ lat = data.lat
75
+ long = data.long
76
+ language = data.language
77
+ logger.info(f"Language: {language}")
78
+
79
+ try:
80
+ process_history = post_process_history(history) if history is not None else None
81
+ except Exception as e:
82
+ logger.error(f"Error processing chat history: {str(e)}")
83
+ yield json.dumps({"type": "error", "content": "Error processing chat history" + str(e)}, ensure_ascii=False) + "\n"
84
+ return
85
+
86
+ config = {
87
+ "configurable": {
88
+ "user_id": user["id"],
89
+ "user_email": user["email"],
90
+ "contact_number": user["contact_number"],
91
+ "session_id": user["id"],
92
+ "lat": lat,
93
+ "long": long,
94
+ }
95
  }
96
+
97
+ initial_input = {
98
+ "messages": [("user", human_message)],
99
+ "messages_history": process_history,
100
+ "entry_message": None,
101
+ "manual_save": False,
102
+ "intent": data.intent,
103
+ "language": language,
104
+ "tool_name": None,
105
+ "ever_leave_skill": False,
106
+ }
107
+
108
+ last_output_state = None
109
+ temp = ""
110
+
111
+ try:
112
+ async for event in workflow.astream(
113
+ input=initial_input,
114
+ config=config,
115
+ stream_mode=["messages", "values"],
 
 
 
 
 
 
116
  ):
117
+ try:
118
+ event_type, event_message = event
119
+ if event_type == "messages":
120
+ message, metadata = event_message
121
+ if (
122
+ isinstance(message, AIMessageChunk)
123
+ and message.tool_calls
124
+ and message.tool_call_chunks[0]["name"] != "ClassifyUserIntent"
125
+ ):
126
+ tool_name = message.tool_call_chunks[0]["name"]
127
+ message_yield = json.dumps(
128
+ {"type": "tool_call", "content": tool_name}, ensure_ascii=False
129
+ )
130
+ print(message_yield)
131
+ yield message_yield + "\n"
132
+
133
+ if metadata["langgraph_node"] in [
134
+ "primary_assistant",
135
+ "scheduling_agent",
136
+ "book_hotel_agent",
137
+ ]:
138
+ if message.content:
139
+ temp += message.content
140
+ message_yield = json.dumps(
141
+ {"type": "message", "content": temp}, ensure_ascii=False
142
+ )
143
+ print(message_yield)
144
+ yield message_yield + "\n"
145
+ if event_type == "values":
146
+ last_output_state = event_message
147
+ except Exception as e:
148
+ logger.error(f"Error processing stream event: {str(e)}")
149
+ yield json.dumps({"type": "error", "content": "Error processing response" + str(e)}, ensure_ascii=False) + "\n"
150
+ return
151
+
152
+ if last_output_state is None:
153
+ raise ValueError("No output state received from workflow")
154
+
155
+ final_ai_output = last_output_state["messages"][-1].content
156
+ final_intent = last_output_state["intent"]
157
+ tool_name_important = last_output_state["tool_name"]
158
+
159
+ final_response = json.dumps(
160
+ {
161
+ "type": "final",
162
+ "content": final_ai_output,
163
+ "intent": final_intent,
164
+ "tool_name": tool_name_important,
165
+ },
166
+ ensure_ascii=False,
167
+ )
168
+ yield final_response
169
+
170
+ except Exception as e:
171
+ logger.error(f"Error in workflow stream processing: {str(e)}")
172
+ yield json.dumps({"type": "error", "content": "Error processing chat stream" + str(e)}, ensure_ascii=False) + "\n"
173
+ return
174
+
175
+ except Exception as e:
176
+ logger.error(f"Unexpected error in chat streaming: {str(e)}")
177
+ yield json.dumps({"type": "error", "content": "An unexpected error occurred" + str(e)}, ensure_ascii=False) + "\n"
178
+ return
179
 
180
  background_tasks.add_task(
181
  save_history, user["id"], human_message, final_ai_output, final_intent
 
185
  async def chat_streaming_no_login_function(
186
  data: Chat, background_tasks: BackgroundTasks
187
  ):
188
+ try:
189
+ human_message = data.message
190
+ history = data.history
191
+ lat = data.lat
192
+ long = data.long
193
+ language = data.language
194
+ logger.info(f"Language: {language}")
195
+
196
+ try:
197
+ process_history = post_process_history(history) if history is not None else None
198
+ except Exception as e:
199
+ logger.error(f"Error processing chat history: {str(e)}")
200
+ yield json.dumps({"type": "error", "content": "Error processing chat history"}, ensure_ascii=False) + "\n"
201
+ return
202
+
203
+ config = {
204
+ "configurable": {
205
+ "user_id": None,
206
+ "user_email": None,
207
+ "contact_number": None,
208
+ "session_id": None,
209
+ "lat": lat,
210
+ "long": long,
211
+ }
212
  }
213
+
214
+ initial_input = {
215
+ "messages": [("user", human_message)],
216
+ "messages_history": process_history,
217
+ "entry_message": None,
218
+ "manual_save": False,
219
+ "intent": data.intent,
220
+ "language": language,
221
+ "tool_name": None,
222
+ "ever_leave_skill": False,
223
+ }
224
+
225
+ last_output_state = None
226
+ temp = ""
227
+
228
+ try:
229
+ async for event in workflow.astream(
230
+ input=initial_input,
231
+ config=config,
232
+ stream_mode=["messages", "values"],
 
 
 
 
 
233
  ):
234
+ try:
235
+ event_type, event_message = event
236
+ if event_type == "messages":
237
+ message, metadata = event_message
238
+ if (
239
+ isinstance(message, AIMessageChunk)
240
+ and message.tool_calls
241
+ and message.tool_call_chunks[0]["name"] != "ClassifyUserIntent"
242
+ ):
243
+ tool_name = message.tool_call_chunks[0]["name"]
244
+ message_yield = json.dumps(
245
+ {"type": "tool_call", "content": tool_name}, ensure_ascii=False
246
+ )
247
+ print(message_yield)
248
+ yield message_yield + "\n"
249
+
250
+ if metadata["langgraph_node"] in [
251
+ "primary_assistant",
252
+ "scheduling_agent",
253
+ "book_hotel_agent",
254
+ ]:
255
+ if message.content:
256
+ temp += message.content
257
+ message_yield = json.dumps(
258
+ {"type": "message", "content": temp}, ensure_ascii=False
259
+ )
260
+ print(message_yield)
261
+ yield message_yield + "\n"
262
+ if event_type == "values":
263
+ last_output_state = event_message
264
+ except Exception as e:
265
+ logger.error(f"Error processing stream event: {str(e)}")
266
+ yield json.dumps({"type": "error", "content": "Error processing response" + str(e)}, ensure_ascii=False) + "\n"
267
+ return
268
+
269
+ if last_output_state is None:
270
+ raise ValueError("No output state received from workflow")
271
+
272
+ final_ai_output = last_output_state["messages"][-1].content
273
+ final_intent = last_output_state["intent"]
274
+ tool_name_important = last_output_state["tool_name"]
275
+
276
+ final_response = json.dumps(
277
+ {
278
+ "type": "final",
279
+ "content": final_ai_output,
280
+ "intent": final_intent,
281
+ "tool_name": tool_name_important,
282
+ },
283
+ ensure_ascii=False,
284
+ )
285
+ yield final_response
286
+
287
+ except Exception as e:
288
+ logger.error(f"Error in workflow stream processing: {str(e)}")
289
+ yield json.dumps({"type": "error", "content": "Error processing chat stream" + str(e)}, ensure_ascii=False) + "\n"
290
+ return
291
+
292
+ except Exception as e:
293
+ logger.error(f"Unexpected error in chat streaming: {str(e)}")
294
+ yield json.dumps({"type": "error", "content": "An unexpected error occurred" + str(e)}, ensure_ascii=False) + "\n"
295
+ return
296
 
297
 
298
  async def chat_function(user, data: Chat, background_tasks: BackgroundTasks):
src/apis/controllers/destination_controller.py CHANGED
@@ -14,18 +14,19 @@ from src.apis.controllers.location_controller import (
14
  get_places,
15
  )
16
  from src.langgraph.config.constant import available_categories
 
17
 
18
 
19
  async def destination_suggestion_controller(
20
- question: str, top_k: int = 5
21
  ) -> List[Dict[str, Any]]:
22
-
 
 
 
23
  async with aiohttp.ClientSession() as session:
24
- # Get question tags
25
  try:
26
- async with session.get(
27
- f"https://abao77-destination-suggestion.hf.space/model/get_destinations_list_by_question/{question}/{top_k}"
28
- ) as response:
29
  if response.status == 200:
30
  data = await response.json()
31
  logger.info(f"Destination suggestion for question: {data}")
@@ -40,11 +41,22 @@ async def destination_suggestion_controller(
40
  raise HTTPException(status_code=500, detail=f"Request failed: {str(e)}")
41
 
42
 
43
- async def destination_recommendation_func(query, top_k=5, tool_chat=False):
44
- routing: RoutingRecommender = routing_recommender_chain.invoke({"query": query})
45
- print("label", routing.label)
46
- if routing.label == "characteristic":
47
- output = await destination_suggestion_controller(query, top_k)
 
 
 
 
 
 
 
 
 
 
 
48
  if tool_chat:
49
  return output
50
  output = [
 
14
  get_places,
15
  )
16
  from src.langgraph.config.constant import available_categories
17
+ from src.utils.logger import logger
18
 
19
 
20
  async def destination_suggestion_controller(
21
+ question: str, user_id: str, top_k: int = 5
22
  ) -> List[Dict[str, Any]]:
23
+ url = f"https://abao77-destination-suggestion.hf.space/model/get_destinations_list_by_question/{question}/{top_k}"
24
+ if user_id:
25
+ url += f"/{user_id}"
26
+ logger.info("Call recommend with user_id")
27
  async with aiohttp.ClientSession() as session:
 
28
  try:
29
+ async with session.get(url) as response:
 
 
30
  if response.status == 200:
31
  data = await response.json()
32
  logger.info(f"Destination suggestion for question: {data}")
 
41
  raise HTTPException(status_code=500, detail=f"Request failed: {str(e)}")
42
 
43
 
44
+ async def destination_recommendation_func(
45
+ query, user_id: str, top_k=5, tool_chat=False
46
+ ):
47
+ routing: RoutingRecommender = await routing_recommender_chain.ainvoke(
48
+ {"query": query}
49
+ )
50
+ print("routing", routing)
51
+ if routing.label == "invalid":
52
+ if tool_chat:
53
+ return "the input message is not related to travel or destination"
54
+ raise HTTPException(
55
+ status_code=400,
56
+ detail="The input is not related to travel or destination recommendations. Please provide a travel-related query.",
57
+ )
58
+ elif routing.label == "characteristic":
59
+ output = await destination_suggestion_controller(query, user_id, top_k)
60
  if tool_chat:
61
  return output
62
  output = [
src/apis/controllers/planner_controller.py CHANGED
@@ -2,39 +2,68 @@ import json
2
  from fastapi import BackgroundTasks
3
  from src.langgraph.multi_agent.planner.planner_flow import planner_app
4
  from src.utils.helper import parse_itinerary
 
5
 
6
 
7
  async def message_generator(input_graph, config, background: BackgroundTasks):
 
 
 
8
 
9
- last_output_state = None
10
- temp = ""
11
- async for event in planner_app.astream(
12
- input=input_graph,
13
- config=config,
14
- stream_mode=["messages", "values"],
15
- ):
16
- event_type, event_message = event
17
- if event_type == "messages":
18
- message, _ = event_message
19
- if message.content:
20
- temp += message.content
21
- message_yield = json.dumps(
22
- {"type": "message", "content": temp},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  ensure_ascii=False,
24
  )
25
- yield message_yield + "\n\n"
26
- if event_type == "values":
27
- last_output_state = event_message
28
- if last_output_state and "final_answer" in last_output_state:
29
- parser_ouput = parse_itinerary(last_output_state["final_answer"])
30
- final_response = json.dumps(
31
- {
32
- "type": "final",
33
- "content": parser_ouput,
34
- },
35
- ensure_ascii=False,
36
- )
37
- yield final_response + "\n\n"
 
 
38
 
39
 
40
  from pydantic import BaseModel, Field
 
2
  from fastapi import BackgroundTasks
3
  from src.langgraph.multi_agent.planner.planner_flow import planner_app
4
  from src.utils.helper import parse_itinerary
5
+ from src.utils.logger import logger
6
 
7
 
8
  async def message_generator(input_graph, config, background: BackgroundTasks):
9
+ try:
10
+ last_output_state = None
11
+ temp = ""
12
 
13
+ try:
14
+ async for event in planner_app.astream(
15
+ input=input_graph,
16
+ config=config,
17
+ stream_mode=["messages", "values"],
18
+ ):
19
+ try:
20
+ event_type, event_message = event
21
+ if event_type == "messages":
22
+ message, _ = event_message
23
+ if message.content:
24
+ temp += message.content
25
+ message_yield = json.dumps(
26
+ {"type": "message", "content": temp},
27
+ ensure_ascii=False,
28
+ )
29
+ yield message_yield + "\n\n"
30
+ if event_type == "values":
31
+ last_output_state = event_message
32
+ except Exception as e:
33
+ logger.error(f"Error processing stream event: {str(e)}")
34
+ yield json.dumps({"type": "error", "content": "Error processing planner response " + str(e)}, ensure_ascii=False) + "\n\n"
35
+ return
36
+
37
+ if last_output_state is None:
38
+ raise ValueError("No output state received from planner workflow")
39
+
40
+ if "final_answer" not in last_output_state:
41
+ raise ValueError("No final answer in planner output")
42
+
43
+ try:
44
+ parser_ouput = parse_itinerary(last_output_state["final_answer"])
45
+ final_response = json.dumps(
46
+ {
47
+ "type": "final",
48
+ "content": parser_ouput,
49
+ },
50
  ensure_ascii=False,
51
  )
52
+ yield final_response + "\n\n"
53
+ except Exception as e:
54
+ logger.error(f"Error parsing itinerary: {str(e)}")
55
+ yield json.dumps({"type": "error", "content": "Error parsing the generated itinerary" + str(e)}, ensure_ascii=False) + "\n\n"
56
+ return
57
+
58
+ except Exception as e:
59
+ logger.error(f"Error in planner workflow stream: {str(e)}")
60
+ yield json.dumps({"type": "error", "content": "Error processing planner stream" + str(e)}, ensure_ascii=False) + "\n\n"
61
+ return
62
+
63
+ except Exception as e:
64
+ logger.error(f"Unexpected error in planner: {str(e)}")
65
+ yield json.dumps({"type": "error", "content": "An unexpected error occurred in the planner" + str(e)}, ensure_ascii=False) + "\n\n"
66
+ return
67
 
68
 
69
  from pydantic import BaseModel, Field
src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc CHANGED
Binary files a/src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc and b/src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc differ
 
src/apis/routes/travel_dest_route.py CHANGED
@@ -1,260 +1,267 @@
1
- from fastapi import APIRouter, Depends, HTTPException, Query
2
- from typing import Annotated, List
3
- from fastapi.responses import JSONResponse
4
- import pandas as pd
5
- import math
6
- from src.apis.models.user_models import User
7
- from src.apis.models.destination_models import Destination
8
- from src.apis.middlewares.auth_middleware import get_current_user
9
- from src.utils.logger import logger
10
- from src.apis.controllers.destination_controller import (
11
- destination_recommendation_func,
12
- )
13
- from fastapi.responses import JSONResponse
14
- from src.utils.mongo import DestinationCRUD
15
- from datetime import datetime
16
- from bson import ObjectId
17
-
18
- router = APIRouter(prefix="/dest", tags=["Destination"])
19
-
20
- user_dependency = Annotated[User, Depends(get_current_user)]
21
- EXCEL_FILE_PATH = "./src/data/destination_1_new_tag.xlsx"
22
-
23
-
24
- @router.get("/get_tourist")
25
- def get_tourist(page: int = Query(default=1, ge=1)):
26
- try:
27
- PAGE_SIZE = 10
28
- df = pd.read_excel(EXCEL_FILE_PATH)
29
- required_columns = ["name", "description", "image"]
30
- if not all(col in df.columns for col in required_columns):
31
- raise HTTPException(
32
- status_code=400, detail="Missing required columns in Excel file"
33
- )
34
- total_items = len(df)
35
- total_pages = math.ceil(total_items / PAGE_SIZE)
36
- start_idx = (page - 1) * PAGE_SIZE
37
- end_idx = start_idx + PAGE_SIZE
38
- paginated_df = df[required_columns].iloc[start_idx:end_idx]
39
- tourist_data = paginated_df.to_dict(orient="records")
40
- return JSONResponse(
41
- content={
42
- "data": tourist_data,
43
- "page": page,
44
- "total_pages": total_pages,
45
- "total_items": total_items,
46
- "page_size": PAGE_SIZE,
47
- }
48
- )
49
- except Exception as e:
50
- logger.error(f"Error reading the Excel file: {str(e)}")
51
- raise HTTPException(
52
- status_code=500, detail=f"Error reading the Excel file: {str(e)}"
53
- )
54
-
55
-
56
- @router.get("/destination_detail")
57
- async def get_destinations(destination_id: str = Query(min_length=1, max_length=50)):
58
- try:
59
- destination_data = await DestinationCRUD.find_by_id(destination_id)
60
- if not destination_data:
61
- return JSONResponse(
62
- content={"message": "Destination not found"}, status_code=404
63
- )
64
- destination_data.pop("created_at")
65
- destination_data.pop("updated_at")
66
- destination_data.pop("expire_at")
67
- destination_data["id"] = str(destination_data["_id"])
68
- destination_data.pop("_id")
69
- print("destination_data", destination_data)
70
- return JSONResponse(content=destination_data, status_code=200)
71
-
72
- except Exception as e:
73
- logger.error(f"Error fetching destination: {str(e)}")
74
-
75
-
76
- @router.get("/paginate")
77
- async def get_paginated_destinations(
78
- page: int = Query(default=1, ge=1), page_size: int = Query(default=10, ge=1, le=100)
79
- ):
80
- """
81
- Get paginated destinations from database
82
-
83
- Args:
84
- page: Page number (starts from 1)
85
- page_size: Number of items per page
86
-
87
- Returns:
88
- Paginated list of destinations with pagination metadata
89
- """
90
- try:
91
- # Calculate skip value for pagination
92
- skip = (page - 1) * page_size
93
-
94
- # Query destinations with pagination
95
- destination_data = await DestinationCRUD.find_many(
96
- filter={}, # Empty filter means get all
97
- skip=skip,
98
- limit=page_size,
99
- sort=[("created_at", -1)], # Sort by created_at descending
100
- )
101
-
102
- # Get total count for pagination metadata
103
- total_items = await DestinationCRUD.count({})
104
- total_pages = math.ceil(total_items / page_size)
105
-
106
- # Process results
107
- serialized_data = []
108
- for dest in destination_data:
109
- dest.pop("created_at")
110
- dest.pop("updated_at")
111
- dest.pop("expire_at")
112
-
113
- dest_dict = {
114
- "id": dest["_id"],
115
- **{k: v for k, v in dest.items() if k != "_id"},
116
- }
117
- serialized_data.append(dest_dict)
118
-
119
- return JSONResponse(
120
- content={
121
- "data": serialized_data,
122
- "page": page,
123
- "total_pages": total_pages,
124
- "total_items": total_items,
125
- "page_size": page_size,
126
- },
127
- status_code=200,
128
- )
129
- except Exception as e:
130
- logger.error(f"Error fetching paginated destinations: {str(e)}")
131
- raise HTTPException(
132
- status_code=500, detail=f"Error fetching paginated destinations: {str(e)}"
133
- )
134
-
135
-
136
- @router.get("/get_tourist_names")
137
- async def get_tourist_names():
138
- try:
139
- destination_data = await DestinationCRUD.find_all()
140
- serialized_data = []
141
- for dest in destination_data:
142
- dest.pop("created_at")
143
- dest.pop("updated_at")
144
- dest.pop("expire_at")
145
- dest["id"] = str(dest["_id"])
146
- dest.pop("_id")
147
- serialized_data.append(dest)
148
- return JSONResponse(content={"data": serialized_data}, status_code=200)
149
- except Exception as e:
150
- logger.error(f"Error getting tourist names: {str(e)}")
151
- raise HTTPException(
152
- status_code=500, detail=f"Error getting tourist names: {str(e)}"
153
- )
154
-
155
-
156
- @router.get("/suggest")
157
- async def destination_suggestion(question: str, top_k: int = Query(default=5, ge=1)):
158
- return JSONResponse(await destination_recommendation_func(question))
159
-
160
-
161
- @router.post("/")
162
- async def create_destination(
163
- destination_data: Destination, current_user: user_dependency
164
- ):
165
- if current_user["role"] != "admin":
166
- raise HTTPException(status_code=403, detail="Unauthorized")
167
- try:
168
- destination_id = await DestinationCRUD.create(destination_data.model_dump())
169
-
170
- if not destination_id:
171
- raise HTTPException(status_code=400, detail="Failed to create destination")
172
-
173
- return JSONResponse(
174
- content={
175
- "message": "Destination created successfully",
176
- "id": destination_id,
177
- },
178
- status_code=201,
179
- )
180
- except Exception as e:
181
- logger.error(f"Error creating destination: {str(e)}")
182
- raise HTTPException(
183
- status_code=500, detail=f"Error creating destination: {str(e)}"
184
- )
185
-
186
-
187
- @router.put("/{destination_id}")
188
- async def update_destination(
189
- destination_id: str, destination_data: dict, current_user: user_dependency
190
- ):
191
- if current_user["role"] != "admin":
192
- raise HTTPException(status_code=403, detail="Unauthorized")
193
- try:
194
- # Add updated_at timestamp
195
- destination_data["updated_at"] = datetime.utcnow()
196
-
197
- # Update destination
198
- result = await DestinationCRUD.update(
199
- {"_id": ObjectId(destination_id)}, destination_data
200
- )
201
-
202
- if not result:
203
- raise HTTPException(status_code=404, detail="Destination not found")
204
-
205
- return JSONResponse(
206
- content={"message": "Destination updated successfully"}, status_code=200
207
- )
208
- except Exception as e:
209
- logger.error(f"Error updating destination: {str(e)}")
210
- raise HTTPException(
211
- status_code=500, detail=f"Error updating destination: {str(e)}"
212
- )
213
-
214
-
215
- @router.delete("/{destination_id}")
216
- async def delete_destination(destination_id: str, current_user: user_dependency):
217
- if current_user["role"] != "admin":
218
- raise HTTPException(status_code=403, detail="Unauthorized")
219
- try:
220
- # Delete destination
221
- result = await DestinationCRUD.delete_one({"_id": ObjectId(destination_id)})
222
-
223
- if not result:
224
- raise HTTPException(status_code=404, detail="Destination not found")
225
-
226
- return JSONResponse(
227
- content={"message": "Destination deleted successfully"}, status_code=200
228
- )
229
- except Exception as e:
230
- logger.error(f"Error deleting destination: {str(e)}")
231
- raise HTTPException(
232
- status_code=500, detail=f"Error deleting destination: {str(e)}"
233
- )
234
-
235
-
236
- @router.get("/")
237
- async def get_all_destinations(current_user: user_dependency):
238
- print("current_user", current_user)
239
- if current_user["role"] != "admin":
240
- raise HTTPException(status_code=403, detail="Unauthorized")
241
- try:
242
- # Get all destinations
243
- destinations = await DestinationCRUD.find_all()
244
-
245
- # Process results
246
- serialized_data = []
247
- for dest in destinations:
248
- dest.pop("created_at")
249
- dest.pop("updated_at")
250
- dest.pop("expire_at")
251
- dest["id"] = str(dest["_id"])
252
- dest.pop("_id")
253
- serialized_data.append(dest)
254
-
255
- return JSONResponse(content={"data": serialized_data}, status_code=200)
256
- except Exception as e:
257
- logger.error(f"Error fetching destinations: {str(e)}")
258
- raise HTTPException(
259
- status_code=500, detail=f"Error fetching destinations: {str(e)}"
260
- )
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Depends, HTTPException, Query
2
+ from typing import Annotated, List
3
+ from fastapi.responses import JSONResponse
4
+ import pandas as pd
5
+ import math
6
+ from src.apis.models.user_models import User
7
+ from src.apis.models.destination_models import Destination
8
+ from src.apis.middlewares.auth_middleware import get_current_user
9
+ from src.utils.logger import logger
10
+ from src.apis.controllers.destination_controller import (
11
+ destination_recommendation_func,
12
+ )
13
+ from fastapi.responses import JSONResponse
14
+ from src.utils.mongo import DestinationCRUD
15
+ from datetime import datetime
16
+ from bson import ObjectId
17
+
18
+ router = APIRouter(prefix="/dest", tags=["Destination"])
19
+
20
+ user_dependency = Annotated[User, Depends(get_current_user)]
21
+ EXCEL_FILE_PATH = "./src/data/destination_1_new_tag.xlsx"
22
+
23
+
24
+ @router.get("/get_tourist")
25
+ def get_tourist(page: int = Query(default=1, ge=1)):
26
+ try:
27
+ PAGE_SIZE = 10
28
+ df = pd.read_excel(EXCEL_FILE_PATH)
29
+ required_columns = ["name", "description", "image"]
30
+ if not all(col in df.columns for col in required_columns):
31
+ raise HTTPException(
32
+ status_code=400, detail="Missing required columns in Excel file"
33
+ )
34
+ total_items = len(df)
35
+ total_pages = math.ceil(total_items / PAGE_SIZE)
36
+ start_idx = (page - 1) * PAGE_SIZE
37
+ end_idx = start_idx + PAGE_SIZE
38
+ paginated_df = df[required_columns].iloc[start_idx:end_idx]
39
+ tourist_data = paginated_df.to_dict(orient="records")
40
+ return JSONResponse(
41
+ content={
42
+ "data": tourist_data,
43
+ "page": page,
44
+ "total_pages": total_pages,
45
+ "total_items": total_items,
46
+ "page_size": PAGE_SIZE,
47
+ }
48
+ )
49
+ except Exception as e:
50
+ logger.error(f"Error reading the Excel file: {str(e)}")
51
+ raise HTTPException(
52
+ status_code=500, detail=f"Error reading the Excel file: {str(e)}"
53
+ )
54
+
55
+
56
+ @router.get("/destination_detail")
57
+ async def get_destinations(destination_id: str = Query(min_length=1, max_length=50)):
58
+ try:
59
+ destination_data = await DestinationCRUD.find_by_id(destination_id)
60
+ if not destination_data:
61
+ return JSONResponse(
62
+ content={"message": "Destination not found"}, status_code=404
63
+ )
64
+ destination_data.pop("created_at")
65
+ destination_data.pop("updated_at")
66
+ destination_data.pop("expire_at")
67
+ destination_data["id"] = str(destination_data["_id"])
68
+ destination_data.pop("_id")
69
+ print("destination_data", destination_data)
70
+ return JSONResponse(content=destination_data, status_code=200)
71
+
72
+ except Exception as e:
73
+ logger.error(f"Error fetching destination: {str(e)}")
74
+
75
+
76
+ @router.get("/paginate")
77
+ async def get_paginated_destinations(
78
+ page: int = Query(default=1, ge=1), page_size: int = Query(default=10, ge=1, le=100)
79
+ ):
80
+ """
81
+ Get paginated destinations from database
82
+
83
+ Args:
84
+ page: Page number (starts from 1)
85
+ page_size: Number of items per page
86
+
87
+ Returns:
88
+ Paginated list of destinations with pagination metadata
89
+ """
90
+ try:
91
+ # Calculate skip value for pagination
92
+ skip = (page - 1) * page_size
93
+
94
+ # Query destinations with pagination
95
+ destination_data = await DestinationCRUD.find_many(
96
+ filter={}, # Empty filter means get all
97
+ skip=skip,
98
+ limit=page_size,
99
+ sort=[("created_at", -1)], # Sort by created_at descending
100
+ )
101
+
102
+ # Get total count for pagination metadata
103
+ total_items = await DestinationCRUD.count({})
104
+ total_pages = math.ceil(total_items / page_size)
105
+
106
+ # Process results
107
+ serialized_data = []
108
+ for dest in destination_data:
109
+ dest.pop("created_at")
110
+ dest.pop("updated_at")
111
+ dest.pop("expire_at")
112
+
113
+ dest_dict = {
114
+ "id": dest["_id"],
115
+ **{k: v for k, v in dest.items() if k != "_id"},
116
+ }
117
+ serialized_data.append(dest_dict)
118
+
119
+ return JSONResponse(
120
+ content={
121
+ "data": serialized_data,
122
+ "page": page,
123
+ "total_pages": total_pages,
124
+ "total_items": total_items,
125
+ "page_size": page_size,
126
+ },
127
+ status_code=200,
128
+ )
129
+ except Exception as e:
130
+ logger.error(f"Error fetching paginated destinations: {str(e)}")
131
+ raise HTTPException(
132
+ status_code=500, detail=f"Error fetching paginated destinations: {str(e)}"
133
+ )
134
+
135
+
136
+ @router.get("/get_tourist_names")
137
+ async def get_tourist_names():
138
+ try:
139
+ destination_data = await DestinationCRUD.find_all()
140
+ serialized_data = []
141
+ for dest in destination_data:
142
+ dest.pop("created_at")
143
+ dest.pop("updated_at")
144
+ dest.pop("expire_at")
145
+ dest["id"] = str(dest["_id"])
146
+ dest.pop("_id")
147
+ serialized_data.append(dest)
148
+ return JSONResponse(content={"data": serialized_data}, status_code=200)
149
+ except Exception as e:
150
+ logger.error(f"Error getting tourist names: {str(e)}")
151
+ raise HTTPException(
152
+ status_code=500, detail=f"Error getting tourist names: {str(e)}"
153
+ )
154
+
155
+
156
+ @router.get("/suggest")
157
+ async def destination_suggestion(
158
+ question: str,
159
+ user_id: str = Query(
160
+ default=None, description="User ID for personalized recommendations"
161
+ ),
162
+ top_k: int = Query(default=5, ge=1, description="Number of destinations to return"),
163
+ ):
164
+ result = await destination_recommendation_func(question, user_id, top_k)
165
+ return JSONResponse(content=result)
166
+
167
+
168
+ @router.post("/")
169
+ async def create_destination(
170
+ destination_data: Destination, current_user: user_dependency
171
+ ):
172
+ if current_user["role"] != "admin":
173
+ raise HTTPException(status_code=403, detail="Unauthorized")
174
+ try:
175
+ destination_id = await DestinationCRUD.create(destination_data.model_dump())
176
+
177
+ if not destination_id:
178
+ raise HTTPException(status_code=400, detail="Failed to create destination")
179
+
180
+ return JSONResponse(
181
+ content={
182
+ "message": "Destination created successfully",
183
+ "id": destination_id,
184
+ },
185
+ status_code=201,
186
+ )
187
+ except Exception as e:
188
+ logger.error(f"Error creating destination: {str(e)}")
189
+ raise HTTPException(
190
+ status_code=500, detail=f"Error creating destination: {str(e)}"
191
+ )
192
+
193
+
194
+ @router.put("/{destination_id}")
195
+ async def update_destination(
196
+ destination_id: str, destination_data: dict, current_user: user_dependency
197
+ ):
198
+ if current_user["role"] != "admin":
199
+ raise HTTPException(status_code=403, detail="Unauthorized")
200
+ try:
201
+ # Add updated_at timestamp
202
+ destination_data["updated_at"] = datetime.utcnow()
203
+
204
+ # Update destination
205
+ result = await DestinationCRUD.update(
206
+ {"_id": ObjectId(destination_id)}, destination_data
207
+ )
208
+
209
+ if not result:
210
+ raise HTTPException(status_code=404, detail="Destination not found")
211
+
212
+ return JSONResponse(
213
+ content={"message": "Destination updated successfully"}, status_code=200
214
+ )
215
+ except Exception as e:
216
+ logger.error(f"Error updating destination: {str(e)}")
217
+ raise HTTPException(
218
+ status_code=500, detail=f"Error updating destination: {str(e)}"
219
+ )
220
+
221
+
222
+ @router.delete("/{destination_id}")
223
+ async def delete_destination(destination_id: str, current_user: user_dependency):
224
+ if current_user["role"] != "admin":
225
+ raise HTTPException(status_code=403, detail="Unauthorized")
226
+ try:
227
+ # Delete destination
228
+ result = await DestinationCRUD.delete_one({"_id": ObjectId(destination_id)})
229
+
230
+ if not result:
231
+ raise HTTPException(status_code=404, detail="Destination not found")
232
+
233
+ return JSONResponse(
234
+ content={"message": "Destination deleted successfully"}, status_code=200
235
+ )
236
+ except Exception as e:
237
+ logger.error(f"Error deleting destination: {str(e)}")
238
+ raise HTTPException(
239
+ status_code=500, detail=f"Error deleting destination: {str(e)}"
240
+ )
241
+
242
+
243
+ @router.get("/")
244
+ async def get_all_destinations(current_user: user_dependency):
245
+ print("current_user", current_user)
246
+ if current_user["role"] != "admin":
247
+ raise HTTPException(status_code=403, detail="Unauthorized")
248
+ try:
249
+ # Get all destinations
250
+ destinations = await DestinationCRUD.find_all()
251
+
252
+ # Process results
253
+ serialized_data = []
254
+ for dest in destinations:
255
+ dest.pop("created_at")
256
+ dest.pop("updated_at")
257
+ dest.pop("expire_at")
258
+ dest["id"] = str(dest["_id"])
259
+ dest.pop("_id")
260
+ serialized_data.append(dest)
261
+
262
+ return JSONResponse(content={"data": serialized_data}, status_code=200)
263
+ except Exception as e:
264
+ logger.error(f"Error fetching destinations: {str(e)}")
265
+ raise HTTPException(
266
+ status_code=500, detail=f"Error fetching destinations: {str(e)}"
267
+ )
src/langgraph/langchain/__pycache__/llm.cpython-311.pyc CHANGED
Binary files a/src/langgraph/langchain/__pycache__/llm.cpython-311.pyc and b/src/langgraph/langchain/__pycache__/llm.cpython-311.pyc differ
 
src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc CHANGED
Binary files a/src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc and b/src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc differ
 
src/langgraph/langchain/llm.py CHANGED
@@ -8,10 +8,10 @@ from langchain_google_genai import ChatGoogleGenerativeAI
8
  llm = ChatGoogleGenerativeAI(
9
  model="gemini-2.0-flash",
10
  temperature=0.1,
11
- max_retries=2,
12
  )
13
  llm_flash = ChatGoogleGenerativeAI(
14
  model="gemini-1.5-flash",
15
  temperature=0.1,
16
- max_retries=2,
17
  )
 
8
  llm = ChatGoogleGenerativeAI(
9
  model="gemini-2.0-flash",
10
  temperature=0.1,
11
+ # max_retries=2,
12
  )
13
  llm_flash = ChatGoogleGenerativeAI(
14
  model="gemini-1.5-flash",
15
  temperature=0.1,
16
+ # max_retries=2,
17
  )
src/langgraph/langchain/prompt.py CHANGED
@@ -144,10 +144,11 @@ routing_recommender_prompt = PromptTemplate.from_template(
144
  """You are a tour guide.
145
  ### Instruction:
146
  You are given a query sentence
147
- Classify the query as 'characteristic' or 'geographic' based on its content.
148
 
149
  - 'Characteristic' if it asks about a place's features (e.g., camping, relaxing, scenic).
150
  - 'Geographic' if it involves location specifics (e.g., near a city, in a province, on a road).
 
151
 
152
  ###
153
  Query sentence: {query}
@@ -167,7 +168,7 @@ Query sentence: {query}
167
 
168
 
169
  class RoutingRecommender(BaseModel):
170
- label: str = Field(..., description="is 'characteristic' or 'geographic'")
171
 
172
 
173
  class CharacteristicExtractor(BaseModel):
 
144
  """You are a tour guide.
145
  ### Instruction:
146
  You are given a query sentence
147
+ Classify the query as 'characteristic', 'geographic' or 'invalid' based on its content.
148
 
149
  - 'Characteristic' if it asks about a place's features (e.g., camping, relaxing, scenic).
150
  - 'Geographic' if it involves location specifics (e.g., near a city, in a province, on a road).
151
+ - 'Invalid' if the query is not related to travel or destination.
152
 
153
  ###
154
  Query sentence: {query}
 
168
 
169
 
170
  class RoutingRecommender(BaseModel):
171
+ label: str = Field(..., description="is 'characteristic', 'geographic' or 'invalid'")
172
 
173
 
174
  class CharacteristicExtractor(BaseModel):
src/langgraph/multi_agent/planner/__pycache__/planner_flow.cpython-311.pyc CHANGED
Binary files a/src/langgraph/multi_agent/planner/__pycache__/planner_flow.cpython-311.pyc and b/src/langgraph/multi_agent/planner/__pycache__/planner_flow.cpython-311.pyc differ
 
src/langgraph/multi_agent/planner/planner_flow.py CHANGED
@@ -104,8 +104,19 @@ async def excute_tools_fn(state: State):
104
  llm_response: AgentAction = state["llm_response"][-1]
105
  tool_call_name = llm_response.tool
106
  tool_args = llm_response.tool_input
107
- logger.info(f"-> Tool name: {tool_call_name}")
 
 
 
 
 
 
 
 
 
 
108
  logger.info(f"-> Tool args: {tool_args}")
 
109
  if tool_call_name == "destination_suggestion":
110
  tool_response = await destination_suggestion.ainvoke(
111
  {"query": tool_args, "config": ""}
 
104
  llm_response: AgentAction = state["llm_response"][-1]
105
  tool_call_name = llm_response.tool
106
  tool_args = llm_response.tool_input
107
+
108
+ # Preprocess tool name to remove markdown formatting
109
+ if tool_call_name.startswith("**") and tool_call_name.endswith("**"):
110
+ tool_call_name = tool_call_name[2:-2] # Remove ** from start and end
111
+ elif tool_call_name.startswith("*") and tool_call_name.endswith("*"):
112
+ tool_call_name = tool_call_name[1:-1] # Remove * from start and end
113
+ elif tool_call_name.startswith("`") and tool_call_name.endswith("`"):
114
+ tool_call_name = tool_call_name[1:-1] # Remove ` from start and end
115
+
116
+ logger.info(f"-> Original tool name: {llm_response.tool}")
117
+ logger.info(f"-> Processed tool name: {tool_call_name}")
118
  logger.info(f"-> Tool args: {tool_args}")
119
+
120
  if tool_call_name == "destination_suggestion":
121
  tool_response = await destination_suggestion.ainvoke(
122
  {"query": tool_args, "config": ""}
src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc CHANGED
Binary files a/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc and b/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc differ
 
src/langgraph/tools/destination_tools.py CHANGED
@@ -14,8 +14,13 @@ async def destination_suggestion(query: str, config: RunnableConfig):
14
  query (str): query related to wanting to go somewhere locations near a certain area/location or location's characteristics user want to go. Auto extracted from user's message.
15
  Using Vietnamese language for better results
16
  """
 
 
 
 
 
17
  logger.info(f"Destination recommendation query: {query}")
18
- response = await destination_recommendation_func(query, tool_chat=True)
19
  logger.info(f"Destination recommendation output: {response}")
20
  return response
21
 
 
14
  query (str): query related to wanting to go somewhere locations near a certain area/location or location's characteristics user want to go. Auto extracted from user's message.
15
  Using Vietnamese language for better results
16
  """
17
+ configuration = config.get("configurable", {})
18
+ user_id = configuration.get("user_id", None)
19
+ if not user_id:
20
+ logger.info("User ID is not provided")
21
+
22
  logger.info(f"Destination recommendation query: {query}")
23
+ response = await destination_recommendation_func(query, user_id, tool_chat=True)
24
  logger.info(f"Destination recommendation output: {response}")
25
  return response
26