ABAO77 commited on
Commit
eec2685
·
verified ·
1 Parent(s): 10b7d6c

Upload 157 files

Browse files
Files changed (41) hide show
  1. src/src/apis/__pycache__/create_app.cpython-311.pyc +0 -0
  2. src/src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc +0 -0
  3. src/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc +0 -0
  4. src/src/apis/controllers/__pycache__/location_controller.cpython-311.pyc +0 -0
  5. src/src/apis/controllers/__pycache__/post_controller.cpython-311.pyc +0 -0
  6. src/src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc +0 -0
  7. src/src/apis/controllers/comment_controller.py +9 -4
  8. src/src/apis/controllers/destination_controller.py +62 -19
  9. src/src/apis/controllers/location_controller.py +40 -2
  10. src/src/apis/controllers/post_controller.py +35 -9
  11. src/src/apis/controllers/reaction_controller.py +10 -3
  12. src/src/apis/create_app.py +1 -1
  13. src/src/apis/interfaces/__pycache__/api_interface.cpython-311.pyc +0 -0
  14. src/src/apis/interfaces/api_interface.py +20 -4
  15. src/src/apis/middlewares/__pycache__/auth_middleware.cpython-311.pyc +0 -0
  16. src/src/apis/models/__pycache__/post_models.cpython-311.pyc +0 -0
  17. src/src/apis/models/post_models.py +6 -5
  18. src/src/apis/routes/__pycache__/auth_route.cpython-311.pyc +0 -0
  19. src/src/apis/routes/__pycache__/post_router.cpython-311.pyc +0 -0
  20. src/src/apis/routes/__pycache__/reaction_route.cpython-311.pyc +0 -0
  21. src/src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc +0 -0
  22. src/src/apis/routes/post_router.py +5 -5
  23. src/src/apis/routes/reaction_route.py +52 -3
  24. src/src/apis/routes/travel_dest_route.py +4 -2
  25. src/src/langgraph/config/__pycache__/constant.cpython-311.pyc +0 -0
  26. src/src/langgraph/config/constant.py +14 -0
  27. src/src/langgraph/langchain/__pycache__/llm.cpython-311.pyc +0 -0
  28. src/src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc +0 -0
  29. src/src/langgraph/langchain/llm.py +1 -1
  30. src/src/langgraph/langchain/prompt.py +51 -2
  31. src/src/langgraph/models/__pycache__/model_validator.cpython-311.pyc +0 -0
  32. src/src/langgraph/models/model_validator.py +24 -1
  33. src/src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc +0 -0
  34. src/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc +0 -0
  35. src/src/langgraph/tools/destination_tools.py +20 -4
  36. src/src/utils/__pycache__/helper.cpython-311.pyc +0 -0
  37. src/src/utils/__pycache__/mongo.cpython-311.pyc +0 -0
  38. src/src/utils/__pycache__/redis.cpython-311.pyc +0 -0
  39. src/src/utils/helper.py +7 -2
  40. src/src/utils/mongo.py +23 -6
  41. src/src/utils/redis.py +6 -6
src/src/apis/__pycache__/create_app.cpython-311.pyc CHANGED
Binary files a/src/src/apis/__pycache__/create_app.cpython-311.pyc and b/src/src/apis/__pycache__/create_app.cpython-311.pyc differ
 
src/src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc CHANGED
Binary files a/src/src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc and b/src/src/apis/controllers/__pycache__/comment_controller.cpython-311.pyc differ
 
src/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc CHANGED
Binary files a/src/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc and b/src/src/apis/controllers/__pycache__/destination_controller.cpython-311.pyc differ
 
src/src/apis/controllers/__pycache__/location_controller.cpython-311.pyc CHANGED
Binary files a/src/src/apis/controllers/__pycache__/location_controller.cpython-311.pyc and b/src/src/apis/controllers/__pycache__/location_controller.cpython-311.pyc differ
 
src/src/apis/controllers/__pycache__/post_controller.cpython-311.pyc CHANGED
Binary files a/src/src/apis/controllers/__pycache__/post_controller.cpython-311.pyc and b/src/src/apis/controllers/__pycache__/post_controller.cpython-311.pyc differ
 
src/src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc CHANGED
Binary files a/src/src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc and b/src/src/apis/controllers/__pycache__/reaction_controller.cpython-311.pyc differ
 
src/src/apis/controllers/comment_controller.py CHANGED
@@ -1,12 +1,11 @@
1
  from typing import Dict
2
  from datetime import datetime
3
- from src.utils.mongo import CommentCRUD
4
  from src.utils.logger import logger
5
  from datetime import datetime
6
  from bson import ObjectId
7
  from datetime import datetime
8
  from bson import ObjectId
9
- from src.utils.mongo import UserCRUD
10
  from asyncio import gather
11
 
12
 
@@ -25,8 +24,11 @@ async def create_a_comment_controller(content: str, user_id: str, post_id: str)
25
  "user_id": user_id,
26
  "post_id": post_id,
27
  }
28
- await CommentCRUD.create(comment)
29
- return {"status": "success", "message": "Comment created successfully"}
 
 
 
30
  except Exception as e:
31
  return {"status": "error", "message": str(e)}
32
 
@@ -107,6 +109,9 @@ async def delete_a_comment_controller(user_id: str, comment_id: str) -> Dict:
107
  }
108
 
109
  await CommentCRUD.delete({"_id": ObjectId(comment_id)})
 
 
 
110
  return {"status": "success", "message": "Comment deleted successfully"}
111
  except Exception as e:
112
  logger.error(f"Error deleting post: {str(e)}")
 
1
  from typing import Dict
2
  from datetime import datetime
3
+ from src.utils.mongo import CommentCRUD, UserCRUD, PostCRUD
4
  from src.utils.logger import logger
5
  from datetime import datetime
6
  from bson import ObjectId
7
  from datetime import datetime
8
  from bson import ObjectId
 
9
  from asyncio import gather
10
 
11
 
 
24
  "user_id": user_id,
25
  "post_id": post_id,
26
  }
27
+ output = await CommentCRUD.create(comment)
28
+ await PostCRUD.update(
29
+ {"_id": ObjectId(post_id)}, {"$inc": {"comment_count": 1}}
30
+ )
31
+ return {"status": "success", "message": output}
32
  except Exception as e:
33
  return {"status": "error", "message": str(e)}
34
 
 
109
  }
110
 
111
  await CommentCRUD.delete({"_id": ObjectId(comment_id)})
112
+ await PostCRUD.update(
113
+ {"_id": ObjectId(exist_data["post_id"])}, {"$inc": {"comment_count": -1}}
114
+ )
115
  return {"status": "success", "message": "Comment deleted successfully"}
116
  except Exception as e:
117
  logger.error(f"Error deleting post: {str(e)}")
src/src/apis/controllers/destination_controller.py CHANGED
@@ -2,26 +2,29 @@ from typing import List, Dict, Any
2
  import aiohttp
