subashpoudel commited on
Commit
0b2c9fd
·
1 Parent(s): abd776c

next commit

Browse files
README.md CHANGED
@@ -1,6 +1,6 @@
1
 
2
  ---
3
- title: trygithubaction
4
  sdk: docker
5
  emoji: 🚀
6
  colorFrom: blue
 
1
 
2
  ---
3
+ title: rt-genai-fastapi
4
  sdk: docker
5
  emoji: 🚀
6
  colorFrom: blue
api/routers/analytics_chatbot.py CHANGED
@@ -10,17 +10,16 @@ from api.stored_data import stored_data
10
  from src.genai.analytics_chatbot.agent import ChatbotAgent
11
  from src.genai.analytics_chatbot.utils.utils import process_query
12
  from api.schemas.analytics_chatbot import UserMessage
 
 
13
 
14
  app_logger = logging.getLogger("app_logger")
15
  error_logger = logging.getLogger("error_logger")
16
- password=os.environ['REDIS_PASSWORD']
17
- host = os.environ['REDIS_HOST']
18
- port = os.environ['REDIS_PORT']
19
 
20
  router = APIRouter()
21
  agent=ChatbotAgent()
22
  graph = agent.chatbot_graph()
23
- redis_client = Redis(host=host, port=port, db=0, decode_responses=True, username="default",password=password)
24
 
25
  @router.get("/analytics-chatbot")
26
  def get_analytics(msg:str):
@@ -45,6 +44,5 @@ def get_analytics(msg:str):
45
  }
46
 
47
  # Save to Redis with TTL (e.g., 1 hour)
48
- redis_client.set(cache_key, json.dumps(response_to_cache), ex=60) # ex=seconds
49
-
50
  return response_to_cache
 
10
  from src.genai.analytics_chatbot.agent import ChatbotAgent
11
  from src.genai.analytics_chatbot.utils.utils import process_query
12
  from api.schemas.analytics_chatbot import UserMessage
13
+ from config.redis_config import redis_client
14
+
15
 
16
  app_logger = logging.getLogger("app_logger")
17
  error_logger = logging.getLogger("error_logger")
18
+
 
 
19
 
20
  router = APIRouter()
21
  agent=ChatbotAgent()
22
  graph = agent.chatbot_graph()
 
23
 
24
  @router.get("/analytics-chatbot")
25
  def get_analytics(msg:str):
 
44
  }
45
 
46
  # Save to Redis with TTL (e.g., 1 hour)
47
+ redis_client.set(cache_key, json.dumps(response_to_cache), ex=1) # ex=seconds
 
48
  return response_to_cache
app.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import FastAPI, Request
2
- from logger_config import setup_loggers
3
  import logging
4
  from api.routers import orchestration, context_analysis, ideation , brainstorm , generate_final_story , generate_image, show_analytics, analytics_chatbot
5
 
 
1
  from fastapi import FastAPI, Request
2
+ from config.logger_config import setup_loggers
3
  import logging
4
  from api.routers import orchestration, context_analysis, ideation , brainstorm , generate_final_story , generate_image, show_analytics, analytics_chatbot
5
 
