Spaces:
Sleeping
Sleeping
Commit
·
6f57d05
1
Parent(s):
adc3455
Refined chatbot
Browse files- .gitignore +0 -1
- api/__pycache__/main.cpython-313.pyc +0 -0
- api/routers/analytics_chatbot.py +1 -0
- logs/access.log +72 -0
- logs/app.log +1 -0
- logs/errors.log +3 -0
- requirements.txt +1 -2
- src/genai/analytics_chatbot/utils/nodes.py +6 -6
- src/genai/analytics_chatbot/utils/streamlit_app.py +1 -1
- src/genai/business_interaction_agent/__init__.py +0 -0
- src/genai/business_interaction_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/__pycache__/agent.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/agent.py +0 -82
- src/genai/business_interaction_agent/utils/__init__.py +0 -0
- src/genai/business_interaction_agent/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/utils/__pycache__/nodes.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/utils/__pycache__/prompts.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/utils/__pycache__/state.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/utils/__pycache__/utils.cpython-312.pyc +0 -0
- src/genai/business_interaction_agent/utils/nodes.py +0 -20
- src/genai/business_interaction_agent/utils/prompts.py +0 -78
- src/genai/business_interaction_agent/utils/state.py +0 -18
- src/genai/business_interaction_agent/utils/tools.py +0 -0
- src/genai/business_interaction_agent/utils/utils.py +0 -55
- src/genai/orchestration_agent/__pycache__/agent.cpython-313.pyc +0 -0
- src/genai/orchestration_agent/agent.py +2 -2
- src/genai/orchestration_agent/utils/__pycache__/nodes.cpython-313.pyc +0 -0
- src/genai/orchestration_agent/utils/__pycache__/prompts.cpython-313.pyc +0 -0
- src/genai/orchestration_agent/utils/nodes.py +10 -3
- src/genai/orchestration_agent/utils/prompts.py +0 -85
- src/genai/orchestration_agent/utils/tools.py +9 -4
- src/genai/utils/__pycache__/models_loader.cpython-313.pyc +0 -0
- src/genai/utils/models_loader.py +6 -3
.gitignore
CHANGED
|
@@ -3,4 +3,3 @@ myenv
|
|
| 3 |
*.pyc
|
| 4 |
__pycache__/
|
| 5 |
logs
|
| 6 |
-
# src/genai/utils/data/ideas.csv
|
|
|
|
| 3 |
*.pyc
|
| 4 |
__pycache__/
|
| 5 |
logs
|
|
|
api/__pycache__/main.cpython-313.pyc
CHANGED
|
Binary files a/api/__pycache__/main.cpython-313.pyc and b/api/__pycache__/main.cpython-313.pyc differ
|
|
|
api/routers/analytics_chatbot.py
CHANGED
|
@@ -23,6 +23,7 @@ def get_analytics(msg:str):
|
|
| 23 |
result=graph.invoke({'messages':user_query},config=config)
|
| 24 |
return {
|
| 25 |
'response': result['response'],
|
|
|
|
| 26 |
}
|
| 27 |
except Exception as e:
|
| 28 |
print(e)
|
|
|
|
| 23 |
result=graph.invoke({'messages':user_query},config=config)
|
| 24 |
return {
|
| 25 |
'response': result['response'],
|
| 26 |
+
'endpoint': result['endpoint']
|
| 27 |
}
|
| 28 |
except Exception as e:
|
| 29 |
print(e)
|
logs/access.log
CHANGED
|
@@ -1173,3 +1173,75 @@
|
|
| 1173 |
2025-09-21 10:33:52,136 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1174 |
2025-09-21 10:35:04,244 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1175 |
2025-09-21 10:35:07,706 | INFO | access_logger | api/main.py:21 | Response status: 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1173 |
2025-09-21 10:33:52,136 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1174 |
2025-09-21 10:35:04,244 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1175 |
2025-09-21 10:35:07,706 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1176 |
+
2025-09-21 12:25:13,482 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1177 |
+
2025-09-21 12:25:20,812 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1178 |
+
2025-10-09 11:29:06,431 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/
|
| 1179 |
+
2025-10-09 11:29:06,431 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1180 |
+
2025-10-09 11:29:06,497 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/favicon.ico
|
| 1181 |
+
2025-10-09 11:29:06,497 | INFO | access_logger | api/main.py:21 | Response status: 404
|
| 1182 |
+
2025-10-09 11:29:09,408 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/docs
|
| 1183 |
+
2025-10-09 11:29:09,409 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1184 |
+
2025-10-09 11:29:09,709 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/openapi.json
|
| 1185 |
+
2025-10-09 11:29:09,716 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1186 |
+
2025-10-09 11:29:33,986 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1187 |
+
2025-10-09 11:29:35,203 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1188 |
+
2025-10-09 11:30:40,745 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1189 |
+
2025-10-09 11:31:20,574 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1190 |
+
2025-10-09 11:34:25,717 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1191 |
+
2025-10-09 11:35:38,452 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1192 |
+
2025-10-09 11:41:36,255 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1193 |
+
2025-10-09 11:44:49,971 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1194 |
+
2025-10-09 11:45:20,930 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1195 |
+
2025-10-09 11:47:44,308 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1196 |
+
2025-10-09 11:47:46,146 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1197 |
+
2025-10-09 11:49:44,607 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20divya%20dhakal
|
| 1198 |
+
2025-10-09 11:49:51,922 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1199 |
+
2025-10-09 11:51:40,929 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20divya%20dhakal
|
| 1200 |
+
2025-10-09 11:51:42,754 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1201 |
+
2025-10-09 11:54:58,936 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20influencer%20divya%20dhakal
|
| 1202 |
+
2025-10-09 11:55:00,225 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1203 |
+
2025-10-09 11:56:35,510 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20influencer%20divya%20dhakal
|
| 1204 |
+
2025-10-09 11:56:38,938 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1205 |
+
2025-10-09 11:59:38,677 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20influencer%20divya%20dhakal
|
| 1206 |
+
2025-10-09 11:59:41,652 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1207 |
+
2025-10-09 12:00:08,823 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1208 |
+
2025-10-09 12:03:05,428 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20influencer%20divya%20dhakal
|
| 1209 |
+
2025-10-09 12:03:10,298 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1210 |
+
2025-10-09 12:12:13,576 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=Give%20me%20engagement%20of%20influencer%20divya%20dhakal
|
| 1211 |
+
2025-10-09 12:12:25,185 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1212 |
+
2025-10-09 12:18:19,115 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1213 |
+
2025-10-09 12:22:14,664 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1214 |
+
2025-10-09 12:48:55,306 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1215 |
+
2025-10-09 12:52:35,834 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1216 |
+
2025-10-09 12:53:59,306 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1217 |
+
2025-10-09 13:07:48,611 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1218 |
+
2025-10-09 13:14:37,411 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/
|
| 1219 |
+
2025-10-09 13:14:37,412 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1220 |
+
2025-10-09 13:14:40,403 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/docs
|
| 1221 |
+
2025-10-09 13:14:40,403 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1222 |
+
2025-10-09 13:14:40,534 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/openapi.json
|
| 1223 |
+
2025-10-09 13:14:40,541 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1224 |
+
2025-10-09 13:14:52,317 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
| 1225 |
+
2025-10-09 13:17:42,737 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/show-analytics
|
| 1226 |
+
2025-10-09 13:31:56,267 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/show-analytics
|
| 1227 |
+
2025-10-09 13:43:59,120 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1228 |
+
2025-10-09 13:44:01,201 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1229 |
+
2025-10-09 13:45:39,951 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1230 |
+
2025-10-09 13:48:18,674 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/
|
| 1231 |
+
2025-10-09 13:48:18,675 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1232 |
+
2025-10-09 13:48:22,486 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/docs
|
| 1233 |
+
2025-10-09 13:48:22,487 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1234 |
+
2025-10-09 13:48:22,524 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/openapi.json
|
| 1235 |
+
2025-10-09 13:48:22,531 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1236 |
+
2025-10-09 13:48:41,039 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1237 |
+
2025-10-09 13:49:10,428 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1238 |
+
2025-10-09 13:52:55,469 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1239 |
+
2025-10-09 13:52:58,602 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1240 |
+
2025-10-09 13:57:58,711 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1241 |
+
2025-10-09 13:58:02,599 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1242 |
+
2025-10-09 14:09:18,549 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/analytics-chatbot?msg=I%20want%20sentiment%20distribution%20of%20divya%20dhakal
|
| 1243 |
+
2025-10-09 14:09:49,177 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1244 |
+
2025-10-09 15:03:04,035 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/show-analytics
|
| 1245 |
+
2025-10-09 15:06:25,706 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/api/show-analytics
|
| 1246 |
+
2025-10-09 15:07:54,561 | INFO | access_logger | api/main.py:21 | Response status: 200
|
| 1247 |
+
2025-10-09 15:08:33,444 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/orchestration
|
logs/app.log
CHANGED
|
@@ -124,3 +124,4 @@
|
|
| 124 |
2025-09-21 10:27:23,421 | INFO | app_logger | api/routers/orchestration.py:28 | Orchestrator executed
|
| 125 |
2025-09-21 10:33:52,133 | INFO | app_logger | api/routers/orchestration.py:28 | Orchestrator executed
|
| 126 |
2025-09-21 10:35:07,705 | INFO | app_logger | api/routers/orchestration.py:28 | Orchestrator executed
|
|
|
|
|
|
| 124 |
2025-09-21 10:27:23,421 | INFO | app_logger | api/routers/orchestration.py:28 | Orchestrator executed
|
| 125 |
2025-09-21 10:33:52,133 | INFO | app_logger | api/routers/orchestration.py:28 | Orchestrator executed
|
| 126 |
2025-09-21 10:35:07,705 | INFO | app_logger | api/routers/orchestration.py:28 | Orchestrator executed
|
| 127 |
+
2025-10-09 15:07:54,561 | INFO | app_logger | api/routers/show_analytics.py:14 | Influencer Analytics returned by orchestrator.
|
logs/errors.log
CHANGED
|
@@ -74,3 +74,6 @@ unique_selected_ideas.0
|
|
| 74 |
2025-09-18 23:40:42,373 | ERROR | error_logger | api/routers/orchestration.py:32 | Unable to run orchestration: Error code: 400 - {'error': {'message': 'tool call validation failed: parameters for tool UserReferenceResponseFormatter did not match schema: errors: [`/video_idea`: expected string, but got null, `/video_story`: expected string, but got null]', 'type': 'invalid_request_error', 'code': 'tool_use_failed', 'failed_generation': '<function=UserReferenceResponseFormatter>{"video_idea": null, "video_story": null}</function>'}}
|
| 75 |
2025-09-18 23:53:08,173 | ERROR | error_logger | api/routers/orchestration.py:32 | Unable to run orchestration: 'video_idea'
|
| 76 |
2025-09-18 23:56:47,469 | ERROR | error_logger | api/routers/orchestration.py:32 | Unable to run orchestration: unhashable type: 'list'
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
2025-09-18 23:40:42,373 | ERROR | error_logger | api/routers/orchestration.py:32 | Unable to run orchestration: Error code: 400 - {'error': {'message': 'tool call validation failed: parameters for tool UserReferenceResponseFormatter did not match schema: errors: [`/video_idea`: expected string, but got null, `/video_story`: expected string, but got null]', 'type': 'invalid_request_error', 'code': 'tool_use_failed', 'failed_generation': '<function=UserReferenceResponseFormatter>{"video_idea": null, "video_story": null}</function>'}}
|
| 75 |
2025-09-18 23:53:08,173 | ERROR | error_logger | api/routers/orchestration.py:32 | Unable to run orchestration: 'video_idea'
|
| 76 |
2025-09-18 23:56:47,469 | ERROR | error_logger | api/routers/orchestration.py:32 | Unable to run orchestration: unhashable type: 'list'
|
| 77 |
+
2025-10-09 11:34:30,750 | ERROR | error_logger | api/routers/orchestration.py:34 | Unable to run orchestration: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}
|
| 78 |
+
2025-10-09 12:53:28,063 | ERROR | error_logger | api/routers/orchestration.py:34 | Unable to run orchestration: ('Connection aborted.', ConnectionResetError(54, 'Connection reset by peer'))
|
| 79 |
+
2025-10-09 13:18:58,909 | ERROR | error_logger | api/routers/show_analytics.py:17 | Unable to extract influencer analytics: ('Connection aborted.', ConnectionResetError(54, 'Connection reset by peer'))
|
requirements.txt
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
-
|
| 2 |
-
langgraph
|
| 3 |
langsmith
|
| 4 |
langchain_groq
|
| 5 |
pydantic==2.11.7
|
|
|
|
| 1 |
+
langgraph==0.6.5
|
|
|
|
| 2 |
langsmith
|
| 3 |
langchain_groq
|
| 4 |
pydantic==2.11.7
|
src/genai/analytics_chatbot/utils/nodes.py
CHANGED
|
@@ -4,11 +4,11 @@ from .state import State
|
|
| 4 |
from .schemas import ResponseFormatter , InfluencerNames
|
| 5 |
from .prompts import chatbot_prompt , get_inf_name_prompt
|
| 6 |
from .utils import generate_api_knowledge
|
| 7 |
-
from src.genai.utils.models_loader import
|
| 8 |
|
| 9 |
class ChatbotNode:
|
| 10 |
def __init__(self):
|
| 11 |
-
self.llm =
|
| 12 |
|
| 13 |
def run(self, state:State):
|
| 14 |
print('Message:',state['messages'])
|
|
@@ -22,8 +22,8 @@ class ChatbotNode:
|
|
| 22 |
state["messages"] = state["messages"][-9:]
|
| 23 |
print('Messages:', state['messages'])
|
| 24 |
print(len(state['messages']))
|
| 25 |
-
result = self.llm.with_structured_output(ResponseFormatter).invoke(messages)
|
| 26 |
-
print(result)
|
| 27 |
return {
|
| 28 |
"messages": [{"role": "assistant", "content": f'''The endpoint is: {result.endpoint}. The parameters are: {result.parameters}'''}],
|
| 29 |
"endpoint": result.endpoint,
|
|
@@ -33,7 +33,7 @@ class ChatbotNode:
|
|
| 33 |
|
| 34 |
class FetchDataNode:
|
| 35 |
def __init__(self):
|
| 36 |
-
self.llm =
|
| 37 |
self.base_url = 'https://reveltrends.vercel.app'
|
| 38 |
self.headers = {
|
| 39 |
"Authorization": "Bearer YOUR_API_KEY", # replace with your API key if needed
|
|
@@ -49,7 +49,7 @@ class FetchDataNode:
|
|
| 49 |
print('Condition satisfied')
|
| 50 |
messages = [SystemMessage(content=get_inf_name_prompt()),
|
| 51 |
HumanMessage(content=f'''The dictionary of parameters is: {state['parameters']}''')]
|
| 52 |
-
response=
|
| 53 |
payload = {
|
| 54 |
"usernames": response.names,
|
| 55 |
"freq": state['parameters']['frequency']
|
|
|
|
| 4 |
from .schemas import ResponseFormatter , InfluencerNames
|
| 5 |
from .prompts import chatbot_prompt , get_inf_name_prompt
|
| 6 |
from .utils import generate_api_knowledge
|
| 7 |
+
from src.genai.utils.models_loader import llm_gpt
|
| 8 |
|
| 9 |
class ChatbotNode:
|
| 10 |
def __init__(self):
|
| 11 |
+
self.llm = llm_gpt
|
| 12 |
|
| 13 |
def run(self, state:State):
|
| 14 |
print('Message:',state['messages'])
|
|
|
|
| 22 |
state["messages"] = state["messages"][-9:]
|
| 23 |
print('Messages:', state['messages'])
|
| 24 |
print(len(state['messages']))
|
| 25 |
+
result = self.llm.with_structured_output(ResponseFormatter, method='function_calling').invoke(messages)
|
| 26 |
+
print('The result is:',result)
|
| 27 |
return {
|
| 28 |
"messages": [{"role": "assistant", "content": f'''The endpoint is: {result.endpoint}. The parameters are: {result.parameters}'''}],
|
| 29 |
"endpoint": result.endpoint,
|
|
|
|
| 33 |
|
| 34 |
class FetchDataNode:
|
| 35 |
def __init__(self):
|
| 36 |
+
self.llm = llm_gpt
|
| 37 |
self.base_url = 'https://reveltrends.vercel.app'
|
| 38 |
self.headers = {
|
| 39 |
"Authorization": "Bearer YOUR_API_KEY", # replace with your API key if needed
|
|
|
|
| 49 |
print('Condition satisfied')
|
| 50 |
messages = [SystemMessage(content=get_inf_name_prompt()),
|
| 51 |
HumanMessage(content=f'''The dictionary of parameters is: {state['parameters']}''')]
|
| 52 |
+
response=llm_gpt.with_structured_output(InfluencerNames).invoke(messages)
|
| 53 |
payload = {
|
| 54 |
"usernames": response.names,
|
| 55 |
"freq": state['parameters']['frequency']
|
src/genai/analytics_chatbot/utils/streamlit_app.py
CHANGED
|
@@ -40,7 +40,7 @@ if prompt := st.chat_input("Ask something about analytics..."):
|
|
| 40 |
bot_reply = f"⚠️ Error {response.status_code}: {response.text}"
|
| 41 |
|
| 42 |
except Exception as e:
|
| 43 |
-
bot_reply = f"
|
| 44 |
|
| 45 |
# Store bot message
|
| 46 |
st.session_state["messages"].append({"role": "assistant", "content": bot_reply})
|
|
|
|
| 40 |
bot_reply = f"⚠️ Error {response.status_code}: {response.text}"
|
| 41 |
|
| 42 |
except Exception as e:
|
| 43 |
+
bot_reply = f"Failed to connect to API: {e}"
|
| 44 |
|
| 45 |
# Store bot message
|
| 46 |
st.session_state["messages"].append({"role": "assistant", "content": bot_reply})
|
src/genai/business_interaction_agent/__init__.py
DELETED
|
File without changes
|
src/genai/business_interaction_agent/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (194 Bytes)
|
|
|
src/genai/business_interaction_agent/__pycache__/agent.cpython-312.pyc
DELETED
|
Binary file (6.32 kB)
|
|
|
src/genai/business_interaction_agent/agent.py
DELETED
|
@@ -1,82 +0,0 @@
|
|
| 1 |
-
from langchain_groq import ChatGroq
|
| 2 |
-
from langgraph.graph import StateGraph, MessagesState, START, END
|
| 3 |
-
from langgraph.checkpoint.memory import MemorySaver
|
| 4 |
-
from langgraph.prebuilt import create_react_agent
|
| 5 |
-
from .utils.state import State,StateUpdateFormatter
|
| 6 |
-
# from .utils.nodes import business_interaction_node, cleanup_messages
|
| 7 |
-
from utils.models_loader import llm
|
| 8 |
-
from langchain_core.messages import SystemMessage, ToolMessage
|
| 9 |
-
from .utils.prompts import business_retrieval_prompt, check_state_update_prompt
|
| 10 |
-
from .utils.utils import manual_retrieval
|
| 11 |
-
from context_analysis_agent.utils.utils import save_to_db
|
| 12 |
-
|
| 13 |
-
business_state = State()
|
| 14 |
-
|
| 15 |
-
class BusinessInteractionChatbot:
|
| 16 |
-
def __init__(self):
|
| 17 |
-
self.messages = []
|
| 18 |
-
self.business_details = None
|
| 19 |
-
self.react_agent=create_react_agent(model=llm,tools=[])
|
| 20 |
-
self.memory = MemorySaver()
|
| 21 |
-
self.workflow = self._initialize_workflow()
|
| 22 |
-
self.interact_agent = self.workflow.compile(checkpointer=self.memory)
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def _initialize_workflow(self):
|
| 26 |
-
workflow = StateGraph(MessagesState)
|
| 27 |
-
workflow.add_node("chatbot", self._call_model)
|
| 28 |
-
workflow.add_node("remove_message",self.delete_messages)
|
| 29 |
-
|
| 30 |
-
workflow.add_edge(START, "chatbot")
|
| 31 |
-
workflow.add_edge("chatbot","remove_message")
|
| 32 |
-
workflow.add_edge("chatbot", END)
|
| 33 |
-
return workflow
|
| 34 |
-
|
| 35 |
-
def delete_messages(self,state):
|
| 36 |
-
print('Entered message deletion....')
|
| 37 |
-
if len(self.messages) > 4:
|
| 38 |
-
print('satisfied...')
|
| 39 |
-
self.messages = self.messages[2:]
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def _call_model(self, state):
|
| 43 |
-
print('Entered into callmodel')
|
| 44 |
-
retrievals = manual_retrieval(str([msg['content'] for msg in self.messages if msg['role'] == 'user']),business_state.business_details)
|
| 45 |
-
template = business_retrieval_prompt(str([msg['content'] for msg in self.messages if msg['role'] == 'user']),str(business_state.business_details))
|
| 46 |
-
messages = [SystemMessage(content=template),ToolMessage(content="Tool's response:\n"+retrievals,tool_call_id='call_business_interaction')] + state["messages"]
|
| 47 |
-
print('The message is:',messages)
|
| 48 |
-
backup_response = self.react_agent.invoke({'messages':messages})['messages'][-1]
|
| 49 |
-
print('Backup response:',backup_response.content)
|
| 50 |
-
return {"messages": [backup_response.content]}
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
def check_state_update(self):
|
| 54 |
-
business_state.business_details
|
| 55 |
-
messages = str([msg['content'] for msg in self.messages if msg['role'] == 'user'])
|
| 56 |
-
template = check_state_update_prompt(business_state.business_details,messages)
|
| 57 |
-
messages = [SystemMessage(content=template)]
|
| 58 |
-
response = llm.with_structured_output(StateUpdateFormatter).invoke(messages)
|
| 59 |
-
# response= llm.invoke(messages)
|
| 60 |
-
# print('Response of state check:',response)
|
| 61 |
-
return response.model_dump()
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def chat(self, user_input: str, business_details:dict):
|
| 65 |
-
print('Entered into chat')
|
| 66 |
-
business_state.business_details=business_details
|
| 67 |
-
self.messages.append({"role": "user", "content": f'{user_input}'})
|
| 68 |
-
checked_details = self.check_state_update()
|
| 69 |
-
print('Checked details:',checked_details)
|
| 70 |
-
print('Business details:',business_state.business_details)
|
| 71 |
-
if checked_details!= business_state.business_details:
|
| 72 |
-
save_to_db(checked_details)
|
| 73 |
-
print('Database Updated as the state changed....')
|
| 74 |
-
business_state.business_details = checked_details
|
| 75 |
-
|
| 76 |
-
config = {"configurable": {"thread_id": "2"}}
|
| 77 |
-
response = self.interact_agent.invoke({"messages":self.messages}, config)['messages'][-1].content
|
| 78 |
-
print('The response:',response)
|
| 79 |
-
self.messages.append({"role": "assistant", "content": response})
|
| 80 |
-
print('The message_history:',self.messages)
|
| 81 |
-
business_state.interactions.append({'user': user_input, 'agent_response': response})
|
| 82 |
-
return response , business_state.business_details
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/genai/business_interaction_agent/utils/__init__.py
DELETED
|
File without changes
|
src/genai/business_interaction_agent/utils/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (200 Bytes)
|
|
|
src/genai/business_interaction_agent/utils/__pycache__/nodes.cpython-312.pyc
DELETED
|
Binary file (1.43 kB)
|
|
|
src/genai/business_interaction_agent/utils/__pycache__/prompts.cpython-312.pyc
DELETED
|
Binary file (4.7 kB)
|
|
|
src/genai/business_interaction_agent/utils/__pycache__/state.cpython-312.pyc
DELETED
|
Binary file (1.66 kB)
|
|
|
src/genai/business_interaction_agent/utils/__pycache__/utils.cpython-312.pyc
DELETED
|
Binary file (3.02 kB)
|
|
|
src/genai/business_interaction_agent/utils/nodes.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
from langchain_core.messages import SystemMessage, ToolMessage
|
| 2 |
-
from .prompts import business_retrieval_prompt
|
| 3 |
-
from .utils import manual_retrieval
|
| 4 |
-
|
| 5 |
-
# This node generates a response using business context and retrieval
|
| 6 |
-
def business_interaction_node(state, llm, react_agent, messages, business_details):
|
| 7 |
-
print('Entered into callmodel')
|
| 8 |
-
user_inputs = str([msg['content'] for msg in messages if msg['role'] == 'user'])
|
| 9 |
-
retrievals = manual_retrieval(user_inputs, business_details)
|
| 10 |
-
template = business_retrieval_prompt(user_inputs, retrievals)
|
| 11 |
-
formatted_messages = [SystemMessage(content=template) , ToolMessage(content="Tool's response:\n"+retrievals)] + state["messages"]
|
| 12 |
-
response = react_agent.invoke({'messages': formatted_messages})['messages'][-1]
|
| 13 |
-
print('Backup response:', response.content)
|
| 14 |
-
return {"messages": [response.content]}
|
| 15 |
-
|
| 16 |
-
# Optional message cleanup node
|
| 17 |
-
def cleanup_messages(messages):
|
| 18 |
-
if len(messages) > 4:
|
| 19 |
-
return messages[2:]
|
| 20 |
-
return messages
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/genai/business_interaction_agent/utils/prompts.py
DELETED
|
@@ -1,78 +0,0 @@
|
|
| 1 |
-
def business_retrieval_prompt(user_message, business_details):
|
| 2 |
-
return f'''
|
| 3 |
-
You are a professional AI assistant helping users understand how influencers can support their business but only on the basis of the tool's response provided to you. You will be given:
|
| 4 |
-
|
| 5 |
-
- A **user message**
|
| 6 |
-
- The response of the tool. The tool's response contains the video stories of the influencers.
|
| 7 |
-
- The **business details** provided by the user.
|
| 8 |
-
|
| 9 |
-
Your job is to:
|
| 10 |
-
1. **First**, analyze the user message and decide if it is actually a business-related question or query that could be answered using influencer content.
|
| 11 |
-
- If the message is just a casual greeting like “Hi”, “Hello”, “How are you?”, or is not business-related (e.g., “Who are you?”, “Tell me a joke”), then **do NOT use the retrievals or business details**. Just respond to the user naturally and politely.
|
| 12 |
-
|
| 13 |
-
2. **If the message is business-related**, proceed to:
|
| 14 |
-
a. Analyze the **business details** and check if the user message aligns with the type of business.
|
| 15 |
-
- If there is a mismatch (e.g., business is a restaurant but the user asks about clothing), politely **alert the user** about the mismatch.
|
| 16 |
-
- Still, go ahead and provide a helpful answer using relevant influencer data if available.
|
| 17 |
-
b. Analyze the influencer data and explain how each influencer might support the business based only on the tool's response.
|
| 18 |
-
|
| 19 |
-
--- USER MESSAGE ---
|
| 20 |
-
{user_message}
|
| 21 |
-
|
| 22 |
-
--- BUSINESS DETAILS ---
|
| 23 |
-
{business_details}
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
Rules:
|
| 27 |
-
- If the user message is **not relevant to influencer or business help**, politely respond in a general helpful way and ignore the tool's response and business details.
|
| 28 |
-
- If the message **is relevant**, then:
|
| 29 |
-
- First verify if the user’s business type matches the context of their message.
|
| 30 |
-
- If not, display a short alert to the user like: “Note: Your query seems to focus on [X], but your business is about [Y].”
|
| 31 |
-
- Then analyze the influencer stories:
|
| 32 |
-
- Identify which influencer content is relevant.
|
| 33 |
-
- Explain what they are promoting and how it might help the business.
|
| 34 |
-
- You **must mention influencer usernames** and only use what is in the tool's response.
|
| 35 |
-
- Do NOT invent or assume any information beyond what is explicitly provided in the tool's response and business details.
|
| 36 |
-
|
| 37 |
-
Keep your response:
|
| 38 |
-
- Context-aware, direct and short as possible
|
| 39 |
-
- Grounded only in the tool's response.
|
| 40 |
-
- Helpful, concise, and user-friendly
|
| 41 |
-
'''.strip()
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
def check_state_update_prompt(business_details: dict, user_messages: str):
|
| 45 |
-
return f"""
|
| 46 |
-
You are an expert business assistant tasked with updating business details by thoroughly analyzing user messages. Your goal is to identify and apply changes to the business details only when the user message explicitly provides new or conflicting information relevant to the business details. Follow these guidelines:
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
1.Deep Analysis: Carefully parse the user message to extract specific details (e.g., business type, platform , business_goals, target audience, challenges etc) that differ from the current business details.
|
| 50 |
-
|
| 51 |
-
2.Relevance Check: Only update the business details if the user message contains explicit updates or corrections to the existing details. Ignore general inquiries (e.g., "Hi", "Hello", "How are you?", "Who are you?") or messages unrelated to business details.
|
| 52 |
-
|
| 53 |
-
3.No Redundant Changes: Do not modify business details if the user message aligns with or restates the current details.
|
| 54 |
-
|
| 55 |
-
4.Preserve Format: Return the updated business details in the exact same format as the input business details (e.g., dictionary structure with identical keys and data types).
|
| 56 |
-
|
| 57 |
-
5.Edge Cases: If the user message is ambiguous, incomplete, or contradictory, do not make speculative changes. Instead, retain the original details for those fields.
|
| 58 |
-
|
| 59 |
-
6.Case Sensitivity and Formatting: Respect the case and formatting of the original business details unless explicitly contradicted by the user message.
|
| 60 |
-
|
| 61 |
-
Input:
|
| 62 |
-
|
| 63 |
-
Current business details: {str(business_details)}
|
| 64 |
-
|
| 65 |
-
User message: {user_messages}
|
| 66 |
-
|
| 67 |
-
Output:
|
| 68 |
-
|
| 69 |
-
Return the updated business details in the same dictionary format as provided. If no changes are warranted, return the original business details unchanged.
|
| 70 |
-
"""
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/genai/business_interaction_agent/utils/state.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
from pydantic import BaseModel, ConfigDict , Field
|
| 2 |
-
from typing import Optional
|
| 3 |
-
|
| 4 |
-
class State(BaseModel):
|
| 5 |
-
interactions: Optional[list] = []
|
| 6 |
-
business_details : Optional[dict] = {}
|
| 7 |
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 8 |
-
|
| 9 |
-
class StateUpdateFormatter(BaseModel):
|
| 10 |
-
business_type: str = Field(description="The type of the business. If updated, the new one.")
|
| 11 |
-
platform: str = Field(description="The platform used for the business. If updated, the new one.")
|
| 12 |
-
target_audience: str = Field(description="The target audience of the business. If updated, the new one.")
|
| 13 |
-
business_goals: str = Field(description="The business goals of the business. If updated, the new one.")
|
| 14 |
-
offerings: str = Field(description="The offerings of the business. If updated, the new one.")
|
| 15 |
-
Challenges_faced: str = Field(description="The challenges faced by the business. If updated, the new one.")
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/genai/business_interaction_agent/utils/tools.py
DELETED
|
File without changes
|
src/genai/business_interaction_agent/utils/utils.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
import pandas as pd
|
| 2 |
-
import faiss
|
| 3 |
-
import re
|
| 4 |
-
from src.genai.utils.models_loader import ST
|
| 5 |
-
import numpy as np
|
| 6 |
-
import ast
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def manual_retrieval(messages, business_details):
|
| 10 |
-
'''
|
| 11 |
-
Always invoke this tool.
|
| 12 |
-
Retrieve influencer's data by semantic search of **user messages** and the **business details**.
|
| 13 |
-
'''
|
| 14 |
-
# === Load CSV ===
|
| 15 |
-
csv_path = 'extracted_data.csv'
|
| 16 |
-
df = pd.read_csv(csv_path)
|
| 17 |
-
|
| 18 |
-
# === Parse stored embeddings ===
|
| 19 |
-
df['embeddings'] = df['embeddings'].apply(lambda x: ast.literal_eval(x) if isinstance(x, str) else x)
|
| 20 |
-
embeddings = np.vstack(df['embeddings'].values).astype('float32')
|
| 21 |
-
|
| 22 |
-
# === Build FAISS index ===
|
| 23 |
-
dimension = embeddings.shape[1]
|
| 24 |
-
index = faiss.IndexFlatL2(dimension)
|
| 25 |
-
index.add(embeddings)
|
| 26 |
-
|
| 27 |
-
# === Load SentenceTransformer model ===
|
| 28 |
-
|
| 29 |
-
# === Encode the query and search ===
|
| 30 |
-
query_embedding = ST.encode(str(messages)).reshape(1, -1).astype('float32')
|
| 31 |
-
top_k=7
|
| 32 |
-
distances, indices = index.search(query_embedding, top_k)
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
# === Format results ===
|
| 37 |
-
outer_list = []
|
| 38 |
-
for i, idx in enumerate(indices[0]):
|
| 39 |
-
res = {
|
| 40 |
-
'rank': i + 1,
|
| 41 |
-
'username': df.iloc[idx]['username'],
|
| 42 |
-
'story': df.iloc[idx]['story'],
|
| 43 |
-
'visible_texts_or_brandings': df.iloc[idx]['visible_texts_or_brandings'],
|
| 44 |
-
'likesCount': df.iloc[idx]['likesCount'],
|
| 45 |
-
'commentCount': df.iloc[idx]['commentCount'],
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
inner_list = []
|
| 49 |
-
inner_list.append(f"[{res['rank']}]. The influencer name is: **{res['username']}** — Likes: **{res['likesCount']}**, Comments: **{res['commentCount']}**")
|
| 50 |
-
inner_list.append(f"The story of that particular video is:\n{res['story']}")
|
| 51 |
-
inner_list.append(f"The branding or promotion done is:\n{res['visible_texts_or_brandings']}")
|
| 52 |
-
|
| 53 |
-
outer_list.append(inner_list)
|
| 54 |
-
print('THE RETRIEVALS ARE:', outer_list)
|
| 55 |
-
return str(outer_list).strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/genai/orchestration_agent/__pycache__/agent.cpython-313.pyc
CHANGED
|
Binary files a/src/genai/orchestration_agent/__pycache__/agent.cpython-313.pyc and b/src/genai/orchestration_agent/__pycache__/agent.cpython-313.pyc differ
|
|
|
src/genai/orchestration_agent/agent.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
from langgraph.graph import StateGraph, MessagesState, START, END
|
| 2 |
from langgraph.checkpoint.memory import MemorySaver
|
| 3 |
-
from .utils.nodes import ToolReturnNode, ExtractUserReferenceNode , ImageCaptionNode ,
|
| 4 |
from src.genai.utils.models_loader import llm_gpt
|
| 5 |
from .utils.state import State
|
| 6 |
from .utils.utils import ImageCaptioner, ResponseBlockExtractor
|
|
@@ -19,7 +19,7 @@ class OrchestrationAgent:
|
|
| 19 |
workflow = StateGraph(State)
|
| 20 |
workflow.add_node("image_caption", ImageCaptionNode().run)
|
| 21 |
workflow.add_node("tool_return", ToolReturnNode().run)
|
| 22 |
-
workflow.add_node("query_response",
|
| 23 |
workflow.add_node("extract_reference", ExtractUserReferenceNode().run)
|
| 24 |
|
| 25 |
workflow.add_edge(START,"image_caption")
|
|
|
|
| 1 |
from langgraph.graph import StateGraph, MessagesState, START, END
|
| 2 |
from langgraph.checkpoint.memory import MemorySaver
|
| 3 |
+
from .utils.nodes import ToolReturnNode, ExtractUserReferenceNode , ImageCaptionNode , QueryResponseNode
|
| 4 |
from src.genai.utils.models_loader import llm_gpt
|
| 5 |
from .utils.state import State
|
| 6 |
from .utils.utils import ImageCaptioner, ResponseBlockExtractor
|
|
|
|
| 19 |
workflow = StateGraph(State)
|
| 20 |
workflow.add_node("image_caption", ImageCaptionNode().run)
|
| 21 |
workflow.add_node("tool_return", ToolReturnNode().run)
|
| 22 |
+
workflow.add_node("query_response", QueryResponseNode().run)
|
| 23 |
workflow.add_node("extract_reference", ExtractUserReferenceNode().run)
|
| 24 |
|
| 25 |
workflow.add_edge(START,"image_caption")
|
src/genai/orchestration_agent/utils/__pycache__/nodes.cpython-313.pyc
CHANGED
|
Binary files a/src/genai/orchestration_agent/utils/__pycache__/nodes.cpython-313.pyc and b/src/genai/orchestration_agent/utils/__pycache__/nodes.cpython-313.pyc differ
|
|
|
src/genai/orchestration_agent/utils/__pycache__/prompts.cpython-313.pyc
CHANGED
|
Binary files a/src/genai/orchestration_agent/utils/__pycache__/prompts.cpython-313.pyc and b/src/genai/orchestration_agent/utils/__pycache__/prompts.cpython-313.pyc differ
|
|
|
src/genai/orchestration_agent/utils/nodes.py
CHANGED
|
@@ -40,6 +40,7 @@ class ImageCaptionNode:
|
|
| 40 |
response=chat_completion.choices[0].message.content
|
| 41 |
return {'image_caption': response}
|
| 42 |
else:
|
|
|
|
| 43 |
return {'image_caption':None}
|
| 44 |
|
| 45 |
|
|
@@ -53,22 +54,26 @@ class ToolReturnNode:
|
|
| 53 |
if len(state["messages"]) > 23:
|
| 54 |
state["messages"] = state["messages"][-18:]
|
| 55 |
template = [SystemMessage(content=tool_return_prompt)] + state["messages"]
|
| 56 |
-
response = self.llm.with_structured_output(ToolResponseFormatter).invoke(template)
|
| 57 |
print('The response is:', response)
|
| 58 |
return {"messages": [{'role': 'assistant', 'content': f"Tool invoked: {response.tools}"}],
|
| 59 |
"tools":response.tools}
|
| 60 |
|
| 61 |
|
| 62 |
-
class
|
| 63 |
def __init__(self):
|
| 64 |
self.llm = llm_gpt
|
| 65 |
|
| 66 |
def run(self,state:State):
|
|
|
|
| 67 |
if len(state['tools'])<1:
|
|
|
|
| 68 |
retrieved_data=retriever.retrieve_for_orchestration(state['messages'])
|
|
|
|
| 69 |
template = [SystemMessage(content=query_response_prompt),
|
| 70 |
FunctionMessage(name='inf-data-retrieval',content=retrieved_data)] + state["messages"]
|
| 71 |
response = self.llm.invoke(template)
|
|
|
|
| 72 |
return {"messages": [{'role': 'assistant', 'content': response.content}],
|
| 73 |
"query_response":response.content}
|
| 74 |
else:
|
|
@@ -91,7 +96,8 @@ class ExtractUserReferenceNode:
|
|
| 91 |
print('Latest human message:', latest_human_message)
|
| 92 |
template = [SystemMessage(content=extract_user_reference_prompt),
|
| 93 |
HumanMessage(content=latest_human_message.content)]
|
| 94 |
-
response = self.llm.with_structured_output(UserReferenceResponseFormatter).invoke(template)
|
|
|
|
| 95 |
return{
|
| 96 |
'video_idea': response.video_idea,
|
| 97 |
'video_story': response.video_story
|
|
@@ -119,6 +125,7 @@ class InvokeToolNode:
|
|
| 119 |
return {
|
| 120 |
'analytics_response':response.json()
|
| 121 |
}
|
|
|
|
| 122 |
|
| 123 |
|
| 124 |
|
|
|
|
| 40 |
response=chat_completion.choices[0].message.content
|
| 41 |
return {'image_caption': response}
|
| 42 |
else:
|
| 43 |
+
print('No image provided')
|
| 44 |
return {'image_caption':None}
|
| 45 |
|
| 46 |
|
|
|
|
| 54 |
if len(state["messages"]) > 23:
|
| 55 |
state["messages"] = state["messages"][-18:]
|
| 56 |
template = [SystemMessage(content=tool_return_prompt)] + state["messages"]
|
| 57 |
+
response = self.llm.with_structured_output(ToolResponseFormatter, method='function_calling').invoke(template)
|
| 58 |
print('The response is:', response)
|
| 59 |
return {"messages": [{'role': 'assistant', 'content': f"Tool invoked: {response.tools}"}],
|
| 60 |
"tools":response.tools}
|
| 61 |
|
| 62 |
|
| 63 |
+
class QueryResponseNode:
|
| 64 |
def __init__(self):
|
| 65 |
self.llm = llm_gpt
|
| 66 |
|
| 67 |
def run(self,state:State):
|
| 68 |
+
print('Entered to query response')
|
| 69 |
if len(state['tools'])<1:
|
| 70 |
+
print('Going for retrieval')
|
| 71 |
retrieved_data=retriever.retrieve_for_orchestration(state['messages'])
|
| 72 |
+
print('The data is retrieved.')
|
| 73 |
template = [SystemMessage(content=query_response_prompt),
|
| 74 |
FunctionMessage(name='inf-data-retrieval',content=retrieved_data)] + state["messages"]
|
| 75 |
response = self.llm.invoke(template)
|
| 76 |
+
print('Query Response:', response)
|
| 77 |
return {"messages": [{'role': 'assistant', 'content': response.content}],
|
| 78 |
"query_response":response.content}
|
| 79 |
else:
|
|
|
|
| 96 |
print('Latest human message:', latest_human_message)
|
| 97 |
template = [SystemMessage(content=extract_user_reference_prompt),
|
| 98 |
HumanMessage(content=latest_human_message.content)]
|
| 99 |
+
response = self.llm.with_structured_output(UserReferenceResponseFormatter, method='function_calling').invoke(template)
|
| 100 |
+
print('The extracted reference:', response)
|
| 101 |
return{
|
| 102 |
'video_idea': response.video_idea,
|
| 103 |
'video_story': response.video_story
|
|
|
|
| 125 |
return {
|
| 126 |
'analytics_response':response.json()
|
| 127 |
}
|
| 128 |
+
|
| 129 |
|
| 130 |
|
| 131 |
|
src/genai/orchestration_agent/utils/prompts.py
CHANGED
|
@@ -39,91 +39,6 @@ Respond to the user's query in polite way.
|
|
| 39 |
You are passed with the data of influencers from the function message. Use that data to give the response.
|
| 40 |
"""
|
| 41 |
|
| 42 |
-
tool_return_prompt_old = """
|
| 43 |
-
You are an AI orchestration agent and a friendly assistant built to help businesses and brands find influencers and generate content ideas, stories, and visuals.
|
| 44 |
-
|
| 45 |
-
Your job is to:
|
| 46 |
-
1. **Read the user's message carefully** and identify their intent.
|
| 47 |
-
2. **Return exactly two things**:
|
| 48 |
-
- tools: a Python-style list of tool names (from the ordered list below) that match the user’s intent.
|
| 49 |
-
- query_response: a short, friendly, and helpful reply that aligns with the tools you've selected. Also return the relevant data of influencers aligning with the user query only if the user asks about the influencers.
|
| 50 |
-
|
| 51 |
-
Your tools output must:
|
| 52 |
-
- Always be a **Python-style list**, even if only one tool is selected.
|
| 53 |
-
- **Respect the sequence** of tools listed below. If more than one tool is needed, always list them in this order (regardless of how the user wrote them).
|
| 54 |
-
- Only include tools that are strictly necessary.
|
| 55 |
-
|
| 56 |
-
Your query_response must:
|
| 57 |
-
- Always match and acknowledge the user's request.
|
| 58 |
-
- Be aligned with the selected tools.
|
| 59 |
-
- Never say something is impossible if a tool exists for it.
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
---
|
| 63 |
-
|
| 64 |
-
### Available Tools (in execution order — use this order in your response):
|
| 65 |
-
|
| 66 |
-
1. **ideation** → Trigger if the user:
|
| 67 |
-
- Wants to generate the video ideas
|
| 68 |
-
- Asks for new or fresh ideas
|
| 69 |
-
- If the user didn't like previous ideas and wants ideas again, you have to return this again.
|
| 70 |
-
|
| 71 |
-
2. **human-idea-refining** → Trigger if the user:
|
| 72 |
-
- Says they liked, loved, locked, or selected specific ideas
|
| 73 |
-
- Gives feedback, asks for improvement, merging, or editing of ideas
|
| 74 |
-
- Refers to using or building on a specific idea (e.g., “based on idea 2”)
|
| 75 |
-
- This is the part where human locks the idea generated by AI or want to modify ideas.
|
| 76 |
-
- In this part you have to be very careful in this part. You have to trigger this tool until the user likes or locks the idea. They can modify the idea as long time as they can. Even after some time of refinements, they can say that i want to go back to the previous idea, undo or something. At that time also they are trying to refine their idea and you have to trigger this tool. So be wise here.
|
| 77 |
-
- Remember, you are **strictly not allowed to** return ideation after human-idea-refining. For eg: ['human-idea-refining','ideation']. This is not allowed
|
| 78 |
-
|
| 79 |
-
3. **generate-story** → Trigger if the user:
|
| 80 |
-
- Wants to generate a story or says that they want to brainstorm
|
| 81 |
-
- Says things like “create a story”, “make a plot”, or “brainstorm a storyline” etc.
|
| 82 |
-
- This is the part where the user gets the story of their selected or refined idea.
|
| 83 |
-
|
| 84 |
-
4. **generate-ultimate-story** → Trigger only if:
|
| 85 |
-
- The user is asking for the **final**, polished, or complete story
|
| 86 |
-
- And has already done previous story brainstorming
|
| 87 |
-
- Never trigger this tool alone unless generate-story has been used before
|
| 88 |
-
|
| 89 |
-
5. **generate-image** → Trigger if the user:
|
| 90 |
-
- Wants a visual, image, or scene created from the final story
|
| 91 |
-
|
| 92 |
-
---
|
| 93 |
-
|
| 94 |
-
### Special Clarifications (Important Rules):
|
| 95 |
-
|
| 96 |
-
- If a user says:
|
| 97 |
-
> “Generate story based on idea 4”
|
| 98 |
-
This means they selected/liked idea 4 → so return:
|
| 99 |
-
"tools": ["human-idea-refining", "generate-story"]
|
| 100 |
-
|
| 101 |
-
- If a user says:
|
| 102 |
-
> “Create an image of the final script”
|
| 103 |
-
→ Return: ["generate-image"]
|
| 104 |
-
|
| 105 |
-
- If a user says:
|
| 106 |
-
> “I want a final version of this story”
|
| 107 |
-
→ Return: ["generate-ultimate-story"] (but only if generate-story was used before)
|
| 108 |
-
|
| 109 |
-
- If a user gives feedback or says “improve this idea” → Return ["human-idea-refining"]
|
| 110 |
-
|
| 111 |
-
- If a user just wants help with finding influencers or advice → Return "tools": [] but still respond helpfully via query_response.
|
| 112 |
-
- Again reminding you the most important part. If you generate more than one tools, **Please** generate them in the sequence above provided. For eg: Don't give **human-idea-refining** after **ideation**. The list of tools have to be in the proper order provided above.
|
| 113 |
-
---
|
| 114 |
-
|
| 115 |
-
### Influencer Assistant Note:
|
| 116 |
-
|
| 117 |
-
You're also an intelligent assistant for brands looking to collaborate with influencers. If the user asks about influencer suggestions, trends, or insights , give detailed, useful replies using the influencer data provided to you.
|
| 118 |
-
|
| 119 |
-
---
|
| 120 |
-
|
| 121 |
-
### Output Format (always this exact format):
|
| 122 |
-
|
| 123 |
-
"tools": ["tool_1", "tool_2"], // or [] if nothing applies
|
| 124 |
-
"query_response": "Short, clear, and friendly message to the user. Give the data of influencers too if provided to you."
|
| 125 |
-
|
| 126 |
-
"""
|
| 127 |
|
| 128 |
extract_user_reference_prompt = """
|
| 129 |
You are an information extractor, NOT a creative assistant. Your ONLY job is to extract video ideas and video stories from user queries **if and only if** they are explicitly written by the user. Do not create, generate, or imagine anything on your own.
|
|
|
|
| 39 |
You are passed with the data of influencers from the function message. Use that data to give the response.
|
| 40 |
"""
|
| 41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
extract_user_reference_prompt = """
|
| 44 |
You are an information extractor, NOT a creative assistant. Your ONLY job is to extract video ideas and video stories from user queries **if and only if** they are explicitly written by the user. Do not create, generate, or imagine anything on your own.
|
src/genai/orchestration_agent/utils/tools.py
CHANGED
|
@@ -3,7 +3,7 @@ import ast
|
|
| 3 |
import pandas as pd
|
| 4 |
import numpy as np
|
| 5 |
from src.genai.utils.data_loader import caption_df, caption_index
|
| 6 |
-
from src.genai.utils.models_loader import embedding_model
|
| 7 |
from src.genai.utils.utils import clean_text
|
| 8 |
import tiktoken
|
| 9 |
|
|
@@ -16,7 +16,9 @@ class InfluencerRetrievalTool:
|
|
| 16 |
self.index = caption_index
|
| 17 |
|
| 18 |
def retrieve_for_analytics(self, business_details):
|
|
|
|
| 19 |
query_embedding = np.array(embedding_model.embed_query(str(business_details))).reshape(1, -1).astype('float32')
|
|
|
|
| 20 |
distances, indices = self.index.search(query_embedding, 10)
|
| 21 |
results = []
|
| 22 |
for idx in indices[0]:
|
|
@@ -31,7 +33,9 @@ class InfluencerRetrievalTool:
|
|
| 31 |
|
| 32 |
def retrieve_for_orchestration(self, query):
|
| 33 |
query_embedding = np.array(embedding_model.embed_query(str(query))).reshape(1, -1).astype('float32')
|
|
|
|
| 34 |
faiss.normalize_L2(query_embedding)
|
|
|
|
| 35 |
distances, indices = self.index.search(query_embedding, len(self.df))
|
| 36 |
similarity_threshold = 0.35
|
| 37 |
selected = [(idx, sim) for idx, sim in zip(indices[0], distances[0]) if sim >= similarity_threshold]
|
|
@@ -49,9 +53,10 @@ class InfluencerRetrievalTool:
|
|
| 49 |
outer_list.append(inner_list)
|
| 50 |
|
| 51 |
cleaned_response = clean_text(str(outer_list))
|
| 52 |
-
|
| 53 |
-
tokens =
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
|
| 57 |
|
|
|
|
| 3 |
import pandas as pd
|
| 4 |
import numpy as np
|
| 5 |
from src.genai.utils.data_loader import caption_df, caption_index
|
| 6 |
+
from src.genai.utils.models_loader import embedding_model , encoding_model
|
| 7 |
from src.genai.utils.utils import clean_text
|
| 8 |
import tiktoken
|
| 9 |
|
|
|
|
| 16 |
self.index = caption_index
|
| 17 |
|
| 18 |
def retrieve_for_analytics(self, business_details):
|
| 19 |
+
print('Generating embeddings..')
|
| 20 |
query_embedding = np.array(embedding_model.embed_query(str(business_details))).reshape(1, -1).astype('float32')
|
| 21 |
+
print('Embeddings generated')
|
| 22 |
distances, indices = self.index.search(query_embedding, 10)
|
| 23 |
results = []
|
| 24 |
for idx in indices[0]:
|
|
|
|
| 33 |
|
| 34 |
def retrieve_for_orchestration(self, query):
|
| 35 |
query_embedding = np.array(embedding_model.embed_query(str(query))).reshape(1, -1).astype('float32')
|
| 36 |
+
print('Embeddings Generated')
|
| 37 |
faiss.normalize_L2(query_embedding)
|
| 38 |
+
print('Query embedded')
|
| 39 |
distances, indices = self.index.search(query_embedding, len(self.df))
|
| 40 |
similarity_threshold = 0.35
|
| 41 |
selected = [(idx, sim) for idx, sim in zip(indices[0], distances[0]) if sim >= similarity_threshold]
|
|
|
|
| 53 |
outer_list.append(inner_list)
|
| 54 |
|
| 55 |
cleaned_response = clean_text(str(outer_list))
|
| 56 |
+
print('response cleaned')
|
| 57 |
+
tokens = encoding_model.encode(cleaned_response)[:1000]
|
| 58 |
+
print('tokens got')
|
| 59 |
+
return encoding_model.decode(tokens)
|
| 60 |
|
| 61 |
|
| 62 |
|
src/genai/utils/__pycache__/models_loader.cpython-313.pyc
CHANGED
|
Binary files a/src/genai/utils/__pycache__/models_loader.cpython-313.pyc and b/src/genai/utils/__pycache__/models_loader.cpython-313.pyc differ
|
|
|
src/genai/utils/models_loader.py
CHANGED
|
@@ -7,6 +7,7 @@ from langchain_anthropic import ChatAnthropic
|
|
| 7 |
from langchain_openai import OpenAIEmbeddings
|
| 8 |
from huggingface_hub import login
|
| 9 |
from dotenv import load_dotenv
|
|
|
|
| 10 |
|
| 11 |
load_dotenv()
|
| 12 |
os.environ['HUGGINGFACEHUB_ACCESS_TOKEN']=os.getenv('HUGGINGFACEHUB_ACCESS_TOKEN')
|
|
@@ -19,9 +20,11 @@ llm_gemini = ChatGoogleGenerativeAI(model="gemini-1.5-flash")
|
|
| 19 |
llm_groq_openai = ChatGroq(model="openai/gpt-oss-120b",temperature=0.7)
|
| 20 |
llm_groq = ChatGroq(model="llama-3.3-70b-versatile",temperature=0)
|
| 21 |
|
| 22 |
-
llm_gpt_small = ChatOpenAI(model="gpt-
|
| 23 |
-
llm_gpt = ChatOpenAI(model="gpt-
|
| 24 |
-
llm_gpt_high = ChatOpenAI(model="gpt-
|
|
|
|
|
|
|
| 25 |
|
| 26 |
captioning_model = "meta-llama/llama-4-scout-17b-16e-instruct"
|
| 27 |
image_generation_model = "black-forest-labs/FLUX.1-schnell"
|
|
|
|
| 7 |
from langchain_openai import OpenAIEmbeddings
|
| 8 |
from huggingface_hub import login
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
+
import tiktoken
|
| 11 |
|
| 12 |
load_dotenv()
|
| 13 |
os.environ['HUGGINGFACEHUB_ACCESS_TOKEN']=os.getenv('HUGGINGFACEHUB_ACCESS_TOKEN')
|
|
|
|
| 20 |
llm_groq_openai = ChatGroq(model="openai/gpt-oss-120b",temperature=0.7)
|
| 21 |
llm_groq = ChatGroq(model="llama-3.3-70b-versatile",temperature=0)
|
| 22 |
|
| 23 |
+
llm_gpt_small = ChatOpenAI(model="gpt-4o-mini",temperature=0.3)
|
| 24 |
+
llm_gpt = ChatOpenAI(model="gpt-4o-mini",temperature=0.3)
|
| 25 |
+
llm_gpt_high = ChatOpenAI(model="gpt-4o-mini",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"
|
| 30 |
image_generation_model = "black-forest-labs/FLUX.1-schnell"
|