3
  from fastapi import HTTPException
4
  from src.utils.logger import logger
5
- async def destination_suggestion_controller(question: str, top_k: int = 5) -> List[Dict[str, Any]]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  async with aiohttp.ClientSession() as session:
7
  # Get question tags
8
  try:
9
  async with session.get(
10
- f"https://darkbreakerk-triventure-ai.hf.space/model/get_question_tags/{question}"
11
- ) as response_tag:
12
- if response_tag.status == 200:
13
- tag_data = await response_tag.json()
14
- tags = " ".join(tag_data["question_tags"])
15
- logger.info(f"Tags: {tags} for question: {question}")
16
- else:
17
- raise HTTPException(
18
- status_code=response_tag.status,
19
- detail=f"Tag request failed with status {response_tag.status}"
20
- )
21
-
22
- # Get destinations list
23
- async with session.get(
24
- f"https://darkbreakerk-triventure-ai.hf.space/model/get_destinations_list/{tags}/{top_k}"
25
  ) as response:
26
  if response.status == 200:
27
  data = await response.json()
@@ -30,8 +33,48 @@ async def destination_suggestion_controller(question: str, top_k: int = 5) -> Li
30
  else:
31
  raise HTTPException(
32
  status_code=response.status,
33
- detail=f"Destinations request failed with status {response.status}"
34
  )
35
-
36
  except aiohttp.ClientError as e:
37
- raise HTTPException(status_code=500, detail=f"Request failed: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import aiohttp
3
  from fastapi import HTTPException
4
  from src.utils.logger import logger
5
+ import json
6
+ from src.langgraph.langchain.prompt import (
7
+ routing_recommender_chain,
8
+ characteristic_extractor_chain,
9
+ RoutingRecommender,
10
+ CharacteristicExtractor,
11
+ )
12
+ from src.apis.controllers.location_controller import (
13
+ get_lat_long_location,
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()
 
33
  else:
34
  raise HTTPException(
35
  status_code=response.status,
36
+ detail=f"Destinations request failed with status {response.status}",
37
  )
38
+
39
  except aiohttp.ClientError as e:
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 = [
51
+ {
52
+ "name": i,
53
+ "map_url": "https://www.google.com/maps/search/109.23333,13.76667",
54
+ }
55
+ for i in output
56
+ ]
57
+ return output
58
+ characteristic_extract_response: CharacteristicExtractor = (
59
+ await characteristic_extractor_chain.ainvoke({"query": query})
60
+ )
61
+ lat, lon = get_lat_long_location(characteristic_extract_response.main_place)
62
+ response = get_places(
63
+ lat,
64
+ lon,
65
+ 5000,
66
+ available_categories.get(characteristic_extract_response.kind, None),
67
+ top_k,
68
+ )
69
+ output = json.loads(response.body)
70
+ if tool_chat:
71
+ output = [
72
+ {
73
+ "name": i["name"],
74
+ "address": i["address"],
75
+ "distance_km": i["distance_km"],
76
+ }
77
+ for i in output
78
+ ]
79
+
80
+ return output
src/src/apis/controllers/location_controller.py CHANGED
@@ -1,8 +1,13 @@
1
  from src.utils.logger import logger
2
- from src.utils.helper import format_geoapify_response, format_weather_data
 
 
 
 
3
  from fastapi.responses import JSONResponse
4
  import requests
5
  import os
 
6
 
7
 
8
  def get_location_details(lat, long):
@@ -53,7 +58,6 @@ def get_places(lat, long, radius, categories, limit=20):
53
  response = requests.get(url)
54
  if response.status_code == 200:
55
  response = response.json().get("features", [])
56
- # logger.info(f"RESPONSE:{response}")
57
  if response:
58
  response = format_geoapify_response(response, long, lat)
59
  return JSONResponse(content=response, status_code=200)
@@ -75,3 +79,37 @@ def get_weather(lat, long):
75
  return JSONResponse(
76
  content={"error": "Error fetching weather"}, status_code=500
77
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from src.utils.logger import logger
2
+ from src.utils.helper import (
3
+ format_geoapify_response,
4
+ format_weather_data,
5
+ get_google_map_url,
6
+ )
7
  from fastapi.responses import JSONResponse
8
  import requests
9
  import os
10
+ import json
11
 
12
 
13
  def get_location_details(lat, long):
 
58
  response = requests.get(url)
59
  if response.status_code == 200:
60
  response = response.json().get("features", [])
 
61
  if response:
62
  response = format_geoapify_response(response, long, lat)
63
  return JSONResponse(content=response, status_code=200)
 
79
  return JSONResponse(
80
  content={"error": "Error fetching weather"}, status_code=500
81
  )
82
+
83
+
84
+ def get_lat_long_location(location_name):
85
+ """
86
+ Retrieve the latitude and longitude for a given location name using Geoapify's Geocoding API.
87
+
88
+ Parameters:
89
+ - location_name (str): The name of the location to geocode.
90
+ - api_key (str): Your Geoapify API key.
91
+
92
+ Returns:
93
+ - tuple: (latitude, longitude) if the location is found; otherwise, (None, None).
94
+ """
95
+ api_key = os.getenv("GEOAPIFY_API_KEY", None)
96
+ base_url = "https://api.geoapify.com/v1/geocode/search"
97
+ params = {"text": location_name, "apiKey": api_key}
98
+ response = requests.get(base_url, params=params)
99
+ data = response.json()
100
+ if response.status_code == 200 and data.get("features"):
101
+ # Extract latitude and longitude from the response
102
+ latitude = data["features"][0]["properties"]["lat"]
103
+ longitude = data["features"][0]["properties"]["lon"]
104
+ return latitude, longitude
105
+ else:
106
+ print(f"Location '{location_name}' not found.")
107
+ return None, None
108
+
109
+
110
+ def get_geometry_nearby_places(lat, lon, radius, kinds):
111
+ res = get_nearby_places(lat=lat, long=lon, radius=radius, kinds=kinds)
112
+ data = json.loads(res.body)
113
+ recommend_places = data.get("places", [])
114
+ recommend_places = [place["properties"]["name"] for place in recommend_places]
115
+ return recommend_places
src/src/apis/controllers/post_controller.py CHANGED
@@ -1,7 +1,7 @@
1
  from typing import Dict
2
  from src.utils.mongo import PostCRUD
3
  from src.utils.logger import logger
4
- from src.utils.mongo import UserCRUD
5
  from asyncio import gather
6
  from src.utils.helper import call_external_api, serialize_datetime
7
 
@@ -16,12 +16,12 @@ async def create_a_post_controller(
16
  url="https://abao77-image-retrieval.hf.space/upload_image",
17
  json={"base64_image": images},
18
  )
19
- print("image url", image_url)
20
  post = {
21
  "content": content,
22
  "user_id": user_id,
23
  "destination_id": destination_id,
24
- "comment_ids": [],
 
25
  "picture": image_url["public_url"] if image_url.get("public_url") else None,
26
  "like": [],
27
  }
@@ -42,8 +42,8 @@ async def get_a_post_controller(post_id: str) -> Dict:
42
  "content": post.get("content"),
43
  "user_id": post.get("user_id"),
44
  "destination_id": post.get("destination_id"),
45
- "comment_ids": post.get("comment_ids", []),
46
- "like": post.get("like", []),
47
  "created_at": serialize_datetime(post.get("created_at")),
48
  "updated_at": serialize_datetime(post.get("updated_at")),
49
  }
@@ -54,27 +54,53 @@ async def get_a_post_controller(post_id: str) -> Dict:
54
  return {"status": "error", "message": str(e)}
55
 
56
 
57
- async def list_all_posts_controller():
58
  try:
59
  posts = await PostCRUD.find_all()
60
  user_ids = [post.get("user_id") for post in posts]
61
  user_infos = await gather(
62
  *[UserCRUD.find_by_id(user_id) for user_id in user_ids]
63
  )
 
64
  user_info_map = {
65
  user_info.get("_id"): user_info for user_info in user_infos if user_info
66
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  serialized_posts = []
69
- for post in posts:
70
  user_id = post.get("user_id")
71
  user_info = user_info_map.get(user_id)
72
  serialized_post = {
73
  "id": serialize_datetime(post.get("_id")),
74
  "content": post.get("content"),
75
  "destination_id": post.get("destination_id"),
76
- "comment_ids": post.get("comment_ids", []),
77
- "like": post.get("like", []),
 
 
 
 
 
78
  "picture": post.get("picture", []),
79
  "created_at": serialize_datetime(post.get("created_at")),
80
  "updated_at": serialize_datetime(post.get("updated_at")),
 
1
  from typing import Dict
2
  from src.utils.mongo import PostCRUD
3
  from src.utils.logger import logger
4
+ from src.utils.mongo import UserCRUD, ReactionCRUD
5
  from asyncio import gather
6
  from src.utils.helper import call_external_api, serialize_datetime
7
 
 
16
  url="https://abao77-image-retrieval.hf.space/upload_image",
17
  json={"base64_image": images},
18
  )
 
19
  post = {
20
  "content": content,
21
  "user_id": user_id,
22
  "destination_id": destination_id,
23
+ "comment_count": 0,
24
+ "reaction_count": 0,
25
  "picture": image_url["public_url"] if image_url.get("public_url") else None,
26
  "like": [],
27
  }
 
42
  "content": post.get("content"),
43
  "user_id": post.get("user_id"),
44
  "destination_id": post.get("destination_id"),
45
+ "comment_count": post.get("comment_count", 0),
46
+ "reaction_count": post.get("reaction_count", 0),
47
  "created_at": serialize_datetime(post.get("created_at")),
48
  "updated_at": serialize_datetime(post.get("updated_at")),
49
  }
 
54
  return {"status": "error", "message": str(e)}
55
 
56
 
57
+ async def list_all_posts_controller(user_id: str):
58
  try:
59
  posts = await PostCRUD.find_all()
60
  user_ids = [post.get("user_id") for post in posts]
61
  user_infos = await gather(
62
  *[UserCRUD.find_by_id(user_id) for user_id in user_ids]
63
  )
64
+
65
  user_info_map = {
66
  user_info.get("_id"): user_info for user_info in user_infos if user_info
67
  }
68
+ all_post_ids = {serialize_datetime(post.get("_id")) for post in posts}
69
+
70
+ formatted_user_reactions = []
71
+ if user_id:
72
+ reactions = await gather(
73
+ *[
74
+ ReactionCRUD.read_one({"user_id": user_id, "post_id": post_id})
75
+ for post_id in all_post_ids
76
+ ]
77
+ )
78
+ formatted_user_reactions = [
79
+ {
80
+ "id": serialize_datetime(reaction.get("_id", None)),
81
+ "post_id": reaction.get("post_id", None),
82
+ "user_id": reaction.get("user_id", None),
83
+ "reaction_type": reaction.get("type", None),
84
+ }
85
+ for reaction in reactions
86
+ if reaction is not None # Ensure only non-None reactions are processed
87
+ ]
88
 
89
  serialized_posts = []
90
+ for index, post in enumerate(posts):
91
  user_id = post.get("user_id")
92
  user_info = user_info_map.get(user_id)
93
  serialized_post = {
94
  "id": serialize_datetime(post.get("_id")),
95
  "content": post.get("content"),
96
  "destination_id": post.get("destination_id"),
97
+ "comment_count": post.get("comment_count", []),
98
+ "reaction_count": post.get("reaction_count", []),
99
+ "current_user_reaction": (
100
+ formatted_user_reactions[index]
101
+ if formatted_user_reactions
102
+ else None
103
+ ),
104
  "picture": post.get("picture", []),
105
  "created_at": serialize_datetime(post.get("created_at")),
106
  "updated_at": serialize_datetime(post.get("updated_at")),
src/src/apis/controllers/reaction_controller.py CHANGED
@@ -1,9 +1,8 @@
1
  from typing import Dict
2
  from datetime import datetime
3
- from src.utils.mongo import ReactionCRUD
4
  from src.utils.logger import logger
5
  from datetime import datetime
6
- from bson import ObjectId
7
  from datetime import datetime
8
  from bson import ObjectId
9
  from src.utils.mongo import UserCRUD
@@ -26,6 +25,9 @@ async def create_a_reaction_controller(user_id: str, post_id: str, type: int) ->
26
  "type": type,
27
  }
28
  await ReactionCRUD.create(reaction)
 
 
 
29
  return {"status": "success", "message": "Reaction created successfully"}
30
  except Exception as e:
31
  return {"status": "error", "message": str(e)}
@@ -70,7 +72,9 @@ async def list_all_reaction_controller(post_id: str) -> Dict:
70
  return {"status": "error", "message": str(e)}
71
 
72
 
73
- async def update_a_reaction_controller(user_id: str, reaction_id: str, type: int) -> Dict:
 
 
74
  try:
75
  exist_data = await ReactionCRUD.find_by_id(reaction_id)
76
  if exist_data["user_id"] != user_id:
@@ -103,6 +107,9 @@ async def delete_a_reaction_controller(user_id: str, reaction_id: str) -> Dict:
103
  if exist_data is None:
104
  return {"status": "error", "message": "Reaction not found"}
105
  await ReactionCRUD.delete({"_id": ObjectId(reaction_id)})
 
 
 
106
  return {"status": "success", "message": "Reaction deleted successfully"}
107
  except Exception as e:
108
  logger.error(f"Error deleting reaction: {str(e)}")
 
1
  from typing import Dict
2
  from datetime import datetime
3
+ from src.utils.mongo import ReactionCRUD, PostCRUD
4
  from src.utils.logger import logger
5
  from datetime import datetime
 
6
  from datetime import datetime
7
  from bson import ObjectId
8
  from src.utils.mongo import UserCRUD
 
25
  "type": type,
26
  }
27
  await ReactionCRUD.create(reaction)
28
+ await PostCRUD.update(
29
+ {"_id": ObjectId(post_id)}, {"$inc": {"reaction_count": 1}}
30
+ )
31
  return {"status": "success", "message": "Reaction created successfully"}
32
  except Exception as e:
33
  return {"status": "error", "message": str(e)}
 
72
  return {"status": "error", "message": str(e)}
73
 
74
 
75
+ async def update_a_reaction_controller(
76
+ user_id: str, reaction_id: str, type: int
77
+ ) -> Dict:
78
  try:
79
  exist_data = await ReactionCRUD.find_by_id(reaction_id)
80
  if exist_data["user_id"] != user_id:
 
107
  if exist_data is None:
108
  return {"status": "error", "message": "Reaction not found"}
109
  await ReactionCRUD.delete({"_id": ObjectId(reaction_id)})
110
+ await PostCRUD.update(
111
+ {"_id": ObjectId(exist_data["post_id"])}, {"$inc": {"reaction_count": -1}}
112
+ )
113
  return {"status": "success", "message": "Reaction deleted successfully"}
114
  except Exception as e:
115
  logger.error(f"Error deleting reaction: {str(e)}")
src/src/apis/create_app.py CHANGED
@@ -26,7 +26,7 @@ api_router.include_router(router_reaction)
26
  def create_app():
27
  app = FastAPI(
28
  docs_url="/",
29
- title="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3M2IwZDMzNTQ5OTg5Zjc1NmZhMzk3MCJ9.a3A9B1ZpzkzIPvhLqFpasK4sk2ocqmc1M80rtyAkbmM",
30
  )
31
 
32
  app.add_middleware(
 
26
  def create_app():
27
  app = FastAPI(
28
  docs_url="/",
29
+ title="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3ZTU1ZTczYTA0MzI1YmUzNjAzYjU2MCJ9.Vdz3DdbvNUdUERDXEOyvi14EuYATRw29eOVWNGTQIYo",
30
  )
31
 
32
  app.add_middleware(
src/src/apis/interfaces/__pycache__/api_interface.cpython-311.pyc CHANGED
Binary files a/src/src/apis/interfaces/__pycache__/api_interface.cpython-311.pyc and b/src/src/apis/interfaces/__pycache__/api_interface.cpython-311.pyc differ
 
src/src/apis/interfaces/api_interface.py CHANGED
@@ -75,15 +75,16 @@ class Location(BaseModel):
75
 
76
 
77
  class Destination(BaseModel):
78
- id: int = Field(..., title="Destination Id", gt=0)
79
- name: str = Field(..., title="Destination Name", min_length=1)
80
- location: str = Field(..., title="Location", min_length=1)
81
- description: str = Field(..., title="Description", min_length=1)
82
 
83
 
84
  class Planning(BaseModel):
85
  duration: str = Field("7", title="Duration")
86
  start_date: str = Field("June 1-7", title="Start date")
 
87
  location: str = Field("Quy Nhon, Vietnam", title="Location")
88
  interests: str = Field("natural, cultural", title="Interests")
89
  nation: str = Field("Vietnamese", title="Nation")
@@ -107,3 +108,18 @@ class Planning(BaseModel):
107
  "limit_interation": 3,
108
  }
109
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
 
77
  class Destination(BaseModel):
78
+ id: Optional[str] = Field(..., title="Destination Id", min_length=0)
79
+ name: Optional[str] = Field(..., title="Destination Name", min_length=1)
80
+ description: Optional[str] = Field(..., title="Description", min_length=0)
81
+ image: Optional[str] = Field(..., title="Image", min_length=0)
82
 
83
 
84
  class Planning(BaseModel):
85
  duration: str = Field("7", title="Duration")
86
  start_date: str = Field("June 1-7", title="Start date")
87
+ end_date: str = Field("June 1-7", title="End date")
88
  location: str = Field("Quy Nhon, Vietnam", title="Location")
89
  interests: str = Field("natural, cultural", title="Interests")
90
  nation: str = Field("Vietnamese", title="Nation")
 
108
  "limit_interation": 3,
109
  }
110
  }