config/__init__.py ADDED
File without changes
config/logger_config.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # logger_config.py
2
+ import logging
3
+ import os
4
+
5
+ class RelativePathFormatter(logging.Formatter):
6
+ def format(self, record):
7
+ record.relpath = os.path.relpath(record.pathname, os.getcwd())
8
+ return super().format(record)
9
+
10
+ def setup_loggers():
11
+ os.makedirs("logs", exist_ok=True)
12
+
13
+ # === Format ===
14
+ # formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
15
+ formatter = RelativePathFormatter("%(asctime)s | %(levelname)s | %(name)s | %(relpath)s:%(lineno)d | %(message)s")
16
+
17
+ # === App Logger ===
18
+ app_logger = logging.getLogger("app_logger")
19
+ app_handler = logging.FileHandler("logs/app.log")
20
+ app_handler.setLevel(logging.INFO)
21
+ app_handler.setFormatter(formatter)
22
+ app_logger.setLevel(logging.INFO)
23
+ app_logger.addHandler(app_handler)
24
+
25
+ # === Error Logger ===
26
+ error_logger = logging.getLogger("error_logger")
27
+ error_handler = logging.FileHandler("logs/errors.log")
28
+ error_handler.setLevel(logging.ERROR)
29
+ error_handler.setFormatter(formatter)
30
+ error_logger.setLevel(logging.ERROR)
31
+ error_logger.addHandler(error_handler)
32
+
33
+ # === Access Logger === (optional for request logs)
34
+ access_logger = logging.getLogger("access_logger")
35
+ access_handler = logging.FileHandler("logs/access.log")
36
+ access_handler.setLevel(logging.INFO)
37
+ access_handler.setFormatter(formatter)
38
+ access_logger.setLevel(logging.INFO)
39
+ access_logger.addHandler(access_handler)
40
+
41
+
42
+ # === Warning Logger ===
43
+ warning_logger = logging.getLogger("warning_logger")
44
+ warning_handler = logging.FileHandler("logs/warnings.log")
45
+ warning_handler.setLevel(logging.WARNING) # WARNING, ERROR, CRITICAL
46
+ warning_handler.setFormatter(formatter)
47
+ warning_logger.setLevel(logging.WARNING)
48
+ warning_logger.addHandler(warning_handler)
49
+
50
+
51
+ # Optional: also log to console
52
+ console_handler = logging.StreamHandler()
53
+ console_handler.setFormatter(formatter)
54
+ app_logger.addHandler(console_handler)
55
+ error_logger.addHandler(console_handler)
56
+ access_logger.addHandler(console_handler)
57
+ warning_logger.addHandler(console_handler)
58
+
config/redis_config.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from redis import Redis
2
+ import os
3
+
4
+ # Load Redis credentials from environment
5
+ password = os.environ['REDIS_PASSWORD']
6
+ host = os.environ['REDIS_HOST']
7
+ port = os.environ.get('REDIS_PORT', 6379) # default fallback
8
+
9
+ # Initialize Redis client
10
+ redis_client = Redis(
11
+ host=host,
12
+ port=port,
13
+ db=0,
14
+ decode_responses=True,
15
+ username="default",
16
+ password=password
17
+ )
logger_config.py CHANGED
@@ -1,58 +1,58 @@
1
- # logger_config.py
2
- import logging
3
- import os
4
-
5
- class RelativePathFormatter(logging.Formatter):
6
- def format(self, record):
7
- record.relpath = os.path.relpath(record.pathname, os.getcwd())
8
- return super().format(record)
9
 
10
- def setup_loggers():
11
- os.makedirs("logs", exist_ok=True)
12
-
13
- # === Format ===
14
- # formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
15
- formatter = RelativePathFormatter("%(asctime)s | %(levelname)s | %(name)s | %(relpath)s:%(lineno)d | %(message)s")
16
-
17
- # === App Logger ===
18
- app_logger = logging.getLogger("app_logger")
19
- app_handler = logging.FileHandler("logs/app.log")
20
- app_handler.setLevel(logging.INFO)
21
- app_handler.setFormatter(formatter)
22
- app_logger.setLevel(logging.INFO)
23
- app_logger.addHandler(app_handler)
24
-
25
- # === Error Logger ===
26
- error_logger = logging.getLogger("error_logger")
27
- error_handler = logging.FileHandler("logs/errors.log")
28
- error_handler.setLevel(logging.ERROR)
29
- error_handler.setFormatter(formatter)
30
- error_logger.setLevel(logging.ERROR)
31
- error_logger.addHandler(error_handler)
32
-
33
- # === Access Logger === (optional for request logs)
34
- access_logger = logging.getLogger("access_logger")
35
- access_handler = logging.FileHandler("logs/access.log")
36
- access_handler.setLevel(logging.INFO)
37
- access_handler.setFormatter(formatter)
38
- access_logger.setLevel(logging.INFO)
39
- access_logger.addHandler(access_handler)
40
-
41
-
42
- # === Warning Logger ===
43
- warning_logger = logging.getLogger("warning_logger")
44
- warning_handler = logging.FileHandler("logs/warnings.log")
45
- warning_handler.setLevel(logging.WARNING) # WARNING, ERROR, CRITICAL
46
- warning_handler.setFormatter(formatter)
47
- warning_logger.setLevel(logging.WARNING)
48
- warning_logger.addHandler(warning_handler)
49
-
50
-
51
- # Optional: also log to console
52
- console_handler = logging.StreamHandler()
53
- console_handler.setFormatter(formatter)
54
- app_logger.addHandler(console_handler)
55
- error_logger.addHandler(console_handler)
56
- access_logger.addHandler(console_handler)
57
- warning_logger.addHandler(console_handler)
58
 
 
1
+ # # logger_config.py
2
+ # import logging
3
+ # import os
4
+
5
+ # class RelativePathFormatter(logging.Formatter):
6
+ # def format(self, record):
7
+ # record.relpath = os.path.relpath(record.pathname, os.getcwd())
8
+ # return super().format(record)
9
 
10
+ # def setup_loggers():
11
+ # os.makedirs("logs", exist_ok=True)
12
+
13
+ # # === Format ===
14
+ # # formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
15
+ # formatter = RelativePathFormatter("%(asctime)s | %(levelname)s | %(name)s | %(relpath)s:%(lineno)d | %(message)s")
16
+
17
+ # # === App Logger ===
18
+ # app_logger = logging.getLogger("app_logger")
19
+ # app_handler = logging.FileHandler("logs/app.log")
20
+ # app_handler.setLevel(logging.INFO)
21
+ # app_handler.setFormatter(formatter)
22
+ # app_logger.setLevel(logging.INFO)
23
+ # app_logger.addHandler(app_handler)
24
+
25
+ # # === Error Logger ===
26
+ # error_logger = logging.getLogger("error_logger")
27
+ # error_handler = logging.FileHandler("logs/errors.log")
28
+ # error_handler.setLevel(logging.ERROR)
29
+ # error_handler.setFormatter(formatter)
30
+ # error_logger.setLevel(logging.ERROR)
31
+ # error_logger.addHandler(error_handler)
32
+
33
+ # # === Access Logger === (optional for request logs)
34
+ # access_logger = logging.getLogger("access_logger")
35
+ # access_handler = logging.FileHandler("logs/access.log")
36
+ # access_handler.setLevel(logging.INFO)
37
+ # access_handler.setFormatter(formatter)
38
+ # access_logger.setLevel(logging.INFO)
39
+ # access_logger.addHandler(access_handler)
40
+
41
+
42
+ # # === Warning Logger ===
43
+ # warning_logger = logging.getLogger("warning_logger")
44
+ # warning_handler = logging.FileHandler("logs/warnings.log")
45
+ # warning_handler.setLevel(logging.WARNING) # WARNING, ERROR, CRITICAL
46
+ # warning_handler.setFormatter(formatter)
47
+ # warning_logger.setLevel(logging.WARNING)
48
+ # warning_logger.addHandler(warning_handler)
49
+
50
+
51
+ # # Optional: also log to console
52
+ # console_handler = logging.StreamHandler()
53
+ # console_handler.setFormatter(formatter)
54
+ # app_logger.addHandler(console_handler)
55
+ # error_logger.addHandler(console_handler)
56
+ # access_logger.addHandler(console_handler)
57
+ # warning_logger.addHandler(console_handler)
58
 
logs/access.log CHANGED
@@ -1537,3 +1537,57 @@
1537
  2025-10-26 15:34:26,248 | INFO | access_logger | app.py:20 | Response status: 200
1538
  2025-10-26 15:41:29,547 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
1539
  2025-10-26 15:41:32,818 | INFO | access_logger | app.py:20 | Response status: 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1537
  2025-10-26 15:34:26,248 | INFO | access_logger | app.py:20 | Response status: 200
1538
  2025-10-26 15:41:29,547 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
1539
  2025-10-26 15:41:32,818 | INFO | access_logger | app.py:20 | Response status: 200