111
+
112
+
113
+ {
114
+ "duration": "6",
115
+ "start_date": "2025-03-18",
116
+ "end_date": "2025-03-24",
117
+ "include_destination": [
118
+ {"id": "", "name": "KHU DÃ NGOẠI TRUNG LƯƠNG", "description": "", "image": ""},
119
+ {"id": "", "name": "NÚI VŨNG CHUA", "description": "", "image": ""},
120
+ ],
121
+ "interests": "natural, cultural",
122
+ "location": "Quy Nhon, Vietnam",
123
+ "nation": "Vietnamese",
124
+ "limit_interation": 5,
125
+ }
src/src/apis/middlewares/__pycache__/auth_middleware.cpython-311.pyc CHANGED
Binary files a/src/src/apis/middlewares/__pycache__/auth_middleware.cpython-311.pyc and b/src/src/apis/middlewares/__pycache__/auth_middleware.cpython-311.pyc differ
 
src/src/apis/models/__pycache__/post_models.cpython-311.pyc CHANGED
Binary files a/src/src/apis/models/__pycache__/post_models.cpython-311.pyc and b/src/src/apis/models/__pycache__/post_models.cpython-311.pyc differ
 
src/src/apis/models/post_models.py CHANGED
@@ -18,10 +18,11 @@ class Comment(BaseDocument):
18
  }
19
  }
20
 
 
21
  class Reaction(BaseDocument):
22
  user_id: str = Field("", description="User's id")
23
  post_id: str = Field("", description="Post's id")
24
- type: int = Field(0, description="Type of like", gt=0, lt=5)
25
 
26
  model_config = {
27
  "json_schema_extra": {
@@ -38,8 +39,8 @@ class Post(BaseDocument):
38
  content: str = Field("", description="Post's content")
39
  user_id: str = Field("", description="User's id")
40
  destination_id: str = Field("", description="Destination's id")
41
- comment_ids: list[str] = Field([], description="Comment's id")
42
- like: list[str] = Field([], description="User's id who like this post")
43
  picture: Optional[list[str]] = Field([], description="Picture's url")
44
  model_config = {
45
  "json_schema_extra": {
@@ -47,8 +48,8 @@ class Post(BaseDocument):
47
  "content": "John Doe",
48
  "user_id": "1234567890",
49
  "destination_id": "1234567890",
50
- "comment_ids": ["1234567890"],
51
- "like": ["1234567890"],
52
  "picture": ["https://example.com/picture.jpg"],
53
  }
54
  }
 
18
  }
19
  }
20
 