1540
+ 2025-10-27 12:15:41,325 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/
1541
+ 2025-10-27 12:15:41,326 | INFO | access_logger | app.py:20 | Response status: 200
1542
+ 2025-10-27 12:15:41,805 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/favicon.ico
1543
+ 2025-10-27 12:15:41,807 | INFO | access_logger | app.py:20 | Response status: 404
1544
+ 2025-10-27 12:15:44,388 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/docs
1545
+ 2025-10-27 12:15:44,389 | INFO | access_logger | app.py:20 | Response status: 200
1546
+ 2025-10-27 12:15:44,535 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/openapi.json
1547
+ 2025-10-27 12:15:44,543 | INFO | access_logger | app.py:20 | Response status: 200
1548
+ 2025-10-27 15:05:10,775 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=is%20the%20analytics%20of%20divya%20dhakal%20good%20or%20bad%3F
1549
+ 2025-10-27 15:05:27,085 | INFO | access_logger | app.py:20 | Response status: 200
1550
+ 2025-10-27 15:07:29,247 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=is%20the%20analytics%20of%20divya%20dhakal%20good%20or%20bad%3F
1551
+ 2025-10-27 15:07:31,099 | INFO | access_logger | app.py:20 | Response status: 200
1552
+ 2025-10-27 15:08:27,971 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=is%20the%20analytics%20of%20divya%20dhakal%20good%20or%20bad%3F
1553
+ 2025-10-27 15:08:29,879 | INFO | access_logger | app.py:20 | Response status: 200
1554
+ 2025-10-27 15:09:01,555 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=is%20the%20overall%20analytics%20of%20divya%20dhakal%20good%20or%20bad%3F
1555
+ 2025-10-27 15:09:08,484 | INFO | access_logger | app.py:20 | Response status: 200
1556
+ 2025-10-27 15:10:29,900 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Which%20influencer%20is%20more%20close%20to%20the%20audience%3F
1557
+ 2025-10-27 15:10:35,983 | INFO | access_logger | app.py:20 | Response status: 200
1558
+ 2025-10-27 15:17:11,285 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=is%20the%20analytics%20of%20divya%20dhakal%20good%20or%20bad%3F
1559
+ 2025-10-27 15:17:11,613 | INFO | access_logger | app.py:20 | Response status: 200
1560
+ 2025-10-27 15:17:49,986 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=which%20influencer%20is%20more%20close%20to%20the%20audience%3F
1561
+ 2025-10-27 15:18:00,905 | INFO | access_logger | app.py:20 | Response status: 200
1562
+ 2025-10-27 15:39:42,259 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=which%20influencer%20is%20more%20close%20to%20the%20audience%3F
1563
+ 2025-10-27 15:39:56,557 | INFO | access_logger | app.py:20 | Response status: 200
1564
+ 2025-10-28 13:44:52,420 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/
1565
+ 2025-10-28 13:44:52,420 | INFO | access_logger | app.py:20 | Response status: 200
1566
+ 2025-10-28 13:44:52,918 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/favicon.ico
1567
+ 2025-10-28 13:44:52,919 | INFO | access_logger | app.py:20 | Response status: 404
1568
+ 2025-10-28 13:44:55,371 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/docs
1569
+ 2025-10-28 13:44:55,372 | INFO | access_logger | app.py:20 | Response status: 200
1570
+ 2025-10-28 13:44:55,491 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/openapi.json
1571
+ 2025-10-28 13:44:55,497 | INFO | access_logger | app.py:20 | Response status: 200
1572
+ 2025-10-28 13:45:19,086 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Which%20influencer%20has%20got%20more%20audience%3F
1573
+ 2025-10-28 13:45:52,641 | INFO | access_logger | app.py:20 | Response status: 200
1574
+ 2025-10-28 13:54:45,938 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Which%20influencer%20has%20got%20more%20audience%3F
1575
+ 2025-10-28 13:54:54,946 | INFO | access_logger | app.py:20 | Response status: 200
1576
+ 2025-10-28 14:04:53,103 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Which%20influencer%20has%20got%20more%20audience%3F
1577
+ 2025-10-28 14:05:07,615 | INFO | access_logger | app.py:20 | Response status: 200
1578
+ 2025-10-28 14:06:20,111 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Which%20influencer%20has%20got%20more%20audience%3F
1579
+ 2025-10-28 14:06:29,195 | INFO | access_logger | app.py:20 | Response status: 200
1580
+ 2025-10-28 14:11:15,115 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Which%20influencer%20is%20good%20overall%3F
1581
+ 2025-10-28 14:11:27,010 | INFO | access_logger | app.py:20 | Response status: 200
1582
+ 2025-10-28 14:15:32,566 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20to%20compare%20the%20analytics%20of%20muna%20and%20divya
1583
+ 2025-10-28 15:04:15,698 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20to%20compare%20the%20analytics%20of%20muna%20and%20divya
1584
+ 2025-10-28 15:04:29,863 | INFO | access_logger | app.py:20 | Response status: 200
1585
+ 2025-10-29 11:44:41,699 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/
1586
+ 2025-10-29 11:44:41,700 | INFO | access_logger | app.py:20 | Response status: 200
1587
+ 2025-10-29 11:44:42,220 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/favicon.ico
1588
+ 2025-10-29 11:44:42,221 | INFO | access_logger | app.py:20 | Response status: 404
1589
+ 2025-10-29 11:44:44,605 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/docs
1590
+ 2025-10-29 11:44:44,606 | INFO | access_logger | app.py:20 | Response status: 200
1591
+ 2025-10-29 11:44:44,718 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/openapi.json
1592
+ 2025-10-29 11:44:44,726 | INFO | access_logger | app.py:20 | Response status: 200
1593
+ 2025-10-29 11:46:57,249 | INFO | access_logger | app.py:18 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=which%20influencer%20has%20the%20largest%20audeince%3F
src/genai/analytics_chatbot/agent.py CHANGED
@@ -1,7 +1,7 @@
1
  from langgraph.graph import StateGraph, START, END
2
  from langgraph.checkpoint.memory import MemorySaver
3
  from .utils.state import State
4
- from .utils.nodes import FetchDataNode , FetchLastMessage , RetrievePossibleEndpoints , FetchParametersNode , RetrieveExactEndpoint, BackupRetrievalNode, RoutingNode
5
 
6
  fetch_last_message_node = FetchLastMessage()
7
  retrieve_api_endpoints_node = RetrievePossibleEndpoints()
@@ -9,6 +9,7 @@ retrieve_exact_endpoint_node = RetrieveExactEndpoint()
9
  fetch_parameters_node = FetchParametersNode()
10
  fetch_data_node = FetchDataNode()
11
  backup_response_node = BackupRetrievalNode()
 
12
 
13
  class ChatbotAgent:
14
  def __init__(self):
@@ -19,19 +20,23 @@ class ChatbotAgent:
19
  graph_builder.add_node("fetch_last_message", fetch_last_message_node.run)
20
  graph_builder.add_node("retrieve_api_endpoints", retrieve_api_endpoints_node.run)
21
  graph_builder.add_node("retrieve_exact_endpoint", retrieve_exact_endpoint_node.run)
 
 
22
  graph_builder.add_node("fetch_parameters", fetch_parameters_node.run)
23
  graph_builder.add_node("fetch_data", fetch_data_node.run)
24
  graph_builder.add_node("backup_response", backup_response_node.run)
 
25
 
26
  graph_builder.add_edge(START, "fetch_last_message")
27
  graph_builder.add_edge("fetch_last_message", 'retrieve_api_endpoints')
28
  graph_builder.add_edge("retrieve_api_endpoints", 'retrieve_exact_endpoint')
29
- graph_builder.add_edge("retrieve_exact_endpoint", 'fetch_parameters')
 
30
  graph_builder.add_edge("fetch_parameters", 'fetch_data')
31
  graph_builder.add_edge("fetch_data", END)
32
  graph_builder.add_edge("backup_response", END)
33
 
34
- graph_builder.add_conditional_edges("fetch_parameters", RoutingNode().run,{'execute_backup':'backup_response', 'go_on':"fetch_data"})
35
- graph_builder.add_conditional_edges("fetch_data", RoutingNode().run,{'execute_backup':'backup_response', 'go_on':END})
36
 
37
  return graph_builder.compile(checkpointer=self.memory)
 
1
  from langgraph.graph import StateGraph, START, END
2
  from langgraph.checkpoint.memory import MemorySaver
3
  from .utils.state import State
4
+ from .utils.nodes import FetchDataNode , FetchLastMessage , RetrievePossibleEndpoints , FetchParametersNode , RetrieveExactEndpoint, BackupRetrievalNode, BackupRoutingNode, QueryCheckNode
5
 
6
  fetch_last_message_node = FetchLastMessage()
7
  retrieve_api_endpoints_node = RetrievePossibleEndpoints()
 
9
  fetch_parameters_node = FetchParametersNode()
10
  fetch_data_node = FetchDataNode()
11
  backup_response_node = BackupRetrievalNode()
12
+ query_check_node = QueryCheckNode()
13
 
14
  class ChatbotAgent:
15
  def __init__(self):
 
20
  graph_builder.add_node("fetch_last_message", fetch_last_message_node.run)
21
  graph_builder.add_node("retrieve_api_endpoints", retrieve_api_endpoints_node.run)
22
  graph_builder.add_node("retrieve_exact_endpoint", retrieve_exact_endpoint_node.run)
23
+ graph_builder.add_node("check_query_type", query_check_node.run)
24
+
25
  graph_builder.add_node("fetch_parameters", fetch_parameters_node.run)
26
  graph_builder.add_node("fetch_data", fetch_data_node.run)
27
  graph_builder.add_node("backup_response", backup_response_node.run)
28
+
29
 
30
  graph_builder.add_edge(START, "fetch_last_message")
31
  graph_builder.add_edge("fetch_last_message", 'retrieve_api_endpoints')
32
  graph_builder.add_edge("retrieve_api_endpoints", 'retrieve_exact_endpoint')
33
+ graph_builder.add_edge("retrieve_exact_endpoint", 'check_query_type')
34
+ graph_builder.add_edge("check_query_type", 'fetch_parameters')
35
  graph_builder.add_edge("fetch_parameters", 'fetch_data')
36
  graph_builder.add_edge("fetch_data", END)
37
  graph_builder.add_edge("backup_response", END)
38
 
39
+ graph_builder.add_conditional_edges("fetch_parameters", BackupRoutingNode().run,{'execute_backup':'backup_response', 'go_on':"fetch_data"})
40
+ graph_builder.add_conditional_edges("fetch_data", BackupRoutingNode().run,{'execute_backup':'backup_response', 'go_on':END})
41
 
42
  return graph_builder.compile(checkpointer=self.memory)
src/genai/analytics_chatbot/utils/nodes.py CHANGED
@@ -3,7 +3,7 @@ from langchain_core.messages import SystemMessage , HumanMessage , FunctionMessa
3
  from .state import State
4
  from .tools import RetrieverBackup
5
  from .schemas import ResponseFormatter , CompareBodyFormatter, ParameterFormatter, EndpointFormatter
6
- from .prompts import chatbot_prompt , get_body_prompt , fetch_last_message_prompt , fetch_parameters_prompt, fetch_endpoint_prompt, backup_retrieval_prompt
7
  from .utils import generate_api_knowledge , process_query, get_endpoint_info
8
  from src.genai.utils.models_loader import llm_gpt
9
  import numpy as np