21
+
22
  class Reaction(BaseDocument):
23
  user_id: str = Field("", description="User's id")
24
  post_id: str = Field("", description="Post's id")
25
+ type: int = Field(0, description="Type of like", ge=0, lt=5)
26
 
27
  model_config = {
28
  "json_schema_extra": {
 
39
  content: str = Field("", description="Post's content")
40
  user_id: str = Field("", description="User's id")
41
  destination_id: str = Field("", description="Destination's id")
42
+ comment_count: int = Field(0, description="Comment's id")
43
+ reaction_count: int = Field(0, description="User's id who like this post")
44
  picture: Optional[list[str]] = Field([], description="Picture's url")
45
  model_config = {
46
  "json_schema_extra": {
 
48
  "content": "John Doe",
49
  "user_id": "1234567890",
50
  "destination_id": "1234567890",
51
+ "comment_count": 1,
52
+ "reaction_count": 1,
53
  "picture": ["https://example.com/picture.jpg"],
54
  }
55
  }
src/src/apis/routes/__pycache__/auth_route.cpython-311.pyc CHANGED
Binary files a/src/src/apis/routes/__pycache__/auth_route.cpython-311.pyc and b/src/src/apis/routes/__pycache__/auth_route.cpython-311.pyc differ
 
src/src/apis/routes/__pycache__/post_router.cpython-311.pyc CHANGED
Binary files a/src/src/apis/routes/__pycache__/post_router.cpython-311.pyc and b/src/src/apis/routes/__pycache__/post_router.cpython-311.pyc differ
 
src/src/apis/routes/__pycache__/reaction_route.cpython-311.pyc CHANGED
Binary files a/src/src/apis/routes/__pycache__/reaction_route.cpython-311.pyc and b/src/src/apis/routes/__pycache__/reaction_route.cpython-311.pyc differ
 
src/src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc CHANGED
Binary files a/src/src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc and b/src/src/apis/routes/__pycache__/travel_dest_route.cpython-311.pyc differ
 
src/src/apis/routes/post_router.py CHANGED
@@ -67,13 +67,13 @@ async def get_post(post_id: str):
67
 
68
 
69
  @router.get("/list", status_code=status.HTTP_200_OK)
70
- async def list_all_posts(background_tasks: BackgroundTasks):
71
- result = await get_key_redis("all_posts")
72
  result = None
73
- result = eval(result) if result else None
74
  if not result:
75
- result = await list_all_posts_controller()
76
- background_tasks.add_task(set_key_redis, "all_posts", str(result), 20)
77
  if result["status"] == "error":
78
  return JSONResponse(content=result, status_code=404)
79
  else:
 
67
 
68
 
69
  @router.get("/list", status_code=status.HTTP_200_OK)
70
+ async def list_all_posts(user_id: Optional[str] = None):
71
+ # result = await get_key_redis("all_posts")
72
  result = None
73
+ # result = eval(result) if result else None
74
  if not result:
75
+ result = await list_all_posts_controller(user_id)
76
+ # background_tasks.add_task(set_key_redis, "all_posts", str(result), 20)
77
  if result["status"] == "error":
78
  return JSONResponse(content=result, status_code=404)
79
  else:
src/src/apis/routes/reaction_route.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter, status, Depends
2
- from typing import Annotated
3
  from fastapi.responses import JSONResponse
4
  from pydantic import Field
5
  from src.apis.models.user_models import User
@@ -11,8 +11,10 @@ from src.apis.controllers.reaction_controller import (
11
  update_a_reaction_controller,
12
  delete_a_reaction_controller,
13
  )
 
14
  from src.utils.redis import set_key_redis, get_key_redis
15
  from src.utils.logger import logger
 
16
 
17
  router = APIRouter(prefix="/reaction", tags=["Reaction"])
18
 
@@ -21,7 +23,7 @@ user_dependency = Annotated[User, Depends(get_current_user)]
21
 
22
  class BodyLike(BaseDocument):
23
  post_id: str = Field("", description="Post's id", min_length=1)
24
- type: int = Field(0, description="Type of like", gt=0, lt=3)
25
 
26
  class Config:
27
  json_schema_extra = {
@@ -59,7 +61,7 @@ async def get_reactions_of_a_post(post_id: str):
59
 
60
  class BodyUpdateReaction(BaseDocument):
61
  reaction_id: str = Field("", description="Reaction's id")
62
- type: str = Field("", description="Type of like", gt=0, lt=3)
63
 
64
  class Config:
65
  json_schema_extra = {
@@ -70,6 +72,53 @@ class BodyUpdateReaction(BaseDocument):
70
  }
71
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  @router.patch("/update/", status_code=status.HTTP_200_OK)
74
  async def update_reaction(body: BodyUpdateReaction, user: user_dependency):
75
  if user is None:
 
1
  from fastapi import APIRouter, status, Depends
2
+ from typing import Annotated, Optional
3
  from fastapi.responses import JSONResponse
4
  from pydantic import Field
5
  from src.apis.models.user_models import User
 
11
  update_a_reaction_controller,
12
  delete_a_reaction_controller,
13
  )
14
+ from src.utils.mongo import ReactionCRUD, PostCRUD
15
  from src.utils.redis import set_key_redis, get_key_redis
16
  from src.utils.logger import logger
17
+ from bson import ObjectId
18
 
19
  router = APIRouter(prefix="/reaction", tags=["Reaction"])
20
 
 
23
 
24
  class BodyLike(BaseDocument):
25
  post_id: str = Field("", description="Post's id", min_length=1)
26
+ type: int = Field(0, description="Type of like", ge=0, lt=5)
27
 
28
  class Config:
29
  json_schema_extra = {
 
61
 
62
  class BodyUpdateReaction(BaseDocument):
63
  reaction_id: str = Field("", description="Reaction's id")
64
+ type: str = Field("", description="Type of like", gt=0, lt=5)
65
 
66
  class Config:
67
  json_schema_extra = {
 
72
  }
73
 
74
 
75
+ class BodyUpdateReactionInteract(BaseDocument):
76
+ reaction_id: Optional[str] = Field("", description="Reaction's id")
77
+ post_id: str = Field("", description="Post's id", min_length=1)
78
+ current_type: int = Field("", description="Current type of like", ge=0, lt=5)
79
+ type: int = Field(0, description="Type of like", ge=0, lt=5)
80
+ model_config = {
81
+ "json_schema_extra": {
82
+ "examples": [
83
+ {
84
+ "reaction_id": "1234567890",
85
+ "post_id": "1234567890",
86
+ "current_type": 1,
87
+ "type": 2,
88
+ },
89
+ ]
90
+ }
91
+ }
92
+
93
+
94
+ @router.post("/interact/", status_code=status.HTTP_200_OK)
95
+ async def interact_reaction(body: BodyUpdateReactionInteract, user: user_dependency):
96
+ if user is None:
97
+ return JSONResponse(content={"message": "User not found"}, status_code=404)
98
+ if not body.reaction_id:
99
+ response = await ReactionCRUD.create(
100
+ {"user_id": user["id"], "post_id": body.post_id, "type": body.type}
101
+ )
102
+ await PostCRUD.update(
103
+ {"_id": ObjectId(body.post_id)}, {"$inc": {"reaction_count": 1}}
104
+ )
105
+
106
+ return JSONResponse(content={"reaction_id": str(response)}, status_code=201)
107
+ elif body.current_type == body.type:
108
+ await ReactionCRUD.delete_one({"_id": ObjectId(body.reaction_id)})
109
+ await PostCRUD.update(
110
+ {"_id": ObjectId(body.post_id)}, {"$inc": {"reaction_count": -1}}
111
+ )
112
+ return JSONResponse(content={"message": "Reaction deleted"}, status_code=200)
113
+ elif body.current_type != body.type:
114
+ await ReactionCRUD.update(
115
+ {"_id": ObjectId(body.reaction_id)}, {"$set": {"type": body.type}}
116
+ )
117
+ return JSONResponse(content={"message": "Reaction updated"}, status_code=200)
118
+
119
+ return JSONResponse(content={"message": "Reaction not found"}, status_code=404)
120
+
121
+
122
  @router.patch("/update/", status_code=status.HTTP_200_OK)
123
  async def update_reaction(body: BodyUpdateReaction, user: user_dependency):
124
  if user is None:
src/src/apis/routes/travel_dest_route.py CHANGED
@@ -8,9 +8,11 @@ from src.apis.middlewares.auth_middleware import get_current_user
8
  from src.utils.logger import logger
9
  from src.apis.controllers.destination_controller import (
10
  destination_suggestion_controller,
 
11
  )
12
  from fastapi.responses import JSONResponse
13
  from src.utils.mongo import DestinationCRUD
 
14
  router = APIRouter(prefix="/dest", tags=["Destination"])
15
 
16
  user_dependency = Annotated[User, Depends(get_current_user)]
@@ -53,7 +55,7 @@ def get_tourist(page: int = Query(default=1, ge=1)):
53
  async def get_tourist_names():
54
  try:
55
  destination_data = await DestinationCRUD.find_all()
56
-
57
  # Drop the fields created_at, updated_at, expire_at from each item
58
 
59
  serialized_data = []
@@ -74,4 +76,4 @@ async def get_tourist_names():
74
 
75
  @router.get("/suggest")
76
  async def destination_suggestion(question: str, top_k: int = Query(default=5, ge=1)):
77
- return JSONResponse(await destination_suggestion_controller(question, top_k))
 
8
  from src.utils.logger import logger
9
  from src.apis.controllers.destination_controller import (
10
  destination_suggestion_controller,
11
+ destination_recommendation_func,
12
  )
13
  from fastapi.responses import JSONResponse
14
  from src.utils.mongo import DestinationCRUD
15
+
16
  router = APIRouter(prefix="/dest", tags=["Destination"])
17
 
18
  user_dependency = Annotated[User, Depends(get_current_user)]
 
55
  async def get_tourist_names():
56
  try:
57
  destination_data = await DestinationCRUD.find_all()
58
+
59
  # Drop the fields created_at, updated_at, expire_at from each item
60
 
61
  serialized_data = []
 
76
 
77
  @router.get("/suggest")
78
  async def destination_suggestion(question: str, top_k: int = Query(default=5, ge=1)):
79
+ return JSONResponse(await destination_recommendation_func(question))
src/src/langgraph/config/__pycache__/constant.cpython-311.pyc CHANGED
Binary files a/src/src/langgraph/config/__pycache__/constant.cpython-311.pyc and b/src/src/langgraph/config/__pycache__/constant.cpython-311.pyc differ
 
src/src/langgraph/config/constant.py CHANGED
@@ -21,3 +21,17 @@ class MongoCfg:
21
  MAX_HISTORY_SIZE: int = 15
22
  class RedisCfg:
23
  REDIS_URL: str = os.getenv("REDIS_URL")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  MAX_HISTORY_SIZE: int = 15
22
  class RedisCfg:
23
  REDIS_URL: str = os.getenv("REDIS_URL")
24
+
25
+ available_categories = {
26
+ "food_and_beverage": "catering",
27
+ "accommodation": "accommodation",
28
+ "entertainment": "entertainment",
29
+ "leisure": "leisure",
30
+ "natural": "natural",
31
+ "national_park": "national_park",
32
+ "tourism": "tourism",
33
+ "camping": "camping",
34
+ "religion": "religion",
35
+ "sport": "sport",
36
+ "commercial": "commercial",
37
+ }
src/src/langgraph/langchain/__pycache__/llm.cpython-311.pyc CHANGED
Binary files a/src/src/langgraph/langchain/__pycache__/llm.cpython-311.pyc and b/src/src/langgraph/langchain/__pycache__/llm.cpython-311.pyc differ
 
src/src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc CHANGED
Binary files a/src/src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc and b/src/src/langgraph/langchain/__pycache__/prompt.cpython-311.pyc differ
 
src/src/langgraph/langchain/llm.py CHANGED
@@ -6,7 +6,7 @@ from langchain_google_genai import ChatGoogleGenerativeAI
6
  # max_retries=2,
7
  # )
8
  llm = ChatGoogleGenerativeAI(
9
- model="gemini-2.0-flash-exp",
10
  temperature=0.1,
11
  max_retries=2,
12
  )
 
6
  # max_retries=2,
7
  # )
8
  llm = ChatGoogleGenerativeAI(
9
+ model="gemini-2.0-flash",
10
  temperature=0.1,
11
  max_retries=2,
12
  )
src/src/langgraph/langchain/prompt.py CHANGED
@@ -1,4 +1,4 @@
1
- from langchain.prompts import ChatPromptTemplate
2
  from datetime import datetime
3
  from pydantic import BaseModel, Field
4
  from langchain_core.messages import HumanMessage
@@ -7,6 +7,8 @@ import pytz
7
  from src.langgraph.state import State
8
  from src.utils.logger import logger
9
  from langchain_core.prompts import PromptTemplate
 
 
10
 
11
  vietnam_timezone = pytz.timezone("Asia/Ho_Chi_Minh")
12
  vietnam_time = datetime.now(vietnam_timezone).strftime("%Y-%m-%d %H:%M:%S")
@@ -95,7 +97,7 @@ scheduling_prompt = ChatPromptTemplate.from_messages(
95
  - Not required user typing in the format of tool call. You must naturally respond to the user's messages.
96
  - You must call "CompleteOrRoute" than say you can't handle the user's request.
97
  - Answer in {language} language
98
- <<Current time: {current_time}>>
99
  """,
100
  ),
101
  ("placeholder", "{history}"),
@@ -138,6 +140,53 @@ book_hotel_prompt = ChatPromptTemplate.from_messages(
138
  ]
139
  ).partial(current_time=vietnam_time)
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  class CompleteOrRoute(BaseModel):
143
  """Call this function to complete the current assistant's task and route the conversation back to the primary assistant."""
 
1
+ from langchain.prompts import ChatPromptTemplate, PromptTemplate
2
  from datetime import datetime
3
  from pydantic import BaseModel, Field
4
  from langchain_core.messages import HumanMessage
 
7
  from src.langgraph.state import State
8
  from src.utils.logger import logger
9
  from langchain_core.prompts import PromptTemplate
10
+ from .llm import llm, llm_flash
11
+ from src.langgraph.config.constant import available_categories
12
 
13
  vietnam_timezone = pytz.timezone("Asia/Ho_Chi_Minh")
14
  vietnam_time = datetime.now(vietnam_timezone).strftime("%Y-%m-%d %H:%M:%S")
 
97
  - Not required user typing in the format of tool call. You must naturally respond to the user's messages.
98
  - You must call "CompleteOrRoute" than say you can't handle the user's request.
99
  - Answer in {language} language
100
+ <<Current time: {current_time}>> (Focus on current_time in the present moment, rather than the past conversation)
101
  """,
102
  ),
103
  ("placeholder", "{history}"),
 
140
  ]
141
  ).partial(current_time=vietnam_time)
142
 
143
+ 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}
154
+ """
155
+ )
156
+ characteristic_extractor_prompt = PromptTemplate.from_template(
157
+ """You are a tour guide.
158
+ ### Instruction:
159
+ Read the sentence carefully
160
+ Given a query about a destination, extract two things:
161
+ 1. **Kind**: Choose one from {available_categories}.
162
+ 2. **Main Place**: Identify the main place mentioned in the query. Include 'Quy Nhon' or 'Binh Dinh' term in the main place response
163
+
164
+ Query sentence: {query}
165
+ ."""
166
+ ).partial(available_categories=list(available_categories.keys()))
167
+
168
+
169
+ class RoutingRecommender(BaseModel):
170
+ label: str = Field(..., description="is 'characteristic' or 'geographic'")
171
+
172
+
173
+ class CharacteristicExtractor(BaseModel):
174
+ kind: str = Field(
175
+ ...,
176
+ description=f"Choose one from {list(available_categories.keys())}",
177
+ )
178
+ main_place: str = Field(..., description="main place mentioned in the query")
179
+
180
+
181
+ routing_recommender_chain = (
182
+ routing_recommender_prompt | llm_flash.with_structured_output(RoutingRecommender)
183
+ )
184
+
185
+ characteristic_extractor_chain = (
186
+ characteristic_extractor_prompt
187
+ | llm_flash.with_structured_output(CharacteristicExtractor)
188
+ )
189
+
190
 
191
  class CompleteOrRoute(BaseModel):
192
  """Call this function to complete the current assistant's task and route the conversation back to the primary assistant."""
src/src/langgraph/models/__pycache__/model_validator.cpython-311.pyc CHANGED
Binary files a/src/src/langgraph/models/__pycache__/model_validator.cpython-311.pyc and b/src/src/langgraph/models/__pycache__/model_validator.cpython-311.pyc differ
 
src/src/langgraph/models/model_validator.py CHANGED
@@ -1,4 +1,6 @@
1
- # from pydantic import BaseModel, Field, EmailStr
 
 
2
  # from typing import Optional
3
  # from datetime import datetime, timezone
4
 
@@ -46,3 +48,24 @@
46
  # "website": "https://www.bluelagoonresort.com",
47
  # }
48
  # }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field, EmailStr
2
+
3
+
4
  # from typing import Optional
5
  # from datetime import datetime, timezone
6
 
 
48
  # "website": "https://www.bluelagoonresort.com",
49
  # }
50
  # }
51
+
52
+
53
+ class NearlyDestinationRecommendation(BaseModel):
54
+ query: str = Field(
55
+ ...,
56
+ title="Query related to wanting to go somewhere",
57
+ description="Auto extracted from user's message. Using Vietnamese language for better results.",
58
+ )
59
+ places: str = Field(..., title="Places", description="place mention in query")
60
+ kinds: str = Field(
61
+ ...,
62
+ title="Kinds",
63
+ description="""
64
+ Kinds of places you want to find choose one of options:
65
+ ["architecture", "historic", "cultural", "natural", "sport", "religion", "accomodations", "foods", "shops", "other"]""",
66
+ )
67
+
68
+ class Config:
69
+ json_schema_extra = {
70
+ "example": {"query": "Địa điểm gần Hà Nội" "places" "Hà Nội"}
71
+ }
src/src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc CHANGED
Binary files a/src/src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc and b/src/src/langgraph/multi_agent/chat/__pycache__/chat_flow.cpython-311.pyc differ
 
src/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc CHANGED
Binary files a/src/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc and b/src/src/langgraph/tools/__pycache__/destination_tools.cpython-311.pyc differ
 
src/src/langgraph/tools/destination_tools.py CHANGED
@@ -1,18 +1,34 @@
1
  from langchain_core.tools import tool
2
  from src.apis.controllers.destination_controller import (
3
  destination_suggestion_controller,
 
4
  )
5
  from langchain_core.runnables.config import RunnableConfig
6
  from src.utils.logger import logger
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
 
9
  @tool
10
  async def destination_recommendation(query: str, config: RunnableConfig):
11
  """Call tool when user want to recommend a travel destination(tourist attractions, restaurants). Not require user typing anything.
12
  Args:
13
- query (str): query related to wanting to go somewhere. Auto extracted from user's message. Using Vietnamese language for better results.
14
- Output: A list of recommended destinations.
15
  """
16
  logger.info(f"Destination recommendation query: {query}")
17
- output = await destination_suggestion_controller(query, 3)
18
- return output
 
 
1
  from langchain_core.tools import tool
2
  from src.apis.controllers.destination_controller import (
3
  destination_suggestion_controller,
4
+ destination_recommendation_func,
5
  )
6
  from langchain_core.runnables.config import RunnableConfig
7
  from src.utils.logger import logger
8
+ from src.apis.controllers.location_controller import (
9
+ get_nearby_places,
10
+ get_lat_long_location,
11
+ get_geometry_nearby_places,
12
+ )
13
+ from src.langgraph.models.model_validator import NearlyDestinationRecommendation
14
+ from src.utils.helper import get_google_map_url
15
+ from src.langgraph.langchain.prompt import (
16
+ routing_recommender_chain,
17
+ characteristic_extractor_chain,
18
+ RoutingRecommender,
19
+ CharacteristicExtractor,
20
+ )
21
+ import json
22
 
23
 
24
  @tool
25
  async def destination_recommendation(query: str, config: RunnableConfig):
26
  """Call tool when user want to recommend a travel destination(tourist attractions, restaurants). Not require user typing anything.
27
  Args:
28
+ 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.
29
+ Using Vietnamese language for better results
30
  """
31
  logger.info(f"Destination recommendation query: {query}")
32
+ response = await destination_recommendation_func(query)
33
+ logger.info(f"Destination recommendation output: {response}")
34
+ return response
src/src/utils/__pycache__/helper.cpython-311.pyc CHANGED
Binary files a/src/src/utils/__pycache__/helper.cpython-311.pyc and b/src/src/utils/__pycache__/helper.cpython-311.pyc differ
 
src/src/utils/__pycache__/mongo.cpython-311.pyc CHANGED
Binary files a/src/src/utils/__pycache__/mongo.cpython-311.pyc and b/src/src/utils/__pycache__/mongo.cpython-311.pyc differ
 
src/src/utils/__pycache__/redis.cpython-311.pyc CHANGED
Binary files a/src/src/utils/__pycache__/redis.cpython-311.pyc and b/src/src/utils/__pycache__/redis.cpython-311.pyc differ
 
src/src/utils/helper.py CHANGED
@@ -104,8 +104,8 @@ def format_geoapify_response(
104
  if include_latnlong:
105
  formatted_item["lat"] = place_lat
106
  formatted_item["lon"] = place_lon
107
- formatted_item["Accommodation Name"] = feature["properties"]["address_line1"]
108
- formatted_item["Address"] = feature["properties"]["formatted"]
109
  formatted_item["distance_km"] = str(round(distance, 2)) + " km"
110
 
111
  if "contact" in feature["properties"]:
@@ -116,6 +116,7 @@ def format_geoapify_response(
116
 
117
  if "accommodation" in feature["properties"]:
118
  formatted_item["accommodation"] = feature["properties"]["accommodation"]
 
119
 
120
  formatted_data.append(formatted_item)
121
 
@@ -301,3 +302,7 @@ def serialize_datetime(obj):
301
  if isinstance(obj, ObjectId):
302
  return str(obj)
303
  return obj
 
 
 
 
 
104
  if include_latnlong:
105
  formatted_item["lat"] = place_lat
106
  formatted_item["lon"] = place_lon
107
+ formatted_item["name"] = feature["properties"]["address_line1"]
108
+ formatted_item["address"] = feature["properties"]["formatted"]
109
  formatted_item["distance_km"] = str(round(distance, 2)) + " km"
110
 
111
  if "contact" in feature["properties"]:
 
116
 
117
  if "accommodation" in feature["properties"]:
118
  formatted_item["accommodation"] = feature["properties"]["accommodation"]
119
+ formatted_item["map_url"] = get_google_map_url(place_lat, place_lon)
120
 
121
  formatted_data.append(formatted_item)
122
 
 
302
  if isinstance(obj, ObjectId):
303
  return str(obj)
304
  return obj
305
+
306
+
307
+ def get_google_map_url(lat, long):
308
+ return f"https://www.google.com/maps/search/{lat},{long}"
src/src/utils/mongo.py CHANGED
@@ -93,12 +93,24 @@ class MongoCRUD:
93
  async def update(self, query: Dict, data: Dict) -> int:
94
  """Update documents in the collection based on a query asynchronously."""
95
  await self._ensure_ttl_index()
96
- data["updated_at"] = get_date_time().replace(tzinfo=None)
97
- if self.ttl_seconds is not None:
98
- data["expire_at"] = data["updated_at"] + timedelta(seconds=self.ttl_seconds)
99
- update_data = self.model(**data).model_dump(exclude_unset=True)
100
- ordered_update = self._order_fields(update_data)
101
- result = await self.collection.update_many(query, {"$set": ordered_update})
 
 
 
 
 
 
 
 
 
 
 
 
102
  return result.modified_count
103
 
104
  async def delete(self, query: Dict) -> int:
@@ -106,6 +118,11 @@ class MongoCRUD:
106
  result = await self.collection.delete_many(query)
107
  return result.deleted_count
108
 
 
 
 
 
 
109
  async def find_by_id(self, id: str) -> Optional[Dict]:
110
  """Find a document by its ID asynchronously."""
111
  return await self.read_one({"_id": ObjectId(id)})
 
93
  async def update(self, query: Dict, data: Dict) -> int:
94
  """Update documents in the collection based on a query asynchronously."""
95
  await self._ensure_ttl_index()
96
+
97
+ # Check if update operators like $inc, $set, etc., are used
98
+ if any(key.startswith("$") for key in data.keys()):
99
+ update_data = data
100
+ else:
101
+ # If no MongoDB operators are used, treat it as a normal update
102
+ data["updated_at"] = get_date_time().replace(tzinfo=None)
103
+ if self.ttl_seconds is not None:
104
+ data["expire_at"] = data["updated_at"] + timedelta(
105
+ seconds=self.ttl_seconds
106
+ )
107
+ update_data = {
108
+ "$set": self._order_fields(
109
+ self.model(**data).model_dump(exclude_unset=True)
110
+ )
111
+ }
112
+
113
+ result = await self.collection.update_many(query, update_data)
114
  return result.modified_count
115
 
116
  async def delete(self, query: Dict) -> int:
 
118
  result = await self.collection.delete_many(query)
119
  return result.deleted_count
120
 
121
+ async def delete_one(self, query: Dict) -> int:
122
+ """Delete a single document from the collection based on a query asynchronously."""
123
+ result = await self.collection.delete_one(query)
124
+ return result.deleted_count
125
+
126
  async def find_by_id(self, id: str) -> Optional[Dict]:
127
  """Find a document by its ID asynchronously."""
128
  return await self.read_one({"_id": ObjectId(id)})
src/src/utils/redis.py CHANGED
@@ -1,19 +1,19 @@
1
  from src.langgraph.config.constant import RedisCfg
2
  import redis.asyncio as redis
3
 
4
- redis_client = redis.from_url(
5
- RedisCfg.REDIS_URL, encoding="utf-8", decode_responses=True
6
- )
7
 
8
 
9
  async def set_key_redis(key, value, time=300):
10
- await redis_client.set(key, value, time)
11
 
12
 
13
  async def get_key_redis(key):
14
 
15
- return await redis_client.get(key)
16
 
17
 
18
  async def delete_key_redis(key):
19
- await redis_client.delete(key)
 
1
  from src.langgraph.config.constant import RedisCfg
2
  import redis.asyncio as redis
3
 
4
+ # redis_client = redis.from_url(
5
+ # RedisCfg.REDIS_URL, encoding="utf-8", decode_responses=True
6
+ # )
7
 
8
 
9
  async def set_key_redis(key, value, time=300):
10
+ return None #redis_client.set(key, value, time)
11
 
12
 
13
  async def get_key_redis(key):
14
 
15
+ return None #await redis_client.get(key)
16
 
17
 
18
  async def delete_key_redis(key):
19
+ return None#redis_client.delete(key)