@@ -37,7 +37,7 @@ class RetrievePossibleEndpoints:
37
  def run(self,state:State):
38
  print('Gone to retrieve possible endpoints')
39
  query_embedding = np.array(embedding_model.embed_query(state['latest_message'])).reshape(1, -1).astype('float32')
40
- distances, indices = self.index.search(query_embedding, 3)
41
  for idx in indices[0]:
42
  row = self.df.iloc[idx]
43
  print('Endpoint:',row['endpoint'])
@@ -69,7 +69,24 @@ class RetrieveExactEndpoint:
69
  "needed_parameters": endpoint_info["parameters"]
70
  }
71
 
72
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  class FetchParametersNode:
74
  def __init__(self):
75
  self.llm = llm_gpt
@@ -78,25 +95,25 @@ class FetchParametersNode:
78
  try:
79
  print('Entered to fetch parameters')
80
  print(state['method'])
81
- if state['method'] == 'GET':
82
- print('Condition satisfied')
83
  template = fetch_parameters_prompt
84
- messages=[SystemMessage(content=template),
85
- HumanMessage(content=f'''The query is: {state['latest_message']}\n. The needed parameters: {str(state['needed_parameters'])}''')
86
- ]
87
  print('messages:', messages)
88
  result = self.llm.with_structured_output(ParameterFormatter, method='function_calling').invoke(messages)
89
- # parameters_values={key: process_query(value) for key, value in result.parameters_values.items()}
90
  parameters_values = {k: (process_query(v) if isinstance(v, str) else v) for k, v in result.parameters_values.items()}
91
-
92
- print('The parameter values:', parameters_values)
93
- return {
94
- 'parameters_values':parameters_values
95
- }
96
- else:
97
- return{
98
- 'parameters_values': {}
99
- }
 
 
 
100
  except Exception as e:
101
  print('Error occoured:', e)
102
  return {'error_message': str(e)}
@@ -116,9 +133,8 @@ class FetchDataNode:
116
  try:
117
  print('Entered to fetch data')
118
  url = f'''{self.base_url}{state['endpoint']}'''
119
- if state['method'] == 'GET':
120
- response = requests.get(url, params=state['parameters_values'],headers=self.headers)
121
- elif state['endpoint'] == '/api/v1/compare/':
122
  print('Condition satisfied')
123
  messages = [SystemMessage(content=get_body_prompt()),
124
  HumanMessage(content=str(state['messages']))]
@@ -136,8 +152,29 @@ class FetchDataNode:
136
  }
137
 
138
  response = requests.post(url, json=payload, headers=headers)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- return {'response':response.json()}
141
  except Exception as e:
142
  print('Error occoured:', e)
143
  return {'error_message': str(e)}
@@ -151,7 +188,9 @@ class BackupRetrievalNode:
151
  retrieval=RetrieverBackup().retrieve(state['latest_message'])
152
  return {'backup_data': retrieval}
153
 
154
- class RoutingNode:
 
 
155
  def __init__(self):
156
  pass
157
 
 
3
  from .state import State
4
  from .tools import RetrieverBackup
5
  from .schemas import ResponseFormatter , CompareBodyFormatter, ParameterFormatter, EndpointFormatter
6
+ from .prompts import query_check_prompt , get_body_prompt , fetch_last_message_prompt , fetch_parameters_prompt, fetch_endpoint_prompt, backup_retrieval_prompt
7
  from .utils import generate_api_knowledge , process_query, get_endpoint_info
8
  from src.genai.utils.models_loader import llm_gpt
9
  import numpy as np
 
37
  def run(self,state:State):
38
  print('Gone to retrieve possible endpoints')
39
  query_embedding = np.array(embedding_model.embed_query(state['latest_message'])).reshape(1, -1).astype('float32')
40
+ distances, indices = self.index.search(query_embedding, 5)
41
  for idx in indices[0]:
42
  row = self.df.iloc[idx]
43
  print('Endpoint:',row['endpoint'])
 
69
  "needed_parameters": endpoint_info["parameters"]
70
  }
71
 
72
+ class QueryCheckNode:
73
+ def __init__(self):
74
+ self.llm = llm_gpt
75
+
76
+ def run(self, state:State):
77
+ try:
78
+ print('Entered to query checking')
79
+ messages = [SystemMessage(content=query_check_prompt),
80
+ HumanMessage(content=f'''The user query is: {state['latest_message']}''')]
81
+ result = self.llm.invoke(messages)
82
+ print(result.content)
83
+ return{'query_type': result.content}
84
+
85
+ except Exception as e:
86
+ print('Error occoured:', e)
87
+ return {'error_message': str(e)}
88
+
89
+
90
  class FetchParametersNode:
91
  def __init__(self):
92
  self.llm = llm_gpt
 
95
  try:
96
  print('Entered to fetch parameters')
97
  print(state['method'])
98
+
99
+ if state['method']=='GET':
100
  template = fetch_parameters_prompt
101
+ messages=[SystemMessage(content=template),HumanMessage(content=f'''The query is: {state['latest_message']}\n. The needed parameters: {str(state['needed_parameters'])}''')]
 
 
102
  print('messages:', messages)
103
  result = self.llm.with_structured_output(ParameterFormatter, method='function_calling').invoke(messages)
 
104
  parameters_values = {k: (process_query(v) if isinstance(v, str) else v) for k, v in result.parameters_values.items()}
105
+
106
+ if 'single_influencer_query' in state['query_type']:
107
+ print('The parameter values:', parameters_values)
108
+ return {
109
+ 'parameters_values':parameters_values
110
+ }
111
+ elif 'aggregate_query' in state['query_type']:
112
+ parameters_values['influencer_username'] = ['divyadhakal_','munachiya','mydarlingfood','_its.me.muskan_']
113
+ print('The parameter values:', parameters_values)
114
+ return{
115
+ 'parameters_values': parameters_values
116
+ }
117
  except Exception as e:
118
  print('Error occoured:', e)
119
  return {'error_message': str(e)}
 
133
  try:
134
  print('Entered to fetch data')
135
  url = f'''{self.base_url}{state['endpoint']}'''
136
+
137
+ if state['endpoint'] == '/api/v1/compare/':
 
138
  print('Condition satisfied')
139
  messages = [SystemMessage(content=get_body_prompt()),
140
  HumanMessage(content=str(state['messages']))]
 
152
  }
153
 
154
  response = requests.post(url, json=payload, headers=headers)
155
+ return {'response': response.json()}
156
+
157
+ elif 'single_influencer_query' in state['query_type']:
158
+ response = requests.get(url, params=state['parameters_values'],headers=self.headers)
159
+ return {'response':response.json()}
160
+
161
+ elif 'aggregate_query' in state['query_type']:
162
+ print('Entered to aggregrated query execution')
163
+ print(state['parameters_values'])
164
+ params = state["parameters_values"]
165
+ if "influencer_username" in params and isinstance(params["influencer_username"], list):
166
+ results = {}
167
+
168
+ # Iterate through each influencer username
169
+ for username in params["influencer_username"]:
170
+ current_params = params.copy()
171
+ current_params["influencer_username"] = username
172
+
173
+ response = requests.get(url, params=current_params, headers=self.headers)
174
+ results[username] = response.json() # Store influencer-wise response
175
+
176
+ return {"response": results}
177
 
 
178
  except Exception as e:
179
  print('Error occoured:', e)
180
  return {'error_message': str(e)}
 
188
  retrieval=RetrieverBackup().retrieve(state['latest_message'])
189
  return {'backup_data': retrieval}
190
 
191
+
192
+
193
+ class BackupRoutingNode:
194
  def __init__(self):
195
  pass
196
 
src/genai/analytics_chatbot/utils/prompts.py CHANGED
@@ -54,15 +54,16 @@ Return the result strictly in this JSON format:
54
  "names": ["<influencer_1>", "<influencer_2>", ...],
55
  "frequency": "<frequency_value>"
56
  }
 
57
 
58
  Example:
59
- If the query is :"I want to compare the analytics of divyadhakal_ and munachiya in weekly basis", then
60
-
61
- Then the expected output is:
62
  {
63
  "names": ["divyadhakal_", "munachiya"],
64
- "frequency": "weekly"
65
  }
 
66
  '''
67
 
68
  fetch_last_message_prompt = '''
@@ -110,4 +111,14 @@ endpoint: /api/v1/analytics/engagement
110
  backup_retrieval_prompt = '''
111
  You are provided with the retrieved data as a function message and the user query.
112
  Respond to the user query only through the context of retrieved data. Don't give hallucinated responses.
 
 
 
 
 
 
 
 
 
 
113
  '''
 
54
  "names": ["<influencer_1>", "<influencer_2>", ...],
55
  "frequency": "<frequency_value>"
56
  }
57
+ If the frequency is not provided to you, use 'weekly' as default.
58
 
59
  Example:
60
+ If the query is :"I want to compare the analytics of divyadhakal_ and munachiya in monthly basis", then
61
+ output:
 
62
  {
63
  "names": ["divyadhakal_", "munachiya"],
64
+ "frequency": "monthly"
65
  }
66
+
67
  '''
68
 
69
  fetch_last_message_prompt = '''
 
111
  backup_retrieval_prompt = '''
112
  You are provided with the retrieved data as a function message and the user query.
113
  Respond to the user query only through the context of retrieved data. Don't give hallucinated responses.
114
+ '''
115
+
116
+ query_check_prompt = '''
117
+ You are an intent classification assistant.
118
+ Given a user query about influencer analytics, classify it as one of the following types:
119
+
120
+ 1. single_influencer_query — if the query refers to one specific influencer (e.g., "Show Muna's engagement rate"). Simply if the query includes the name of influencer.
121
+ 2. aggregate_query — if the query involves comparing multiple influencers, rankings, or overall statistics (e.g., "Who has the highest engagement?").
122
+
123
+ Return only one label: "single_influencer_query" or "aggregate_query".
124
  '''
src/genai/analytics_chatbot/utils/state.py CHANGED
@@ -12,4 +12,5 @@ class State(TypedDict):
12
  error_message:str
13
  latest_message:str
14
  parameters_values:str
15
- backup_data:str
 
 
12
  error_message:str
13
  latest_message:str
14
  parameters_values:str
15
+ backup_data:str
16
+ query_type: str
src/genai/analytics_chatbot/utils/tools.py CHANGED
@@ -35,34 +35,6 @@ class RetrieverBackup:
35
  else:
36
  return self.df
37
 
38
-
39
- def retrieve_old(self, query):
40
- query_embedding = np.array(embedding_model.embed_query(str(query))).reshape(1, -1).astype('float32')
41
- print('Embeddings Generated')
42
- faiss.normalize_L2(query_embedding)
43
- print('Query embedded')
44
- filtered_df = self._filter_dataset(query)
45
- distances, indices = self.index.search(query_embedding, len(filtered_df))
46
- similarity_threshold = 0.35
47
- selected = [(idx, sim) for idx, sim in zip(indices[0], distances[0]) if sim >= similarity_threshold]
48
- if not selected:
49
- return "No influencers found."
50
-
51
- outer_list = []
52
- for rank, (idx, sim) in enumerate(selected, 1):
53
- row = filtered_df.iloc[idx]
54
- inner_list = [
55
- f"[{rank}]. The influencer name is: **{row['username']}** — Likes: **{row['likesCount']}**, Comments: **{row['commentCount']}**",
56
- f"The branding or promotion done is:\n{row['visible_texts_or_brandings']}",
57
- f"The details of product or service is:\n{row['product_or_service_details']}"
58
- ]
59
- outer_list.append(inner_list)
60
-
61
- cleaned_response = clean_text(str(outer_list))
62
- print('response cleaned')
63
- tokens = encoding_model.encode(cleaned_response)[:500]
64
- print('tokens got')
65
- return encoding_model.decode(tokens)
66
 
67
  def retrieve(self, query):
68
  query_embedding = np.array(embedding_model.embed_query(str(query))).reshape(1, -1).astype('float32')
@@ -70,7 +42,7 @@ class RetrieverBackup:
70
 
71
  # Search on full dataset (index is built on full df)
72
  distances, indices = self.index.search(query_embedding, len(self.df))
73
- similarity_threshold = 0.35
74
 
75
  # Prepare matched usernames
76
  usernames = self.df["username"].dropna().unique()
 
35
  else:
36
  return self.df
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  def retrieve(self, query):
40
  query_embedding = np.array(embedding_model.embed_query(str(query))).reshape(1, -1).astype('float32')
 
42
 
43
  # Search on full dataset (index is built on full df)
44
  distances, indices = self.index.search(query_embedding, len(self.df))
45
+ similarity_threshold = 0.1
46
 
47
  # Prepare matched usernames
48
  usernames = self.df["username"].dropna().unique()
src/genai/analytics_chatbot/utils/utils.py CHANGED
@@ -103,3 +103,6 @@ def process_query(user_query: str) -> str:
103
 
104
 
105
 
 
 
 
 
103
 
104
 
105
 
106
+
107
+
108
+
src/genai/utils/models_loader.py CHANGED
@@ -24,6 +24,7 @@ llm_gpt_small = ChatOpenAI(model="gpt-3.5-turbo",temperature=0.3)
24
  llm_gpt = ChatOpenAI(model="gpt-3.5-turbo",temperature=0.3)
25
  llm_gpt_high = ChatOpenAI(model="gpt-5-nano",temperature=0.5)
26
  encoding_model = tiktoken.encoding_for_model('gpt-4o-mini')
 
27
 
28
 
29
  captioning_model = "meta-llama/llama-4-scout-17b-16e-instruct"
 
24
  llm_gpt = ChatOpenAI(model="gpt-3.5-turbo",temperature=0.3)
25
  llm_gpt_high = ChatOpenAI(model="gpt-5-nano",temperature=0.5)
26
  encoding_model = tiktoken.encoding_for_model('gpt-4o-mini')
27
+ # encoding_model = 'encoding_model'
28
 
29
 
30
  captioning_model = "meta-llama/llama-4-scout-17b-16e-instruct"