vicky4s4s commited on
Commit
01e9350
·
verified ·
1 Parent(s): 8e359bd

Upload 76 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. all_combine_code/.env +59 -0
  3. all_combine_code/.gitignore +28 -0
  4. all_combine_code/Dockerfile +19 -0
  5. all_combine_code/__init__.py +0 -0
  6. all_combine_code/agents_/__init__.py +0 -0
  7. all_combine_code/agents_/__pycache__/__init__.cpython-313.pyc +0 -0
  8. all_combine_code/agents_/delveant_prompts/__init__.py +0 -0
  9. all_combine_code/agents_/delveant_prompts/crm_leads_agents.py +157 -0
  10. all_combine_code/agents_/delveant_prompts/db_collection_name_finder.py +113 -0
  11. all_combine_code/agents_/delveant_prompts/tool_clasification_agent.py +327 -0
  12. all_combine_code/all_services_/__pycache__/delveant_core_services.cpython-313.pyc +0 -0
  13. all_combine_code/all_services_/delveant_core_services.py +1011 -0
  14. all_combine_code/app.py +21 -0
  15. all_combine_code/azure-pipelines.yml +176 -0
  16. all_combine_code/genre_classification/genre_.ipynb +611 -0
  17. all_combine_code/genre_classification/main_file.ipynb +2110 -0
  18. all_combine_code/genre_classification/test1_file.ipynb +325 -0
  19. all_combine_code/genreclassification/.env +1 -0
  20. all_combine_code/genreclassification/Dockerfile +22 -0
  21. all_combine_code/genreclassification/chartmetric_summary_generator3.py +264 -0
  22. all_combine_code/genreclassification/main_app.py +71 -0
  23. all_combine_code/genreclassification/requirements.txt +8 -0
  24. all_combine_code/genreclassification/songs_report_generator1.py +66 -0
  25. all_combine_code/genreclassification/viberate_api_report_generator2.py +205 -0
  26. all_combine_code/local_model_to_s3/bucket_inside_file_delete.py +30 -0
  27. all_combine_code/local_model_to_s3/local_model_to_s3_bucket.py +28 -0
  28. all_combine_code/memory_/__init__.py +0 -0
  29. all_combine_code/memory_/__pycache__/__init__.cpython-313.pyc +0 -0
  30. all_combine_code/memory_/__pycache__/chat_history_handle.cpython-313.pyc +0 -0
  31. all_combine_code/memory_/__pycache__/prompt_agents.cpython-313.pyc +0 -0
  32. all_combine_code/memory_/chat_history_handle.py +194 -0
  33. all_combine_code/memory_/prompt_agents.py +82 -0
  34. all_combine_code/models_/__init__.py +0 -0
  35. all_combine_code/models_/__pycache__/__init__.cpython-313.pyc +0 -0
  36. all_combine_code/models_/__pycache__/code_generation_model.cpython-313.pyc +0 -0
  37. all_combine_code/models_/code_generation_model.py +90 -0
  38. all_combine_code/models_/delveant_llm_model_/__init__.py +0 -0
  39. all_combine_code/models_/delveant_llm_model_/__pycache__/__init__.cpython-313.pyc +0 -0
  40. all_combine_code/models_/delveant_llm_model_/__pycache__/multi_agent_llm.cpython-313.pyc +0 -0
  41. all_combine_code/models_/delveant_llm_model_/multi_agent_llm.py +264 -0
  42. all_combine_code/models_/delveant_llm_model_/tool_calling_model.py +117 -0
  43. all_combine_code/mongodb_data_chat/delveant_chatbot.py +152 -0
  44. all_combine_code/recommedation_engine/__pycache__/app.cpython-313.pyc +0 -0
  45. all_combine_code/recommedation_engine/__pycache__/embeddings_.cpython-313.pyc +0 -0
  46. all_combine_code/recommedation_engine/__pycache__/s3_file_uploader.cpython-313.pyc +0 -0
  47. all_combine_code/recommedation_engine/app.py +85 -0
  48. all_combine_code/recommedation_engine/binary_classifier/__pycache__/classify.cpython-313.pyc +0 -0
  49. all_combine_code/recommedation_engine/binary_classifier/classify.py +100 -0
  50. all_combine_code/recommedation_engine/embeddings_.py +98 -0
.gitattributes CHANGED
@@ -107,3 +107,5 @@ genre_finder/find_genre_different_apporoch/features_dataset.csv filter=lfs diff=
107
  genre_finder/find_genre_different_apporoch/all_genre.csv filter=lfs diff=lfs merge=lfs -text
108
  rocketship-ml-model-train/assets/all_genres_clean.csv filter=lfs diff=lfs merge=lfs -text
109
  rocketship-ml-model-train/assets/features_dataset.csv filter=lfs diff=lfs merge=lfs -text
 
 
 
107
  genre_finder/find_genre_different_apporoch/all_genre.csv filter=lfs diff=lfs merge=lfs -text
108
  rocketship-ml-model-train/assets/all_genres_clean.csv filter=lfs diff=lfs merge=lfs -text
109
  rocketship-ml-model-train/assets/features_dataset.csv filter=lfs diff=lfs merge=lfs -text
110
+ all_combine_code/stage_extra_files/dev/sample.txt filter=lfs diff=lfs merge=lfs -text
111
+ all_combine_code/stage_extra_files/output.json filter=lfs diff=lfs merge=lfs -text
all_combine_code/.env ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GOOGLE_API_KEY=AIzaSyAzo1_bhEC8ASX5V-POgXQYArc12eS7Ln4 #AIzaSyAnXCwQEpRmekZOH_l6Pbpm2ccQpEWxMEw
2
+ # GOOGLE_CX=479d684fcf07c49ed #e01019bc2a8e944dc
3
+ # OPENAI_API_KEY=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
4
+
5
+ #LUSA_API_KEY=bd3e6a99-874e-474a-b238-3d2a9e8bf6bf
6
+ GOOGLE_API_KEY=AIzaSyAnXCwQEpRmekZOH_l6Pbpm2ccQpEWxMEw
7
+ GOOGLE_CX=479d684fcf07c49ed
8
+ # OPENAI_API_KEY=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
9
+ OPENAI_API_KEY=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
10
+ OPENROUTER_API_KEY=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
11
+ OPENROUTER_URL=https://openrouter.ai/api/v1
12
+ GEMINI_API_KEY=AIzaSyAwpQ8oPIc9AywvbrT8Jdd-FMzjLzosfyI
13
+ GROQ_API_KEY=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
14
+ TOOL_CALL_MODEL=openai/gpt-5.1-chat
15
+ #CODE_CREATE_MODEL=x-ai/grok-code-fast-1
16
+ CODE_CREATE_MODEL=gpt-4.1-2025-04-14
17
+
18
+ AI_CHAT_HISTORY_S3=delvant-ai-all-models
19
+ HISTORY_MAX_FOLDER_COUNT=500
20
+ PL_HISTORY_MAX_FOLDER_COUNT=200
21
+
22
+ MONGO_URI=mongodb+srv://dev:zzVIduQiBkc8Wstz@delveant.wg3g6m.mongodb.net/
23
+ SUB_DOMAIN=kovaioncrmstage94320
24
+
25
+ FLOW_URL=https://integrate.delveant.cloud/api/v1/flow/executeflow
26
+ CORE_API_URL=https://coreapi.delveant.cloud/api/v1
27
+ FLOW_URL_API=https://coreapi.delveant.cloud/api/v1/automatelead
28
+
29
+ TENANT_ID=a758fb60-2fef-4a98-be9d-a1820297556b
30
+ CLIENT_ID=686034a6-11ee-4659-a225-5b56915828e0
31
+ CLIENT_SECRET=F-X8Q~CAScz7.2imThjYs7inSAStZ8cFLYtxGceR
32
+
33
+ SANITIZER_SALT=e7576e0f19400cd8347050bcd67a2dd583286c909aac0a822a2bbc65ddb2b22e
34
+ SANITIZER_SECRET_KEY=wuo1phSbKySaacnzy-iS_Jji2TiGnEpnb5lvN6uOsrQ=
35
+
36
+ EMAIL_API_MICROSOFT=https://coreapi.delveant.cloud/api/v1/oauth/microsoft/send-email
37
+ EMAIL_API_GOOGLE=https://coreapi.delveant.cloud/api/v1/oauth/google/send-email
38
+
39
+ AUNTHENTICATE_URL=https://coreapi.delveant.cloud/api/v1/oauth/authenticated-email
40
+ SCHEDULE_MICROSOFT=https://coreapi.delveant.cloud/api/v1/oauth/microsoft/schedule-meeting
41
+ SCHEDULE_GOOGLE=https://coreapi.delveant.cloud/api/v1/oauth/google/schedule-meeting
42
+ LEAD_API_URL=https://integrate.kovaion.cloud/api/v1/permission/updatedUpload
43
+
44
+ AWS_ACCESS_KEY=AKIAZEBAZEFJRQURGGYZ
45
+ AWS_SECRET_KEY=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
46
+ AWS_REGION=ap-south-1
47
+ BEDROCK_API_KEY=ABSKQmVkcm9ja0FQSUtleS13bnd6LWF0LTYyNzEzMzk4MTAxMTpZOTRJaEp0R3RCOHRTMzY2S1hzOTNuVXB3b2FvZ3JBdkRHQTh2Vi9hWlFPNWppV0hYT1N0S1I1dmpEST0=
48
+
49
+ FINANCE_MONGO_URI=mongodb+srv://preprod:pfPDxshbRQ1p6qYV@cluster0.nnian.mongodb.net/
50
+ FINANCE_DB_NAME="engage"
51
+ FINANCE_COLLECTION_NAME="FinanceHub_PL Report"
52
+ PL_HISTORY_S3=delveant-ai
53
+
54
+ SPORTZIA_MONGO_URI=mongodb+srv://preprod:48Kzz9AwoKvZqn4C@preprod.wg3g6m.mongodb.net/
55
+ SPORTZIA_DB_NAME=builderpreprod
56
+ SPORTZI_COLLECTION_NMAE=Sportzia_ChatBot
57
+
58
+ VITE_RESPONSE_ENCRYPTION_KEY=default_secret_key_32_chars_long!!
59
+ VITE_PAYLOAD_ENCRYPTION_KEY=default_secret_key_32_chars_long!!
all_combine_code/.gitignore ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python compiled files
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+
7
+ # Ignore pycache specifically in tools_ folder
8
+ tools_/__pycache__/
9
+
10
+ # Virtual environment
11
+ venv/
12
+ .venv
13
+
14
+ # IDE files (PyCharm)
15
+ .idea/
16
+ *.iml
17
+
18
+ # Logs
19
+ *.log
20
+
21
+ # OS files
22
+ .DS_Store
23
+ Thumbs.db
24
+
25
+
26
+ #git rm -r --cached .
27
+ #git add .
28
+
all_combine_code/Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-bullseye
2
+
3
+ WORKDIR /app
4
+
5
+ RUN mkdir -p \
6
+ /app/tmp \
7
+ /app/vector_dbs \
8
+ && chmod -R 775 /app
9
+
10
+ #ENV TORCHINDUCTOR_CACHE_DIR=/app/tmp/torchinductor
11
+
12
+ COPY . /app
13
+
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
+
16
+ EXPOSE 5000
17
+
18
+ CMD ["gunicorn","app:app","-k","uvicorn.workers.UvicornWorker","-w","8","--preload","--timeout","300","--keep-alive","10","--bind","0.0.0.0:5000"]
19
+
all_combine_code/__init__.py ADDED
File without changes
all_combine_code/agents_/__init__.py ADDED
File without changes
all_combine_code/agents_/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (176 Bytes). View file
 
all_combine_code/agents_/delveant_prompts/__init__.py ADDED
File without changes
all_combine_code/agents_/delveant_prompts/crm_leads_agents.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+
4
+ def mongodb_query_generator_leads(
5
+ input_message,
6
+ data,
7
+ user_id,
8
+ data_with_datatype,
9
+ db_name,
10
+ collection_name, source
11
+ ):
12
+ return f"""
13
+ You are a MongoDB query expert.
14
+
15
+ Your task:
16
+ Generate a valid PyMongo query JSON based on the user request and collection sample documents.
17
+
18
+ - All records inside `isDeleted` available, **isDeleted=true means it's deleted records**, Your developed all pipeline code inside must Not consider this records.
19
+ - Strictly use all pipeline inside, follow concepts = {{isDeleted:{{$ne:true}}}}
20
+ - NEVER output Markdown, text, or comments.
21
+
22
+
23
+ ### INPUTS
24
+
25
+ User Query:
26
+ {input_message}
27
+
28
+ Database: {db_name}
29
+ Collection: {collection_name}
30
+
31
+ FIELDS & TYPES (STRICT – CASE SENSITIVE):
32
+ {data_with_datatype}
33
+
34
+ SCHEMA SAMPLE (2 records only, may not be complete):
35
+ {data}
36
+
37
+ SOURCE field(key) inside present names:
38
+ {source}
39
+
40
+ *Note source related queries clearly understand to make the response.*
41
+
42
+
43
+ ### GENERAL RULES:
44
+
45
+ 1) Always return ONLY a valid JSON object.
46
+ 2) Do NOT include explanations.
47
+ 3) Do NOT wrap response in markdown.
48
+ 4) JSON must be directly parsable by json.loads().
49
+ 5) If aggregation is required, use "operation": "aggregate" and provide pipeline.
50
+ 6) If simple filtering is enough, use "operation": "find".
51
+
52
+ -----------------------------------
53
+ DATE RELATED QUERIES (CRITICAL):
54
+
55
+ - Must follow the rules **date related queries inside** year not mention strictly use current year.
56
+ eg . 23 march create lead names (date 23, month march, year not mention use current year)
57
+
58
+ If the user request involves:
59
+ - last N days
60
+ - last month
61
+ - this month
62
+ - today
63
+ - yesterday
64
+ - specific date range
65
+ - between two dates
66
+
67
+ You MUST:
68
+
69
+ 1) Use this import:
70
+ from datetime import datetime, timedelta, timezone
71
+
72
+ 2) Always define:
73
+ now = datetime.now(timezone.utc)
74
+
75
+ 3) Dynamically calculate date ranges using timedelta.
76
+
77
+ 4) NEVER:
78
+ - Compare date fields with raw strings
79
+ - Use ISODate()
80
+ - Use $dateSubtract
81
+ - Hardcode year unless user explicitly provides year
82
+ - Generate invalid python
83
+
84
+ 5) If multiple date fields exist:
85
+ - MUST use "creationdate" if present
86
+ - Otherwise fallback in this order:
87
+ contacteddate → engageddate → closeddate
88
+
89
+ 6) Date condition must be placed:
90
+ - inside "filter" (for find)
91
+ - inside "$match" (for aggregate)
92
+
93
+ 7) For date queries:
94
+ - python_code MUST define variables: start_date, end_date, now
95
+ - In filter/pipeline, reference them as plain strings: "start_date", "end_date"
96
+ - These will be resolved at runtime by the caller
97
+ - Example:
98
+ python_code: "from datetime import datetime, timedelta, timezone\\nnow = datetime.now(timezone.utc)\\nstart_date = datetime(now.year, 2, 23, tzinfo=timezone.utc)\\nend_date = datetime(now.year, 2, 27, tzinfo=timezone.utc)"
99
+ filter: {{"creationdate": {{"$gte": "start_date", "$lt": "end_date"}}
100
+
101
+
102
+ 8) TOP / BEST LEADS RULE (CRITICAL):
103
+
104
+ - Any query containing **“top / best / top N leads”, Must follow the below pipeline concept.
105
+ - pipeline = [{{'$addFields': {{'leadscore_num': {{'$convert': {{'input': {{'$trim': {{'input': {{'$toString': '$leadscore'}}}}}}, 'to': 'double', 'onError': None, 'onNull': None}}}}, 'firstname_str': {{'$trim': {{'input': {{'$toString': {{'$ifNull': ['$firstname', '']}}}}}}}}, 'lastname_str': {{'$trim': {{'input': {{'$toString': {{'$ifNull': ['$lastname', '']}}}}}}}}}}}}, {{'$addFields': {{'full_name': {{'$trim': {{'input': {{'$concat': [{{'$ifNull': ['$firstname_str', '']}}, {{'$cond': [{{'$and': [{{'$ne': ['$firstname_str', '']}}, {{'$ne': ['$lastname_str', '']}}]}}, ' ', '']}}, {{'$ifNull': ['$lastname_str', '']}}]}}}}}}}}}}, {{'$sort': {{'leadscore_num': -1}}}}, {{'$limit': 3}}, {{'$project': {{'_id': 0, 'full_name': 1, 'email': 1, 'phone': 1}}}}]
106
+
107
+ 9) pipeline or find query inside , user message need to set the limits.
108
+
109
+ 10) DATE OUTPUT (STRICT – MUST FOLLOW)
110
+ - NEVER project date fields directly (e.g., "creationdate": 1 ❌)
111
+ - ALWAYS use:
112
+ "$dateToString": {{ "format": "%d-%m-%Y", "date": "$<field>" }}
113
+ - Output must be string only.
114
+ - Your response inside all date must present "%d-%m-%Y" this format only.
115
+
116
+ 11) All response inside strictly avoid `_id` this field.
117
+
118
+
119
+ ### OUTPUT FORMAT:
120
+
121
+ If NOT a date query:
122
+
123
+ {{
124
+ "operation": "find" | "aggregate",
125
+ "python_code" : null
126
+ "filter": {{}},
127
+ "projection": {{}},
128
+ "pipeline": [],
129
+ "sort": {{}},
130
+ "limit": null or int value
131
+ }}
132
+
133
+ If it IS a date query:
134
+
135
+ {{
136
+ "operation": "find" | "aggregate",
137
+ "python_code": "from datetime import datetime, timedelta, timezone\\nnow = datetime.now(timezone.utc)\\n# date calculation here",
138
+ "filter": {{}},
139
+ "projection": {{}},
140
+ "pipeline": [],
141
+ "sort": {{}},
142
+ "limit": null or int value
143
+ }}
144
+
145
+ -----------------------------------
146
+ STRICT REQUIREMENTS:
147
+
148
+ - Output must be valid JSON
149
+ - Do not include comments
150
+ - Do not include explanation text
151
+ - Do not include markdown formatting
152
+ - Field names must match exactly
153
+ - Use double quotes only
154
+ - Ensure JSON is syntactically correct
155
+
156
+ Return ONLY the JSON object.
157
+ """
all_combine_code/agents_/delveant_prompts/db_collection_name_finder.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def db_collection_find(input, user_id, person_id, collection_keywords, priveous_chat_history_query) -> str:
2
+ return f"""
3
+ You are the **Accurate CRM Query Formatter and Database Collection Name Finder Agent**.
4
+
5
+ ---
6
+
7
+ ### USER INPUT
8
+ {input}
9
+
10
+ ### PREVIOUS CHAT HISTORY QUERY:
11
+ {priveous_chat_history_query}
12
+
13
+
14
+ ## PRIMARY OBJECTIVE
15
+
16
+ Your task is to generate:
17
+ 1. User input query clearly understand any spell mistake or sentence mistake present time correct it. otherwise return the same user query.
18
+ 2. The **correct CRM database collection name**, based on the user’s intent and query context.
19
+ 3. If the user’s input query is **self-related** (e.g., contains words like *my*, *personal*, etc.) — for example, “show my details” or “please give my details” — then `{user_id}` or `{person_id}` this two id's any one added to make the professional questions format.
20
+ 4. **User Input** suppose not clear format/meaning, kindly use **Previous chat history query**
21
+ ---
22
+
23
+ ## STEP-BY-STEP LOGIC
24
+
25
+ ### 1. Understand USER QUERY
26
+ - Interpret the intent of the user query carefully.
27
+ - Rephrase it to a **professional CRM-style analytic question** (e.g., "Show me my best lead source" → "Identify the best-performing lead source for user {user_id} based on lead scoring metrics.").
28
+
29
+ ---
30
+
31
+ ## 2. DATABASE COLLECTION MAPPING LOGIC:
32
+ {collection_keywords}
33
+ ---
34
+
35
+ ## 3. Self-Related (Me / My) Handling:
36
+ **Decision Table**
37
+
38
+ | Step | Check | Requirement | If Fails |
39
+ | ---- | ------------------------- | ---------------------------------------------------------------------------------------------- | --------------- |
40
+ | 1 | Self reference present | `my`, `mine`, `myself`, `for me`, `about me`, `show me my`, `give me my`, `tell me my`, or `I` | ❌ Do NOT inject |
41
+ | 2 | Other identifiers absent | No names, no user IDs, no `by/for/assigned to <person>` | ❌ Do NOT inject |
42
+ | 3 | Personal ownership intent | Refers to user's own records (tasks, deals, contacts, opportunities, etc.) | ❌ Do NOT inject |
43
+ | ✅ | All 3 pass | Inject `{user_id}` | ✅ Inject |
44
+
45
+
46
+ **Examples**
47
+
48
+ | Query | Inject `{user_id}`? | Output |
49
+ | ---------------------------------- | ------------------- | --------------------------------------------- |
50
+ | show my deals | ✅ Yes | Show the deals for user `{user_id}` |
51
+ | give me my tasks for today | ✅ Yes | Give the tasks for today for user `{user_id}` |
52
+ | list my opportunities | ✅ Yes | List the opportunities for user `{user_id}` |
53
+ | show deals closed by Satheesh | ❌ No | No injection |
54
+ | list top 10 deals | ❌ No | No injection |
55
+ | show opportunities for account ABC | ❌ No | No injection |
56
+ | Show me deals won in the last 6 months | ❌ No | No injection |
57
+ | Please give the details | ❌ No | No injection |
58
+
59
+
60
+
61
+ ## 4.Database Collection Classification (Strict):
62
+ | Priority | Intent Type | Keywords / Signals | Collection |
63
+ |----------|-------------|-------------------|-------------|
64
+ | 1 | Audit / Logs / Tracking | meeting, schedule, participants, logs, email status, sent, failed, email logs, not contacted | admin.audit.CRM_View |
65
+ | 2 | Tasks / Activities / Work | task, tasks, follow-up, followups, activities, to-do, reminder, pending, assigned, today, overdue | CRM_Task_View |
66
+ | 3 | Revenue / Deals / Sales | revenue, amount, payment, deal, deals, quote, quotation, pipeline, opportunity, sales | CRM_Opportunity_View |
67
+ | 4 | Accounts / Companies | account, accounts, company, companies, client, customer, firm, organization, business | CRM_Accounts_View |
68
+ | 5 | Contacts / Events | contact, contacts, event, event name, event register | CRM_Contacts_View |
69
+ | 6 | Leads / Person Specific | person name, leads info, personal email id | CRM_Leads_View |
70
+ | 7 | Comparative (Dynamic) | best, top, highest, maximum, most | decide by entity |
71
+
72
+ Apply in this exact order:
73
+
74
+ 1. If meeting/email logs/audit → admin.audit.CRM_View (ALWAYS override)
75
+ 2. If task/activity keywords → CRM_Task_View
76
+ 3. If revenue/deal keywords → CRM_Opportunity_View
77
+ 4. If account/company keywords → CRM_Accounts_View
78
+ 5. If contact/event keywords → CRM_Contacts_View
79
+ 6. If person name or leads data info → CRM_Leads_View
80
+
81
+
82
+ EXTRA EMAIL RULES (STRICT)
83
+
84
+ • Email ID of a person → CRM_Leads_View (eg . show my mail id, show ravi mail id)
85
+ • Contact details (contains word "contact") → CRM_Contacts_View
86
+ • Email status/log/count → admin.audit.CRM_View
87
+
88
+ ### 5. OUTPUT RULES
89
+
90
+ - Return **only valid JSON**, formatted exactly as below.
91
+ - Do **not** include explanations, markdown, or extra text.
92
+ - Do **not** repeat input or chat history in the output.
93
+ - If query inside 'leads or lead' words mention, use the 'CRM_Leads_View'
94
+
95
+ **Output JSON structure:**
96
+
97
+ ```json
98
+ {{
99
+ "query": "corrected query --> (keep in mind user query 98% don't change.only correct the spell mistake or self related query add {user_id}).",
100
+ "db_collection_name": "CRM_Collection_Name"
101
+ }}
102
+ ```
103
+
104
+ ### Notes:
105
+ - If the user input query is at least 45% clear and meaningful, use the current `user input` query.
106
+ - If the user input query is less than 45% clear, incomplete, vague, or not meaningful, then use the Previous Chat History Query instead.
107
+ - Query inside any mailid or name or comany name don't correct the spell mistake, because this is case-sensitive.
108
+ eg . user_input inside : `manoiiifecs@hgamil.com` this present some spell mistake, strictly this format don't correct it.
109
+
110
+ - If a company name is mentioned in short form (e.g., CDSL, AI), DO NOT expand it to its full form.
111
+ Always preserve the exact company name format as provided in the user input.
112
+
113
+ """
all_combine_code/agents_/delveant_prompts/tool_clasification_agent.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from textwrap import dedent
2
+
3
+ TOOLS = [
4
+ # 1. CRM Info
5
+ {
6
+ "type": "function",
7
+ "function": {
8
+ "name": "get_crm_info",
9
+ "description": "Retrieve CRM Database DATA/MongoDB information such as leads, contacts, tasks, meetings, opportunities, accounts, revenue,comapny details ,analytics, and reports.",
10
+ "parameters": {
11
+ "type": "object",
12
+ "properties": {
13
+ "user_message": {"type": "string", "description": "The original user query"},
14
+ "collection": {
15
+ "type": "string",
16
+ "enum": [
17
+ "CRM_Task_View",
18
+ "CRM_Leads_View",
19
+ "CRM_Contacts_View",
20
+ "CRM_Opportunity_View",
21
+ "CRM_Accounts_View",
22
+ "admin.audit.CRM_View"
23
+ ],
24
+ "description": "The CRM/MongoDB collection to query"
25
+ }
26
+ },
27
+ "required": ["user_message", "collection"]
28
+ }
29
+ }
30
+ },
31
+
32
+ # 2. Send_email_api
33
+ {
34
+ "type": "function",
35
+ "function": {
36
+ "name": "send_email_api",
37
+ "description": "Send an email with subject, body, optional CC, and attachments.",
38
+ "parameters": {
39
+ "type": "object",
40
+ "properties": {
41
+ "to_email": {"type": "string"},
42
+ "subject": {"type": "string"},
43
+ "body": {"type": "string"},
44
+ "provider": {
45
+ "type": "string",
46
+ "description": "Email provider (google or microsoft)",
47
+ "enum": ["google", "microsoft"],
48
+ },
49
+ "cc_email": {"type": "string"},
50
+ "attachments": {
51
+ "type": "array",
52
+ "items": {
53
+ "type": "object",
54
+ "properties": {
55
+ "url": {"type": "string"},
56
+ "filename": {"type": "string"},
57
+ "mimeType": {"type": "string"}
58
+ },
59
+ "required": ["url", "filename", "mimeType"]
60
+ }
61
+ }
62
+ },
63
+ "required": ["to_email", "subject", "body", "provider"]
64
+ }
65
+ }
66
+ },
67
+ # 3. Create lead
68
+ {
69
+ "type": "function",
70
+ "function": {
71
+ "name": "create_lead",
72
+ "description": "Create a new lead in CRM. Requires email, company name, and the lead's requirement.",
73
+ "parameters": {
74
+ "type": "object",
75
+ "properties": {
76
+ "email": {"type": "string"},
77
+ "company_name": {"type": "string"},
78
+ "requirement": {"type": "string"}
79
+ },
80
+ "required": ["email", "company_name", "requirement"]
81
+ }
82
+ }
83
+ },
84
+
85
+ # 4.Schedule_meeting_api
86
+ {
87
+ "type": "function",
88
+ "function": {
89
+ "name": "schedule_meeting_api",
90
+ "description": "Schedule a meeting.",
91
+ "parameters": {
92
+ "type": "object",
93
+ "properties": {
94
+ "title": {"type": "string"},
95
+ "recipient": {"type": "string"},
96
+ "description": {"type": "string"},
97
+ "start_time": {"type": "string"},
98
+ "end_time": {"type": "string"},
99
+ "time_zone": {"type": "string"}
100
+ },
101
+ "required": ["title", "recipient", "description", "start_time", "end_time"]
102
+ }
103
+ }
104
+ },
105
+ # 5. Update query
106
+ {
107
+ "type": "function",
108
+ "function": {
109
+ "name": "update_query",
110
+ "description": "Update an existing CRM record.",
111
+ "parameters": {
112
+ "type": "object",
113
+ "properties": {
114
+ "field_name": {"type": "string"},
115
+ "new_value": {"type": "string"},
116
+ "collection": {"type": "string"}
117
+ },
118
+ "required": ["field_name", "new_value", "collection"]
119
+ }
120
+ }
121
+ },
122
+
123
+ # 6. Greetings
124
+ {
125
+ "type": "function",
126
+ "function": {
127
+ "name": "greetings_api",
128
+ "description": "Handle greetings, small talk, or identity questions.",
129
+ "parameters": {
130
+ "type": "object",
131
+ "properties": {
132
+ "message": {"type": "string"}
133
+ },
134
+ "required": ["message"]
135
+ }
136
+ }
137
+ },
138
+
139
+ # 7. PDF File QA
140
+ {
141
+ "type": "function",
142
+ "function": {
143
+ "name": "pdf_file_api",
144
+ "description": "Extract or explain information from uploaded PDF files.",
145
+ "parameters": {
146
+ "type": "object",
147
+ "properties": {
148
+ "question": {"type": "string"}
149
+ },
150
+ "required": ["question"]
151
+ }
152
+ }
153
+ },
154
+
155
+ # 8. General Chat
156
+ {
157
+ "type": "function",
158
+ "function": {
159
+ "name": "general_chatbot_api",
160
+ "description": "MANDATORY fallback tool. Must be called for any non-CRM, non-email, non-meeting, non-PDF, non-greeting question. Text responses are forbidden.",
161
+ "parameters": {
162
+ "type": "object",
163
+ "properties": {
164
+ "question": {"type": "string"}
165
+ },
166
+ "required": ["question"]
167
+ }
168
+ }
169
+ }
170
+ ]
171
+
172
+
173
+ def tool_classification_agent():
174
+ return """
175
+ # You are a strict enterprise tool **Router** that analyzes intent, selects exactly one correct below given tool, never responds in text, and always outputs a valid tool call or asks for missing required fields draft text format.
176
+
177
+ **Available tools:**
178
+
179
+ | **No.** | **Tool Name** | **When to Use / Description** |
180
+ | ------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
+ | 1 | **send_email_api** | Use for any requests related to sending emails, including subject and content. |
182
+ | 2 | **get_crm_info** | Use for CRM /MongoDB/ database related queries such as: <br>• Leads, contacts, opportunities, tasks <br>• Salary, revenue, sales revenue <br>• Email or company lookups <br>• Events or task related queries <br>• Analytics, updates, or retrieving database info <br>• MongoDB data extraction, counts,sums,totals |
183
+ | 3 | **create_lead** | Use for creating a new lead in the CRM system using email and company name. |
184
+ | 4 | **schedule_meeting_api** | Use for scheduling meetings in the calendar. |
185
+ | 5 | **update_query** | Use for updating or changing existing CRM records. |
186
+ | 6 | **greetings_api** | Use when the user input is a greeting, polite message, small talk, or asks about the assistant’s identity. <br>Examples: “hi”, “hello”, “how are you”, “what is your name”, “who are you” |
187
+ | 7 | **pdf_file_api** | Use for questions related to extracting or retrieving information from PDF files. |
188
+ | 8 | **general_chatbot_api** | General questions answering like (eg. what is java?, what is oops, how to train the ml models? |
189
+
190
+ # **CRM Query Classification Table (6 Collections) this based in tool name is `get_crm_info`**
191
+
192
+ | **Rule No** | **Query Type / Condition** | **Keywords / Indicators** | **Collection to Use** | **Notes / Examples** |
193
+ | ----------- | --------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------- | -------------------------------------------------- |
194
+ | **1** | Task-related queries | tasks, follow-ups, activities, current work, pending actions, to-do | **CRM_Task_View** | "Show my pending tasks", "List today’s follow-ups" |
195
+ | **2A** | Person name mentioned | Any human name or mobile, phone, contact number, mail, email details | **CRM_Leads_View** | "Show details of Rahul", "Lead info for Anitha" |
196
+ | **2B** | Communication-related queries | events, event register details | **CRM_Contacts_View** | Contacts or event extraction strictly uses this |
197
+ | **3** | Meeting & email audit queries | meeting schedule, next meeting, meeting time, participants, email status, sent/failed emails, logs | **admin.audit.CRM_View** | Includes “leads not contacted in last 10 days” |
198
+ | **4** | Opportunity & revenue queries | revenue, amount, payment, deal, deals, quotation, quote, pipeline | **CRM_Opportunity_View** | "Show total revenue this month", "List open deals" |
199
+ | **5** | Best / top / highest analysis | best, top, highest, maximum, most | **Dynamic (based on entity)** | Compute using keys like score, amount, probability |
200
+ | **6** | Account / company-related queries | accounts, companies, organizations, clients, customers, firms, businesses, enterprises | **CRM_Accounts_View** | "How many accounts assigned in Jan 2026?" |
201
+
202
+
203
+ **Tool selection rules (VERY IMPORTANT)**
204
+ - If the user’s request is about CRM data in any way (leads, contacts, opportunities, tasks, meetings, emails, accounts, metrics, reports), you MUST use 'get_crm_info' unless the user is clearly asking to create/update something."
205
+ - Use 'get_crm_info' when the user asks to *summarize, list, show, get, fetch, report, analyze, understand, group, classify, categorize, segment, score, or suggest next actions* for meetings, leads, contacts, opportunities, tasks, emails, or any CRM-related data.
206
+
207
+ 1)`get_crm_info`: (below rules understand to must return `get_crm_info`):
208
+
209
+ - If user queries is related to the CRM/MongoDB/Database data or information extraction releted strictly return this tool.
210
+ - Query word is "count,sum, leads name, event registrations, register records, analytics, meetings calls details, etc..
211
+ - If query is Task data extraction related, Lead data extractions related, Contacts data extractions related, Opportunities data extractions related, Acoounts data extractions related.
212
+ - **show, list, get, fetch, summarize, analyze, report, count, total, pending, upcoming, status, details, history, dashboard, metrics, performance, pipeline, revenue, sales, events, meetings (if not creating), emails (if not sending), tasks, leads, contacts, accounts, opportunities**
213
+ - **Important Note:** `What is my mail id or Show my mail id or show my details for U0006` this format self related queries must return "get_crm_info" tool.
214
+
215
+ Sample Example Questions:
216
+ * Summarize today’s meetings
217
+ * What is my mail ID / show my mail ID
218
+ * Show my pending tasks
219
+ * Give me lead status report
220
+ * Analyze my pipeline for this quarter
221
+ * Group my leads into cold, warm, and hot categories and suggest next actions for each
222
+ * Which leads should I follow up with today
223
+ * Tell me which opportunities are most likely to close this month
224
+ * Show the top 5 sources with count of contacts registered for the event *“Intelligent HR & Payroll for NextGen Workforce 2026”* based on source
225
+ * What is the total registrations for the event *“Intelligent HR & Payroll for NextGen Workforce 2026”*
226
+ * How many emails has been add from santhosh last year?
227
+ * Please give the `Martinez20 Technologies` company all detail like employee count,etc
228
+
229
+
230
+ 2)`send_email_api`: (below rules understand to must return `send_email_api`):
231
+
232
+ - User query related for send email ,email send, compose, or draft emails or this similar.
233
+ - **Mandatory fields are:** 'to_email', 'subject', and 'body'. Optional: 'cc_email', 'attachments'
234
+ - If above mention `Mandatory fields` are missing, ask ONLY for the missing ones in a single message, then generate the draft.
235
+ - Please provide the following details so I can draft the email for you: \n -**to_mail** \n \n- **Subject** of the email \n- **Body/content** of the email.
236
+
237
+ 3)`create_lead`: (below rules understand to must return `create_lead`):
238
+
239
+ - Use 'create_lead' when the user asks to create, add, register, or open a new lead
240
+ - Mandatory fields: 'email', 'company_name', and 'requirement'
241
+ - If the user provides multiple fields in one request, extract ALL of them and call the API immediately
242
+ - If above mention `Mandatory fields` fields are missing, ask ONLY for the missing ones in a single message, then generate the draft..
243
+ - If the lead already exists, respond with details and provide the view link.
244
+ - Once all fields are collected, call the API immediately.
245
+ - Confirm success or failure clearly to the user.
246
+ - When the user message contains multiple data points (emails, company names, requirements).
247
+
248
+ 4)`schedule_meeting_api`:( below rules understand to must return `schedule_meeting_api`):
249
+
250
+ - When the user explicitly wants to schedule/arrange/book/assign a meeting (e.g., 'schedule a meeting', 'book a demo').
251
+ - **Mandatory fields:** 'title/summary', 'recipient/attendees', 'date', 'startTime', 'endTime'. Optional: 'description', 'time_zone'
252
+ - If above mention `Mandatory fields` are missing, ask ONLY for the missing ones in a single message, then generate the draft.
253
+
254
+ - CRITICAL: If the user mentions a timezone (e.g., 'Europe/London', 'time zone is Europe/London'), extract ONLY the timezone value (e.g., 'Europe/London') and pass it as the 'time_zone' parameter. DO NOT include timezone information in the 'description' field.\n"
255
+ - IMPORTANT: For dates in DD/MM/YYYY format (e.g., '05/12/2025'), interpret as day/month/year (5th December 2025, not May 12th). When passing to start_time and end_time, use clear format like '2025-12-05 1:00 PM'.\n"
256
+ - The 'description' field should contain meeting details but NEVER timezone information.
257
+ - If no timezone is mentioned, default to 'Asia/Kolkata'
258
+
259
+ - **IMPORTANT NOTE:** Do NOT use this tool when the user asks to summarize, list, or check meetings; in those cases, use 'get_crm_info'.
260
+
261
+ *Note: User sometimes give the very short format query like only `schedule meeting` or `book meeting` this similar very short format time, above mention "mandatory fields" what is missing that used to send the draft message.*
262
+
263
+ 5)`greetings_api`:
264
+
265
+ - If the user input is a greeting, polite message, small talk, or asks about the assistant’s identity.
266
+ - ALSO route here when the query specifically mentions "Delvant CRM" as a product/platform explanation request.
267
+
268
+ Special Condition:
269
+ - If the phrase "Delvant CRM" appears together in the query
270
+ AND the intent is explain/what/how/about/usage/guide/help,
271
+ ALWAYS use greetings_api.
272
+
273
+ Examples:
274
+ - What is Delvant CRM
275
+ - Explain Delvant CRM
276
+ - How to use Delvant CRM
277
+ - About Delvant CRM
278
+ - Help me understand Delvant CRM
279
+
280
+ Important:
281
+ - Only trigger when both words appear together: "Delvant CRM"
282
+ - If only "CRM" or only "Delvant" appears → follow normal routing rules.
283
+
284
+
285
+ 6)`pdf_file_api`: (below rules understand to must return `pdf_file_api`):
286
+
287
+ - ** **only** for questions that require information from the uploaded PDF files inside the data.
288
+
289
+ - The query **must explicitly or implicitly refer to PDF content**, such as:
290
+ * Asking for personal details found in the PDF (email, phone number, address, etc.)
291
+ * Asking questions like:
292
+
293
+ * “Please give my email and phone number from the PDF”
294
+ * “What is a transformer according to my PDF data?”
295
+ * “Explain this topic using my PDF file”
296
+ * "What is my name use pdf file"
297
+
298
+ * If the question mentions:
299
+ * *my pdf file*
300
+ * *pdf data*
301
+ * *pdf document*
302
+ * *this pdf*
303
+ * *use pdf*
304
+ * or asks to extract / explain information **from the PDF**
305
+
306
+ *Note: query inside `not use pdf` or `not consider pdf` or this similar word mention time strictly avoid `pdf_file_api`*
307
+
308
+ 7)`general_chatbot_api`: (below rules understand to must return `general_chatbot_api`):
309
+
310
+ - Use when the user asks general, informational, or explanatory questions (what is, how to, explain, define, usage, guide, integrate, examples).
311
+ - If no other tool matches, ALWAYS call this tool.
312
+ - NEVER answer in text.
313
+ - Action: call general_chatbot_api({"question": "<user message>"}).
314
+
315
+
316
+ ### VERY CRITICAL NOTES:
317
+ 1. If query contains "lead" AND ("create" OR "new" OR "add") → call create_lead
318
+ 2. Else if query contains other create intents → return draft message
319
+ 3. Lead creation has highest priority: if the request mentions "lead" with create/add/new, ALWAYS call `create_lead` tool and NEVER return the draft message.
320
+
321
+ | Condition (User Intent) | Action |
322
+ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
323
+ | Request is **create new task / account / contact / any create except lead** | Return exactly: `"Development is in progress by the technical team. Launching soon."` |
324
+ | Request is **create lead / new lead / add lead / register lead / any lead creation** | **Do NOT return message** → strictly call `create_lead` tool |
325
+
326
+ """
327
+
all_combine_code/all_services_/__pycache__/delveant_core_services.cpython-313.pyc ADDED
Binary file (42.7 kB). View file
 
all_combine_code/all_services_/delveant_core_services.py ADDED
@@ -0,0 +1,1011 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio,time
2
+ import json
3
+ import warnings
4
+
5
+ warnings.filterwarnings("ignore")
6
+ import subprocess, sys, os, re
7
+ import datetime,threading
8
+ import logging,requests,httpx
9
+
10
+ from
11
+ from models_.delveant_llm_model_.multi_agent_llm import (MultiAgentLLM,
12
+ CodeErrorFixer)
13
+ from tools_.delveant_all_tools_.extracted_json_output import (extract_json_outputs)
14
+ from tools_.delveant_all_tools_.s3_history_id_details import get_sorted_memory_ids
15
+
16
+ from tools_.delveant_all_tools_.encrypt_and_decrypt import encrypt_payload,decrypt_payload
17
+
18
+ from agents_.plot_finder import plot_finder_agent
19
+ from agents_.coding_error_fix_agent import code_error_resolver
20
+ from models_.delveant_llm_model_.tool_calling_model import tool_finder_llm
21
+ from rag_method.chat_with_pdf import question_and_answering_rag
22
+
23
+ from dotenv import load_dotenv
24
+ load_dotenv()
25
+
26
+ from pymongo import MongoClient
27
+ import mimetypes
28
+ from urllib.parse import urlparse
29
+ import dateparser
30
+ import pytz
31
+ from datetime import datetime,timezone
32
+
33
+ from textwrap import dedent
34
+
35
+ MONGO_URI = os.getenv("MONGO_URI")
36
+ client = MongoClient(MONGO_URI)
37
+
38
+ def convert_mongo_python_repr(text):
39
+ text = re.sub(
40
+ r"\{\s*['\"]?\$oid['\"]?\s*:\s*['\"]([0-9a-fA-F]+)['\"]\s*\}",
41
+ r'"\1"',
42
+ text
43
+ )
44
+ text = re.sub(
45
+ r"ObjectId\(['\"]([0-9a-fA-F]+)['\"]\)",
46
+ r'"\1"',
47
+ text
48
+ )
49
+ def datetime_replacer(match):
50
+ parts = match.group(1).split(",")
51
+ parts = [int(p.strip()) for p in parts[:3]]
52
+ dt = datetime.date(*parts)
53
+ return '"' + dt.strftime("%Y-%m-%d") + '"'
54
+ text = re.sub(
55
+ r"datetime\.datetime\(([^)]+)\)",
56
+ datetime_replacer,
57
+ text
58
+ )
59
+ text = re.sub(
60
+ r"(['\"]?)([A-Za-z0-9_]+)\1\s*:",
61
+ r'"\2": ',
62
+ text
63
+ )
64
+ def value_replacer(match):
65
+ value = match.group(1).strip()
66
+ if re.match(r"^-?\d+(\.\d+)?$", value):
67
+ return value
68
+ if value in ["true", "false", "null"]:
69
+ return value
70
+ return '"' + value.strip("'\"") + '"'
71
+ text = re.sub(
72
+ r":\s*([^,{}\s][^,{}]*)",
73
+ lambda m: ": " + value_replacer(m),
74
+ text
75
+ )
76
+ return text
77
+
78
+ async def plot_finder(prompt):
79
+ llm = MultiAgentLLM()
80
+ output = await llm.plot_finder(prompt)
81
+ return output
82
+
83
+ async def greetings_api(prompt):
84
+ llm = MultiAgentLLM()
85
+ output = await llm.generate_ai_response_greetings(prompt)
86
+ return output
87
+
88
+ async def pdf_file_api(query,id):
89
+ output = await question_and_answering_rag(query,id)
90
+ return output
91
+
92
+ async def empty_responser(prompt):
93
+ llm = MultiAgentLLM()
94
+ output = await llm.empty_list_responser(prompt)
95
+ return output
96
+
97
+ async def error_code_handler(prompt):
98
+ llm = CodeErrorFixer()
99
+ output = await llm.code_debuger(prompt)
100
+ return output
101
+
102
+ async def general_chatbot_api(prompt):
103
+ llm = MultiAgentLLM()
104
+ output = await llm.general_questions_answer(prompt)
105
+ return output
106
+
107
+ def extract_timezone_from_message(message):
108
+ """Extract timezone from user message"""
109
+ tz_patterns = [
110
+ r'time zone is ([A-Za-z/]+)',
111
+ r'timezone is ([A-Za-z/]+)',
112
+ r'in ([A-Za-z]+/[A-Za-z]+)',
113
+ r'([A-Za-z]+/[A-Za-z]+) time',
114
+ ]
115
+
116
+ for pattern in tz_patterns:
117
+ match = re.search(pattern, message, re.IGNORECASE)
118
+ if match:
119
+ return match.group(1)
120
+ return None
121
+
122
+ def run_python_code(path, timeout=40):
123
+ """
124
+ Run Python code with timeout of 60 seconds (1 minute)
125
+ Returns dict with either 'answer' or 'error' key
126
+ """
127
+ try:
128
+ result = subprocess.run(
129
+ [sys.executable, path],
130
+ capture_output=True,
131
+ text=True,
132
+ timeout=80,
133
+ encoding='utf-8'
134
+ )
135
+ if result.returncode != 0:
136
+ error_message = result.stderr.strip() if result.stderr else "Unknown error"
137
+ return {"error": error_message}
138
+ final_responser = result.stdout.strip()
139
+ return {"answer": final_responser}
140
+ except subprocess.TimeoutExpired:
141
+ return {"error": "Script execution timeout (exceeded 1 minute)"}
142
+ except subprocess.CalledProcessError as e:
143
+ error_message = e.stderr.strip() if e.stderr else str(e)
144
+ return {"error": error_message}
145
+ except Exception as e:
146
+ return {"error": str(e)}
147
+
148
+ async def process_successful_result(final_responser, corrected_query):
149
+ import regex as rex
150
+ matches = rex.findall(r'\{(?:[^{}]|(?R))*\}', str(final_responser), rex.DOTALL)
151
+ first_two_records = matches[:2]
152
+ plot_finder_prompt = plot_finder_agent(corrected_query, str(first_two_records))
153
+ decision_find = await plot_finder(plot_finder_prompt)
154
+ clean_final = extract_json_outputs(decision_find)
155
+ matches = re.findall(r'"(.*?)":"(.*?)"', clean_final)
156
+ matches_dict = dict(matches)
157
+ output_format = matches_dict["format_data"]
158
+ plot_output = matches_dict["plot"]
159
+
160
+ if output_format.strip() == "empty":
161
+ final_response_1 = await empty_responser(corrected_query)
162
+ return {"status": "success", "view": "empty", "output": final_response_1,
163
+ "plot": "not_required","corrected_query": corrected_query}
164
+
165
+ elif output_format.strip() == "card":
166
+ return {"status": "success", "view": "card", "output": final_responser,
167
+ "plot": "not_required","corrected_query": corrected_query}
168
+
169
+ elif output_format.strip() =="data":
170
+ return {"status": "success", "view": "data", "plot": plot_output,
171
+ "output": final_responser,"corrected_query": corrected_query}
172
+
173
+
174
+ def parse_datetime_to_iso(dt_str, tz="Asia/Kolkata"):
175
+ if not dt_str:
176
+ return None
177
+
178
+ settings = {
179
+ "RETURN_AS_TIMEZONE_AWARE": True,
180
+ "TIMEZONE": tz,
181
+ "DATE_ORDER": "YMD",
182
+ }
183
+
184
+ dt = dateparser.parse(dt_str, settings=settings)
185
+
186
+ if not dt:
187
+ return None
188
+
189
+ if dt.tzinfo is None:
190
+ tz_obj = pytz.timezone(tz)
191
+ dt = tz_obj.localize(dt)
192
+ dt_utc = dt.astimezone(pytz.UTC)
193
+ return dt_utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
194
+
195
+ def normalize_attendees(attendees):
196
+ if isinstance(attendees, list) and len(attendees) == 1 and "," in attendees[0]:
197
+ return [email.strip() for email in attendees[0].split(",")]
198
+ return attendees
199
+
200
+ async def get_crm_info(input_query, previous_chat_history_query, db_name, user_id, person_id):
201
+ script_path = None
202
+ corrected_query = None
203
+ try:
204
+ obj = DelveAnt()
205
+ final_output, corrected_query, first_python_code, path = await obj.run_file(
206
+ input_query, previous_chat_history_query, db_name, user_id, person_id
207
+ )
208
+ if final_output == "successfully_file_created":
209
+ try:
210
+ script_path = path
211
+ result = run_python_code(script_path, timeout=40)
212
+ if "answer" in result:
213
+ final_responser = result["answer"]
214
+ return await process_successful_result(final_responser, corrected_query)
215
+ elif "error" in result:
216
+ max_retries = 3
217
+ for attempt in range(max_retries):
218
+ print(f"Error fixing attempt {attempt + 1}/{max_retries}")
219
+ with open(script_path, "r", encoding="utf-8") as f:
220
+ file_contents = f.read()
221
+ error_message = result["error"]
222
+ prompt = code_error_resolver(file_contents, error_message, input_query)
223
+ print(prompt)
224
+ chat_response = await error_code_handler(prompt)
225
+ filter_python_code = re.sub(r"```(?:python)?|```", "", chat_response).strip()
226
+ with open(script_path, "w", encoding="utf-8") as f:
227
+ f.write(filter_python_code)
228
+ result = run_python_code(script_path, timeout=40)
229
+ if "answer" in result:
230
+ final_responser = result["answer"]
231
+ return await process_successful_result(final_responser, corrected_query)
232
+ return {
233
+ "status": "error",
234
+ "view": "text",
235
+ "output": f"Failed to execute after {max_retries} attempts. Last error: {result.get('error', 'Unknown error')}",
236
+ "plot": "not_required",
237
+ "corrected_query": corrected_query,
238
+ }
239
+ except Exception as e:
240
+ return {
241
+ "status": "error",
242
+ "view": "text",
243
+ "output": f"Server error: {str(e)}",
244
+ "plot": "not_required",
245
+ "corrected_query": corrected_query if 'corrected_query' in locals() else "query not found error",
246
+ }
247
+
248
+ finally:
249
+ if script_path and os.path.exists(script_path):
250
+ try:
251
+ os.remove(path)
252
+ print(f"Cleaned up: {script_path}")
253
+ except Exception as e:
254
+ print(f"Error deleting {script_path}:", e)
255
+
256
+ else:
257
+ return {"status": "success", "view": "text", "output": final_output, "plot": "not_required","corrected_query": corrected_query}
258
+
259
+ except Exception as e:
260
+ logging.exception("Error processing request")
261
+ return {
262
+ "status": "error",
263
+ "view": "text",
264
+ "output": f"Server error: {str(e)}",
265
+ "plot": "not_required",
266
+ "corrected_query": corrected_query if 'corrected_query' in locals() else "query not found error",
267
+ }
268
+
269
+ async def create_lead(email, company_name, requirement, subdomain, user_email,headers):
270
+ flow_url = os.getenv("FLOW_URL_API")
271
+
272
+ payload = {
273
+ "widgetid": "6880c0ecb384e4a0fe7c901f",
274
+ "subdomain": subdomain,
275
+ "createtype": "manual",
276
+ "assignee": user_email,
277
+ "email": email,
278
+ "company": company_name,
279
+ "requirement": requirement
280
+ }
281
+
282
+ encrypted_string = await encrypt_payload(payload)
283
+
284
+ final_payload = json.dumps({
285
+ "payload": encrypted_string
286
+ })
287
+
288
+
289
+ timeout = httpx.Timeout(120.0)
290
+
291
+ for attempt in range(3):
292
+ try:
293
+ async with httpx.AsyncClient(timeout=timeout) as client:
294
+ response = await client.post(
295
+ flow_url,
296
+ content=final_payload, # or data=final_payload
297
+ headers=headers
298
+ )
299
+ response.raise_for_status()
300
+ final_answer,_ = await decrypt_payload(response.text)
301
+ return {"success": final_answer}
302
+
303
+ except httpx.TimeoutException:
304
+ if attempt == 2:
305
+ answer = {
306
+ "error": "Lead creation API timed out after retries"
307
+ }
308
+ return answer
309
+ await asyncio.sleep(2 ** attempt)
310
+
311
+ except httpx.HTTPStatusError as e:
312
+ answer = {
313
+ "error": f"Lead creation API failed: {e.response.text}"
314
+ }
315
+ return answer
316
+
317
+ except Exception as e:
318
+ answer = {
319
+ "error": f"Internal server error: {str(e)}"
320
+ }
321
+ return answer
322
+
323
+
324
+ def build_attachments(attachment_links):
325
+ attachments = []
326
+
327
+ for item in attachment_links:
328
+ if isinstance(item, str):
329
+ # Just a raw URL string
330
+ url = item
331
+ parsed = urlparse(url)
332
+ filename = parsed.path.split("/")[-1]
333
+ mime_type, _ = mimetypes.guess_type(url)
334
+ mime_type = mime_type or "application/octet-stream"
335
+
336
+ elif isinstance(item, dict):
337
+ # Dictionary payload (preferred)
338
+ url = item.get("url")
339
+ filename = item.get("filename")
340
+ mime_type = item.get("mimeType")
341
+
342
+ # Fallbacks if not provided
343
+ if not filename and url:
344
+ parsed = urlparse(url)
345
+ filename = parsed.path.split("/")[-1]
346
+ if not mime_type and url:
347
+ mime_type, _ = mimetypes.guess_type(url)
348
+ mime_type = mime_type or "application/octet-stream"
349
+
350
+ else:
351
+ continue
352
+
353
+ if url:
354
+ attachments.append({
355
+ "filename": filename,
356
+ "url": url,
357
+ "mimetype": mime_type
358
+ })
359
+
360
+ return attachments
361
+
362
+
363
+ async def send_email_api(
364
+ to_email,
365
+ subject,
366
+ body,
367
+ subdomain,
368
+ provider,
369
+ user_id,
370
+ lead_id,
371
+ entityname,
372
+ cc_email,
373
+ headers,
374
+ attachment_links,
375
+ bcc=None,
376
+ ):
377
+ def delayed_send_email(frommail, to_email, subject, subdomain):
378
+ time.sleep(10)
379
+ # send_email(from_email=frommail, to_email=to_email, keyword=subject, subdomain=subdomain)
380
+
381
+ try:
382
+ AUNTHENTICATE_URL = os.getenv("AUNTHENTICATE_URL")
383
+ EMAIL_API_MICROSOFT = os.getenv("EMAIL_API_MICROSOFT")
384
+ EMAIL_API_GOOGLE = os.getenv("EMAIL_API_GOOGLE")
385
+
386
+ if not AUNTHENTICATE_URL:
387
+ return {"error": "AUNTHENTICATE_URL environment variable not set"}
388
+ if not EMAIL_API_MICROSOFT or not EMAIL_API_GOOGLE:
389
+ return {"error": "Email API environment variables not set"}
390
+
391
+ timeout = httpx.Timeout(120.0)
392
+
393
+ sample_data = {
394
+ "subDomain": subdomain,
395
+ "systemUserId": user_id,
396
+ }
397
+
398
+ encrypted = await encrypt_payload(sample_data, compress=False)
399
+
400
+ final_payload = json.dumps({
401
+ "payload": encrypted
402
+ })
403
+
404
+ async with httpx.AsyncClient(timeout=timeout) as client:
405
+ auth_response = await client.post(
406
+ AUNTHENTICATE_URL,
407
+ content=final_payload,
408
+ headers=headers
409
+ )
410
+
411
+ auth_response.raise_for_status()
412
+ encrypt_response = auth_response.text
413
+ decrypted_response, check_true_false = await decrypt_payload(encrypt_response)
414
+
415
+ if check_true_false == True:
416
+ resp_json = decrypted_response
417
+
418
+ if isinstance(resp_json, dict) and isinstance(resp_json.get("data"), list):
419
+ auth_list = resp_json["data"]
420
+ elif isinstance(resp_json, list):
421
+ auth_list = resp_json
422
+ else:
423
+ answer = {"error": "Invalid authentication response format", "auth_response": resp_json}
424
+ return answer
425
+
426
+ if not auth_list:
427
+ answer = {"error": "Authentication list is empty", "auth_response": resp_json}
428
+ return answer
429
+
430
+ first_auth = auth_list[0]
431
+
432
+ frommail = first_auth.get("email") or first_auth.get("userId")
433
+ provider = first_auth.get("provider")
434
+
435
+ if not frommail:
436
+ return {"error": "No authenticated email found", "auth_response": resp_json}
437
+
438
+ if not provider:
439
+ return {"error": "Provider missing in authentication response", "auth_response": resp_json}
440
+
441
+ provider = provider.lower()
442
+
443
+ if provider not in ("microsoft", "google"):
444
+ return {"error": f"Unsupported provider '{provider}'"}
445
+
446
+ payload = {
447
+ "userId": frommail,
448
+ "provider": provider,
449
+ "subDomain": subdomain,
450
+ "to": to_email,
451
+ "cc": cc_email,
452
+ "bcc": bcc,
453
+ "subject": subject,
454
+ "systemUserId": user_id,
455
+ "body": body,
456
+ "leadId": lead_id,
457
+ "attachments": attachment_links,
458
+ "entityName": entityname,
459
+ }
460
+
461
+ if cc_email:
462
+ payload["cc"] = cc_email
463
+ if attachment_links:
464
+ payload["attachments"] = attachment_links
465
+
466
+ url = EMAIL_API_MICROSOFT if provider.lower() == "microsoft" else EMAIL_API_GOOGLE
467
+
468
+ encrypted1 = await encrypt_payload(payload, compress=False)
469
+
470
+ final_payload1 = json.dumps({
471
+ "payload": encrypted1
472
+ })
473
+
474
+ async with httpx.AsyncClient(timeout=timeout) as client:
475
+ response = await client.post(url, content=final_payload1, headers=headers)
476
+
477
+ response.raise_for_status()
478
+
479
+ threading.Thread(
480
+ target=delayed_send_email,
481
+ args=(frommail, to_email, subject, subdomain)
482
+ ).start()
483
+
484
+ response_json,check_true_false = await decrypt_payload(response.text)
485
+ if check_true_false == True:
486
+ return response_json
487
+ else:
488
+ return {"error": "response return JSONDecodeEror"}
489
+
490
+ except httpx.RequestError as e:
491
+ return {"error": f"Email sending failed: {str(e)}"}
492
+
493
+ except Exception as e:
494
+ return {"error": f"Unexpected error in send_email_api: {str(e)}"}
495
+
496
+
497
+ async def schedule_meeting_api(title, description, recipient, start_time,
498
+ end_time, provider, subdomain, user_id, headers ,time_zone="Asia/Kolkata",
499
+ person_id=""):
500
+ try:
501
+ AUNTHENTICATE_URL = os.getenv("AUNTHENTICATE_URL")
502
+ SCHEDULE_MICROSOFT = os.getenv("SCHEDULE_MICROSOFT")
503
+ SCHEDULE_GOOGLE = os.getenv("SCHEDULE_GOOGLE")
504
+
505
+ if not AUNTHENTICATE_URL:
506
+ return {"error": "AUNTHENTICATE_URL environment variable not set"}
507
+ if not SCHEDULE_MICROSOFT or not SCHEDULE_GOOGLE:
508
+ return {"error": "SCHEDULE_MICROSOFT / SCHEDULE_GOOGLE environment variables not set"}
509
+ start_iso = parse_datetime_to_iso(start_time, tz=time_zone)
510
+ end_iso = parse_datetime_to_iso(end_time, tz=time_zone)
511
+ print(f"start_iso={start_iso}, end_iso={end_iso}")
512
+
513
+ if not start_iso or not end_iso:
514
+ return {"error": "Invalid start or end time format."}
515
+
516
+ auth_payload = {"subDomain": subdomain, "systemUserId": user_id}
517
+
518
+ encrypted = await encrypt_payload(auth_payload, compress=False)
519
+
520
+
521
+ final_payload = {
522
+ "payload": encrypted
523
+ }
524
+
525
+ try:
526
+ getfromemail = requests.post(
527
+ AUNTHENTICATE_URL,
528
+ json=final_payload,
529
+ headers=headers,
530
+ timeout=60
531
+ )
532
+
533
+ except Exception as e:
534
+ answer = {"error": f"Network error calling AUNTHENTICATE_URL: {e}"}
535
+ return answer
536
+
537
+ decrypt_response, check_status = await decrypt_payload(getfromemail.text)
538
+
539
+ if check_status == True:
540
+ resp_json = decrypt_response
541
+ else:
542
+ resp_json = None
543
+
544
+ print({"auth_response_json": resp_json})
545
+
546
+ if not getfromemail.ok:
547
+ return {
548
+ "error": "Authenticated email API returned an error",
549
+ "status_code": getfromemail.status_code,
550
+ "response_text": getfromemail.text,
551
+ "parsed_json": resp_json,
552
+ "sent_payload": auth_payload,
553
+ }
554
+
555
+ if isinstance(resp_json, dict) and isinstance(resp_json.get("data"), list):
556
+ auth_list = resp_json["data"]
557
+ elif isinstance(resp_json, list):
558
+ auth_list = resp_json
559
+ else:
560
+ return {"error": "Invalid authentication response format", "auth_response": resp_json}
561
+
562
+ if not auth_list:
563
+ return {"error": "Authentication list is empty", "auth_response": resp_json}
564
+
565
+ first_auth = auth_list[0]
566
+ frommail = first_auth.get("email") or first_auth.get("userId")
567
+ provider = first_auth.get("provider")
568
+
569
+ if not frommail:
570
+ return {"error": "No authenticated email found", "auth_response": resp_json}
571
+
572
+ if not provider:
573
+ return {"error": "Provider missing in authentication response", "auth_response": resp_json}
574
+
575
+ provider = provider.lower()
576
+ if provider not in ("microsoft", "google"):
577
+ return {"error": f"Unsupported provider '{provider}'"}
578
+
579
+ attendees = normalize_attendees(
580
+ [recipient] if isinstance(recipient, str) else recipient
581
+ )
582
+
583
+ payload = {
584
+ "userId": frommail,
585
+ "provider": provider,
586
+ "subDomain": subdomain,
587
+ "summary": title,
588
+ "description": description or "",
589
+ "startTime": start_iso,
590
+ "endTime": end_iso,
591
+ "systemUserId": user_id,
592
+ "personId": person_id or recipient,
593
+ "attendees": attendees,
594
+ "timeZone": time_zone,
595
+ }
596
+ encrypted1 = await encrypt_payload(payload, compress=False)
597
+
598
+ final_payload1 = {
599
+ "payload": encrypted1
600
+ }
601
+
602
+ url = SCHEDULE_MICROSOFT if provider == "microsoft" else SCHEDULE_GOOGLE
603
+ response = requests.post(url, json=final_payload1, timeout=120,headers=headers)
604
+ response.raise_for_status()
605
+ try:
606
+ response_, check_status = await decrypt_payload(response.text)
607
+ if check_status == True:
608
+ schedule_json = response_
609
+ else:
610
+ schedule_json = None
611
+
612
+ except ValueError:
613
+ schedule_json = None
614
+
615
+ if not response.ok:
616
+ return {
617
+ "error": "Meeting scheduling failed at schedule API",
618
+ "status_code": response.status_code,
619
+ "response_text": response.text,
620
+ "parsed_json": schedule_json,
621
+ "sent_payload": payload,
622
+ }
623
+
624
+ return schedule_json or {"message": "Meeting scheduled, but response was not JSON."}
625
+
626
+ except Exception as e:
627
+ answer = {"error": f"Network error: {e}"}
628
+ return answer
629
+
630
+
631
+ class ToolClasifier:
632
+ def __init__(self):
633
+ pass
634
+
635
+ def ensure_dict(self,data):
636
+ if isinstance(data, dict):
637
+ return data
638
+ if isinstance(data, str):
639
+ return json.loads(data)
640
+ raise TypeError(f"Unsupported tool_args type: {type(data)}")
641
+
642
+ async def handle_crm_chat(self,input_query,
643
+ previous_chat_history_query,
644
+ db_name, user_id,person_id,
645
+ lead_id,leademail,leadname,
646
+ user_email, session_id,
647
+ loggedUserRole,
648
+ loggedUserMail,provider,
649
+ your_id,entityname,
650
+ attachments,rag_uuid,headers):
651
+
652
+ tool_name, tool_args = await tool_finder_llm(previous_chat_history_query)
653
+ logging.info(f"tool_name: {tool_name}")
654
+ logging.info(f"tool_args: {tool_args}")
655
+ steps = []
656
+ step_counter = 1
657
+ output_result = tool_args
658
+
659
+ def add_step(desc: str):
660
+ nonlocal step_counter
661
+ steps.append({f"step{step_counter}": desc})
662
+ step_counter += 1
663
+
664
+ add_step(f"User asked: '{input_query}'")
665
+ add_step(f"Before Chat based query: {previous_chat_history_query}")
666
+
667
+ all_names = ["send_email_api", "get_crm_info", "create_lead","schedule_meeting_api","update_query",
668
+ "greetings_api","pdf_file_api","general_chatbot_api","no_tool_name","no_tool_args"
669
+ ]
670
+ if tool_name in all_names:
671
+ add_step(f"AI selected tool: {tool_name}")
672
+ add_step(f"Tool called with arguments: {tool_args}")
673
+ logging.info(f"Tool called : {tool_name}")
674
+
675
+ if tool_name.strip().strip() == "get_crm_info":
676
+ answer = await get_crm_info(
677
+ input_query=input_query,
678
+ previous_chat_history_query=previous_chat_history_query,
679
+ db_name=db_name,
680
+ user_id=user_id,
681
+ person_id=person_id
682
+ )
683
+ data = answer
684
+ if data.get("status") == "success":
685
+ view = data.get("view")
686
+ plot = data.get("plot")
687
+ status = data.get("status")
688
+ corrected_query = data.get("corrected_query")
689
+ json_str = json.dumps(data, ensure_ascii=False)
690
+ json_bytes = json_str.encode("utf-8")
691
+ decoded_str = json_bytes.decode("utf-8")
692
+ data = json.loads(decoded_str)
693
+ tool_output = {
694
+ "result": data["output"],
695
+ "pipeline": None,
696
+ "plot": plot,
697
+ "view": view,
698
+ "status": status
699
+ }
700
+ reply_text = json.dumps(tool_output, ensure_ascii=False)
701
+
702
+ elif data.get("status") == "error":
703
+ tool_output = answer
704
+ reply_text = json.dumps(tool_output, ensure_ascii=False)
705
+ add_step(f"Corrected query: {corrected_query}")
706
+ add_step(f"Received CRM response: {tool_output}")
707
+
708
+
709
+ elif tool_name.strip().lower() == "create_lead":
710
+ data_dict = json.loads(tool_args)
711
+ email = data_dict["email"] or "".strip()
712
+ company_name = data_dict["company_name"] or "".strip()
713
+ requirement = data_dict["requirement"] or "".strip()
714
+ missing = None
715
+ if not email:
716
+ missing = "email"
717
+ elif not company_name:
718
+ missing = "company_name"
719
+ elif not requirement:
720
+ missing = "requirement"
721
+
722
+ if missing:
723
+ field_labels = {
724
+ "email": "lead's email address",
725
+ "company_name": "lead's company name",
726
+ "requirement": "the lead's requirement/request (e.g., 'Sportizia demo')"
727
+ }
728
+ prompt = f"Please provide {field_labels[missing]}."
729
+ add_step(f"Missing '{missing}'. Asking user for it.")
730
+ reply_text = prompt
731
+ tool_output = None
732
+
733
+ else:
734
+ tool_output = await create_lead(
735
+ email=email,
736
+ company_name=company_name,
737
+ requirement=requirement,
738
+ subdomain=db_name,
739
+ user_email=user_email,
740
+ headers=headers
741
+ )
742
+ add_step(
743
+ f"Lead creation API called with payload {{'email': '{email}', 'company_name': '{company_name}', 'requirement': '{requirement}'}}")
744
+ add_step(f"Received Lead creation response: {tool_output}")
745
+ if tool_output.get("success"):
746
+ reply_text = tool_output["success"]
747
+ tool_output = tool_output["success"]
748
+ else:
749
+ reply_text = tool_output["error"]
750
+ tool_output = tool_output["error"]
751
+
752
+ elif tool_name.strip().lower() == "send_email_api":
753
+ response_json =json.loads(tool_args)
754
+ to_email = response_json.get("to_email", "").strip()
755
+ subject = response_json.get("subject", "").strip()
756
+ body = response_json.get("body", "").strip()
757
+ cc_email = response_json.get("cc_email", "").strip()
758
+ db = client[db_name]
759
+ audit_collection = db["admin.audit.CRM"]
760
+ missing = None
761
+ if not to_email:
762
+ missing = "to_email"
763
+ elif not subject:
764
+ missing = "subject"
765
+ elif not body:
766
+ missing = "body"
767
+
768
+ if missing:
769
+ field_labels = {
770
+ "to_email": "recipient email address",
771
+ "subject": "email subject",
772
+ "body": "email message/content"
773
+ }
774
+ prompt = f"Please provide {field_labels[missing]}."
775
+ add_step(f"Missing '{missing}'. Asking user for it.")
776
+ reply_text = prompt
777
+ tool_output = None
778
+
779
+ else:
780
+ tool_output = await send_email_api(
781
+ to_email=to_email,
782
+ subject=subject,
783
+ body=body,
784
+ subdomain=db_name,
785
+ provider=provider,
786
+ user_id=user_id,
787
+ lead_id=lead_id,
788
+ entityname =entityname,
789
+ cc_email=cc_email,
790
+ headers=headers,
791
+ attachment_links=attachments,
792
+ )
793
+
794
+ add_step(
795
+ f"Email API called with payload {{'to_email': '{to_email}', 'subject': '{subject}', 'body': '{body[:50]}...'}}")
796
+ add_step(f"Received Email response: {tool_output}")
797
+
798
+ if tool_output and not tool_output.get("error"):
799
+ reply_text = dedent(f"""
800
+ <div style="font-family: 'Inter', sans-serif;">
801
+ <h3 style="color:#1e293b; margin:0; font-size:13.5px;">✅ Email Sent Successfully</h3>
802
+ <p style="color:#1e293b; margin:8px 0; font-size:13.5px;">
803
+ <span><b>To:</b> {to_email}</span>
804
+ </p>
805
+ <p style="color:#1e293b; margin:8px 0; font-size:13.5px;">
806
+ <span><b>Subject:</b> {subject}</span>
807
+ </p>
808
+ {f'<p style="color:#1e293b; margin:8px 0; font-size:13.5px;"><b>CC:</b> {cc_email}</p>' if cc_email else ''}
809
+ </div>
810
+ """)
811
+ if lead_id:
812
+ audit_data = {
813
+ "ApplicationName": "CRM",
814
+ "formName": "Leads",
815
+ "createdId": lead_id,
816
+ "Action": "SENDEMAIL",
817
+ "data": {
818
+ "emailStatus": "send",
819
+ "subject": subject,
820
+ "to": to_email,
821
+ "cc": cc_email,
822
+ "attachments": attachments,
823
+ "body": body,
824
+ "leadEmail":to_email,
825
+ "sentAt": datetime.now(timezone.utc)
826
+ },
827
+ "created_by": person_id,
828
+ "created_at": datetime.now(timezone.utc)
829
+ }
830
+ print(audit_data, "audit_data")
831
+ try:
832
+ result = audit_collection.insert_one(audit_data)
833
+ except Exception as e:
834
+ return {"error": str(e)}
835
+ else:
836
+ reply_text = f"""
837
+ <div style="font-family: 'Inter', sans-serif;">
838
+ <h3 style="color:#f44336; margin:0; font-size:13.5px;">❌ Failed to Send Email</h3>
839
+ <p style="color:#1e293b; margin:8px 0; font-size:13.5px;">
840
+ {tool_output.get("error", "Unknown error")}
841
+ </p>
842
+ </div>
843
+ """
844
+
845
+ elif tool_name.strip().lower() == "schedule_meeting_api":
846
+ response_json = json.loads(tool_args)
847
+ recipient = response_json.get("recipient", "").strip()
848
+ subject = response_json.get("subject", "").strip()
849
+ title = response_json.get("title", "").strip()
850
+ description = response_json.get("description", "").strip()
851
+ start_time = response_json.get("start_time", "").strip()
852
+ end_time = response_json.get("end_time", "").strip()
853
+ time_zone = response_json.get("time_zone", "").strip()
854
+ if not time_zone:
855
+ extracted_tz = extract_timezone_from_message(input_query)
856
+ time_zone = extracted_tz if extracted_tz else "Asia/Kolkata"
857
+ db = client[db_name]
858
+ audit_collection = db["admin.audit.CRM"]
859
+ missing = None
860
+ if not recipient:
861
+ missing = "recipient"
862
+ elif not title:
863
+ missing = "title"
864
+ elif not start_time or not end_time:
865
+ missing = "datetime"
866
+
867
+ if missing:
868
+ prompts = {
869
+ "recipient": "recipient email",
870
+ "title": "meeting title",
871
+ "datetime": "meeting start and end time (e.g., 'Sep 5 2025 10:00pm to 11:00pm')"
872
+ }
873
+ reply_text = f"Please provide {prompts[missing]}."
874
+ tool_output = None
875
+
876
+ else:
877
+ tool_output = await schedule_meeting_api(
878
+ title, description, recipient, start_time, end_time, provider,
879
+ db_name, user_id, headers ,time_zone, person_id,
880
+ )
881
+ add_step(
882
+ f"Meeting API called with payload {{'recipient': '{recipient}', 'title': '{title}'}}")
883
+
884
+ add_step(f"Received Meeting response: {tool_output}")
885
+
886
+ if tool_output and not tool_output.get("error"):
887
+ reply_text = f"""
888
+ <div style="font-family: 'Inter', sans-serif;">
889
+ <h3 style="color:#1e293b; margin:0; font-size:13.5px;">✅ Meeting Scheduled Successfully</h3>
890
+ </div>
891
+ """
892
+ if lead_id:
893
+ audit_data = {
894
+ "ApplicationName": "CRM",
895
+ "formName": "Leads",
896
+ "createdId": lead_id,
897
+ "Action": "SCHEDULEMEETING",
898
+ "data": {
899
+ "summary": title,
900
+ "description": description,
901
+ "startTime": start_time,
902
+ "endTime": end_time,
903
+ "attendees": recipient,
904
+ "timeZone": time_zone,
905
+ "leadEmail": leademail,
906
+ "leadName": leadname
907
+ },
908
+ "created_by": person_id,
909
+ "created_at": datetime.now(timezone.utc)
910
+ }
911
+
912
+ audit_collection.insert_one(audit_data)
913
+ else:
914
+ reply_text = f"""
915
+ <div style="font-family: 'Inter', sans-serif;">
916
+ <h3 style="color:#f44336; margin:0; font-size:13.5px;">
917
+ Failed to Schedule Meeting
918
+ </h3>
919
+ <p style="color:#6b7280; margin:5px 0; font-size:13.5px;">
920
+ We couldn't schedule your meeting because your email account is not connected.
921
+ Please connect your email in the Settings page to continue.
922
+ </p>
923
+ <!-- Error message hidden
924
+ <p style="color:#6b7280; margin:6px 0; font-size:13px;">
925
+ Error: {tool_output.get("error", "Unknown error")}
926
+ </p>
927
+ -->
928
+
929
+ <a data-action="navigate-settings" data-tab="email"
930
+ style="display:inline-block; margin-top:8px; padding:6px 10px; background:#875255;
931
+ color:white; text-decoration:none; border-radius:6px; font-size:13px; cursor:pointer;">
932
+ Go to Settings
933
+ </a>
934
+
935
+ </div>
936
+ """
937
+
938
+ elif tool_name.strip().lower() == "update_query":
939
+ response_json = self.ensure_dict(tool_args)
940
+ field_name = response_json.get("field_name", "").strip()
941
+ new_value = response_json.get("new_value", "").strip()
942
+ collection_name = response_json.get("collection", "").strip()
943
+ missing = None
944
+ if not field_name:
945
+ missing = "field_name"
946
+ elif not new_value:
947
+ missing = "new_value"
948
+
949
+ if missing:
950
+ field_labels = {
951
+ "field_name": "the field name (e.g., status, lead_name, company_name)",
952
+ "new_value": "the new value for that field"
953
+ }
954
+ prompt = f"Please provide {field_labels[missing]}."
955
+ add_step(f"Missing '{missing}'. Asking user for it.")
956
+ reply_text = prompt
957
+ tool_output = None
958
+ else:
959
+ tool_output = "Our technical team is actively working on the development. The update will be released soon."
960
+ reply_text = "Our technical team is actively working on the development. The update will be released soon."
961
+
962
+ elif tool_name.strip().lower() == "greetings_api":
963
+ ans = await greetings_api(input_query)
964
+ tool_output = {"status": "success", "view": "text", "plot": "not_required", "output": ans}
965
+ reply_text = ans
966
+
967
+ elif tool_name.strip().lower() == "pdf_file_api":
968
+ output_result_ans = await pdf_file_api(input_query,rag_uuid)
969
+ tool_output = {"status": "success", "view": "text", "plot": "not_required", "output": output_result_ans}
970
+ reply_text = output_result_ans
971
+
972
+ elif tool_name.strip().lower() == "general_chatbot_api":
973
+ answer = await general_chatbot_api(input_query)
974
+ tool_output = {"status": "success", "view": "text", "plot": "not_required", "output": answer}
975
+ reply_text = answer
976
+
977
+ elif tool_name.strip().lower() == "no_tool_name" and tool_args != "no_tool_args":
978
+ answer = tool_args
979
+ tool_output = {"status": "success", "view": "text", "plot": "not_required", "output": answer}
980
+ reply_text = answer
981
+
982
+ elif tool_name.strip().lower() == "no_tool_name" and tool_args == "no_tool_args":
983
+ answer = "Could you please clarify your requirement? You may request CRM-related data (leads, tasks, meetings, accounts) or actions such as sending an email or creating a lead."
984
+ tool_output = {"status": "success", "view": "text", "plot": "not_required", "output": answer}
985
+ reply_text = answer
986
+
987
+ else:
988
+ tool_name = "no_tool_name"
989
+ tool_output = {
990
+ "status": "success",
991
+ "view": "text",
992
+ "plot": "not_required",
993
+ "output": "No tool action was needed. The query has been processed and a textual response is provided."
994
+ }
995
+ reply_text = "No tool action was needed. The query has been processed and a textual response is provided."
996
+
997
+ s3_bucket_ids = get_sorted_memory_ids()
998
+ return {
999
+ "session_id": session_id,
1000
+ "selected_tool": tool_name,
1001
+ "tool_args": tool_args,
1002
+ "result": tool_output,
1003
+ "chat_reply": reply_text,
1004
+ "needs_input": (
1005
+ (tool_name is not None and tool_output is None) or
1006
+ (tool_name is None and tool_output is None)
1007
+ ),
1008
+ "steps": steps,
1009
+ "your_id":your_id,
1010
+ "history_all_ids":s3_bucket_ids,
1011
+ }
all_combine_code/app.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, sys, uuid
2
+
3
+ os.environ.setdefault("TORCHINDUCTOR_CACHE_DIR", "/app/tmp/torchinductor")
4
+ os.environ.setdefault("TORCHDYNAMO_DISABLE", "1")
5
+
6
+ import warnings, shutil
7
+
8
+ warnings.simplefilter("ignore", FutureWarning)
9
+ from fastapi import FastAPI, Request, HTTPException
10
+ from fastapi.responses import PlainTextResponse
11
+ from fastapi import UploadFile, File
12
+ from fastapi.middleware.cors import CORSMiddleware
13
+ import logging
14
+ from memory_.chat_history_handle import generate_response, store_final_chat
15
+ from tools_.delveant_all_tools_.encrypt_and_decrypt import encrypt_payload, decrypt_payload
16
+
17
+ from all_services_.delveant_core_services import ToolClasifier
18
+
19
+ print("successfully imported")
20
+
21
+
all_combine_code/azure-pipelines.yml ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ trigger:
2
+ branches:
3
+ include:
4
+ - dev
5
+
6
+ # pool:
7
+ # vmImage: ubuntu-latest
8
+ pool:
9
+ name: Default
10
+ demands:
11
+ - agent.name -equals stage1-newdevtest
12
+
13
+ variables:
14
+ AWS_DEFAULT_REGION: 'ap-south-1'
15
+ AWS_ACCOUNT_ID: '627133981011'
16
+ IMAGE_REPO_NAME: 'delveant_agent'
17
+
18
+ steps:
19
+ # Install AWS CLI (if not already available on your agent)
20
+ - script: |
21
+ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
22
+ unzip awscliv2.zip
23
+ sudo ./aws/install --update
24
+ displayName: 'Install AWS CLI'
25
+ condition: not(succeeded())
26
+
27
+ # Configure AWS credentials and login to ECR
28
+ - script: |
29
+ aws configure set aws_access_key_id $(AWS_ACCESS_KEY_ID)
30
+ aws configure set aws_secret_access_key $(AWS_SECRET_ACCESS_KEY)
31
+ aws configure set default.region $(AWS_DEFAULT_REGION)
32
+
33
+ # Get ECR login token and login to Docker
34
+ aws ecr get-login-password --region $(AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_DEFAULT_REGION).amazonaws.com
35
+ displayName: 'Configure AWS and Login to ECR'
36
+ # Create reports directory
37
+ - script: |
38
+ mkdir -p $(Build.SourcesDirectory)/security-reports
39
+ displayName: 'Create Reports Directory'
40
+
41
+ # Secret detection scan with Gitleaks
42
+ - script: |
43
+ echo "🔍 Scanning for secrets in code..."
44
+ docker run --rm -v $(Build.SourcesDirectory):/code zricethezav/gitleaks:latest detect --source="/code" -f json -r /code/security-reports/gitleaks-report.json
45
+ if [ -s /code/security-reports/gitleaks-report.json ]; then
46
+ echo "❌ CRITICAL: Secrets detected in code! Build failed for security."
47
+ exit 1
48
+ else
49
+ echo "✅ No secrets found in code."
50
+ fi
51
+ displayName: '🔐 Secret Detection Scan'
52
+ continueOnError: false
53
+
54
+ # Dependency vulnerability scan with Trivy
55
+ - script: |
56
+ echo "🔍 Scanning dependencies for vulnerabilities with Trivy..."
57
+ mkdir -p /tmp/trivy-cache
58
+
59
+ docker run --rm \
60
+ -v $(Build.SourcesDirectory):/workspace \
61
+ -v /tmp/trivy-cache:/root/.cache/trivy \
62
+ aquasec/trivy:latest fs \
63
+ --format json \
64
+ --output /workspace/security-reports/dependency-vulnerabilities.json \
65
+ --severity HIGH,CRITICAL \
66
+ /workspace
67
+
68
+ docker run --rm \
69
+ -v $(Build.SourcesDirectory):/workspace \
70
+ -v /tmp/trivy-cache:/root/.cache/trivy \
71
+ aquasec/trivy:latest fs \
72
+ --format template \
73
+ --template "@contrib/html.tpl" \
74
+ --output /workspace/security-reports/dependency-vulnerabilities.html \
75
+ --severity HIGH,CRITICAL \
76
+ /workspace
77
+
78
+ VULN_COUNT=$(docker run --rm \
79
+ -v $(Build.SourcesDirectory):/workspace \
80
+ -v /tmp/trivy-cache:/root/.cache/trivy \
81
+ aquasec/trivy:latest fs \
82
+ --format json \
83
+ --severity HIGH,CRITICAL \
84
+ --quiet \
85
+ /workspace | jq -r '.Results[]?.Vulnerabilities // [] | length' | awk '{sum += $1} END {print sum+0}')
86
+
87
+ if [ "$VULN_COUNT" -gt 0 ]; then
88
+ echo "❌ CRITICAL: $VULN_COUNT high/critical dependency vulnerabilities found! Build failed."
89
+ exit 1
90
+ else
91
+ echo "✅ No critical dependency vulnerabilities found."
92
+ fi
93
+ displayName: '📦 Dependency Vulnerability Scan (Trivy)'
94
+ continueOnError: true
95
+
96
+ # Static Application Security Testing (SAST) - CORRECTED VERSION
97
+ - script: |
98
+ echo "🔍 Running Static Application Security Testing with Semgrep..."
99
+
100
+ # Create output directory
101
+ mkdir -p $(Build.SourcesDirectory)/security-reports
102
+
103
+ # Run Semgrep with explicit entrypoint
104
+ docker run --rm \
105
+ -v $(Build.SourcesDirectory):/src \
106
+ --entrypoint semgrep \
107
+ returntocorp/semgrep \
108
+ --config=auto \
109
+ --json \
110
+ --output=/src/security-reports/sast-report.json \
111
+ /src
112
+
113
+ # Check if report was generated and analyze results
114
+ if [ -f $(Build.SourcesDirectory)/security-reports/sast-report.json ]; then
115
+ CRITICAL_COUNT=$(jq '[.results[] | select(.extra.severity == "ERROR")] | length' $(Build.SourcesDirectory)/security-reports/sast-report.json 2>/dev/null || echo "0")
116
+
117
+ echo "📊 SAST Analysis Results:"
118
+ echo " Critical Issues Found: $CRITICAL_COUNT"
119
+
120
+ if [ "$CRITICAL_COUNT" -gt 0 ]; then
121
+ echo "❌ CRITICAL: $CRITICAL_COUNT critical security issues found!"
122
+ exit 1
123
+ else
124
+ echo "✅ No critical security issues found in static analysis."
125
+ fi
126
+ else
127
+ echo "⚠️ SAST report not generated. Check Semgrep execution."
128
+ fi
129
+ displayName: '🔍 Static Application Security Testing (SAST)'
130
+ continueOnError: true # Keep true for testing
131
+
132
+ # Publish security reports - ALWAYS RUN EVEN IF BUILD FAILS
133
+ - task: PublishBuildArtifacts@1
134
+ condition: always() # ← This ensures reports are published even if scans fail
135
+ inputs:
136
+ pathToPublish: '$(Build.SourcesDirectory)/security-reports'
137
+ artifactName: 'security-reports-$(Build.BuildId)'
138
+ publishLocation: 'Container'
139
+ displayName: '📊 Publish Security Reports'
140
+
141
+ # Build Docker image
142
+ - script: |
143
+ docker build -t $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_DEFAULT_REGION).amazonaws.com/$(IMAGE_REPO_NAME):$(Build.BuildId) .
144
+ displayName: 'Build Docker Image'
145
+
146
+ # Container scan (only if image built successfully)
147
+ # - script: |
148
+ # docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
149
+ # -v $(Build.SourcesDirectory):/reports \
150
+ # aquasec/trivy:latest image \
151
+ # --exit-code 1 \
152
+ # --severity CRITICAL \
153
+ # $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_DEFAULT_REGION).amazonaws.com/$(IMAGE_REPO_NAME):$(Build.BuildId)
154
+ # displayName: '🐳 Container Vulnerability Scan'
155
+ # List images for verification
156
+ - script: |
157
+ docker images
158
+ displayName: 'List Docker Images'
159
+
160
+ # Push to ECR
161
+ - script: |
162
+ docker push $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_DEFAULT_REGION).amazonaws.com/$(IMAGE_REPO_NAME):$(Build.BuildId)
163
+ displayName: 'Push Docker Image to ECR'
164
+
165
+ # Update deployment YAML
166
+ - script: |
167
+ git config --global user.email "devops@kovaion.ai"
168
+ git config --global user.name "Azure DevOps"
169
+ git clone https://$(AZURE_PAT)@dev.azure.com/kovaionai/Deployment/_git/Deployment
170
+ cd Deployment
171
+ git checkout delveant_cloud
172
+ sed -i 's|627133981011.dkr.ecr.ap-south-1.amazonaws.com/delveant_agent:.*|627133981011.dkr.ecr.ap-south-1.amazonaws.com/delveant_agent:$(Build.BuildId)|' agents.yaml
173
+ git add agents.yaml
174
+ git commit -m "update yaml with build ids"
175
+ git push origin delveant_cloud
176
+ displayName: 'Update YAML with Latest Image Tag and Push'
all_combine_code/genre_classification/genre_.ipynb ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "metadata": {
5
+ "ExecuteTime": {
6
+ "end_time": "2026-03-27T13:35:25.148983400Z",
7
+ "start_time": "2026-03-27T13:35:25.121163900Z"
8
+ }
9
+ },
10
+ "cell_type": "code",
11
+ "source": [
12
+ "#! pip install xgboost\n",
13
+ "#! pip install lightgbm"
14
+ ],
15
+ "id": "989cd81ab35b2992",
16
+ "outputs": [],
17
+ "execution_count": 17
18
+ },
19
+ {
20
+ "metadata": {
21
+ "ExecuteTime": {
22
+ "end_time": "2026-03-27T14:16:27.830605300Z",
23
+ "start_time": "2026-03-27T14:15:36.993019800Z"
24
+ }
25
+ },
26
+ "cell_type": "code",
27
+ "source": [
28
+ "import pandas as pd\n",
29
+ "import numpy as np\n",
30
+ "import re\n",
31
+ "import ast\n",
32
+ "from sklearn.model_selection import train_test_split\n",
33
+ "from sklearn.preprocessing import StandardScaler, MultiLabelBinarizer\n",
34
+ "from sklearn.multioutput import MultiOutputClassifier\n",
35
+ "from xgboost import XGBClassifier\n",
36
+ "\n",
37
+ "path = r\"C:\\Users\\VigneshSubramani\\Downloads\\model_ready_dataset_final.csv\"\n",
38
+ "df = pd.read_csv(path)\n",
39
+ "\n",
40
+ "def parse_subgenres(text):\n",
41
+ " if pd.isna(text) or text == \"\":\n",
42
+ " return []\n",
43
+ " match = re.search(r\"\\[(.*?)\\]\", str(text))\n",
44
+ " if match:\n",
45
+ " try:\n",
46
+ " genre_list = ast.literal_eval(\"[\" + match.group(1) + \"]\")\n",
47
+ " return [g.strip() for g in genre_list if g.strip()]\n",
48
+ " except:\n",
49
+ " return []\n",
50
+ " return []\n",
51
+ "\n",
52
+ "df['subgenres_list'] = df['genre_subgenre'].apply(parse_subgenres)\n",
53
+ "\n",
54
+ "mlb = MultiLabelBinarizer()\n",
55
+ "y = mlb.fit_transform(df['subgenres_list'])\n",
56
+ "\n",
57
+ "drop_cols = ['_id', 'genre', 'genre_subgenre', 'subgenres_list', 'labels']\n",
58
+ "feature_cols = [c for c in df.columns if c not in drop_cols]\n",
59
+ "X = df[feature_cols].copy()\n",
60
+ "\n",
61
+ "X = X.select_dtypes(include=[np.number]).fillna(0)\n",
62
+ "\n",
63
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
64
+ "\n",
65
+ "scaler = StandardScaler()\n",
66
+ "X_train_scaled = scaler.fit_transform(X_train)\n",
67
+ "X_test_scaled = scaler.transform(X_test)\n",
68
+ "\n",
69
+ "model = MultiOutputClassifier(\n",
70
+ " XGBClassifier(\n",
71
+ " n_estimators=600,\n",
72
+ " learning_rate=0.08,\n",
73
+ " max_depth=6,\n",
74
+ " subsample=0.85,\n",
75
+ " colsample_bytree=0.85,\n",
76
+ " random_state=42,\n",
77
+ " eval_metric='logloss'\n",
78
+ " ),\n",
79
+ " n_jobs=-1\n",
80
+ ")\n",
81
+ "\n",
82
+ "model.fit(X_train_scaled, y_train)\n",
83
+ "\n",
84
+ "def predict_genre_subgenre(idx):\n",
85
+ " pred = model.predict(X_test_scaled[idx].reshape(1, -1))[0]\n",
86
+ " subgenres = mlb.inverse_transform(pred.reshape(1, -1))[0]\n",
87
+ " if len(subgenres) == 0:\n",
88
+ " return \"Rock -- None\"\n",
89
+ " return f\"Rock -- {', '.join(subgenres)}\"\n",
90
+ "\n",
91
+ "print(\"FINAL PREDICTIONS:\")\n",
92
+ "for i in range(10):\n",
93
+ " print(predict_genre_subgenre(i))"
94
+ ],
95
+ "id": "f1eebe48c0a482c3",
96
+ "outputs": [
97
+ {
98
+ "name": "stdout",
99
+ "output_type": "stream",
100
+ "text": [
101
+ "FINAL PREDICTIONS:\n",
102
+ "Rock -- Contemporary Singer Songwriter\n",
103
+ "Rock -- None\n",
104
+ "Rock -- None\n",
105
+ "Rock -- None\n",
106
+ "Rock -- Classic Alternative Rock\n",
107
+ "Rock -- None\n",
108
+ "Rock -- Indie Rock\n",
109
+ "Rock -- Americana\n",
110
+ "Rock -- Indie Rock\n",
111
+ "Rock -- None\n"
112
+ ]
113
+ }
114
+ ],
115
+ "execution_count": 68
116
+ },
117
+ {
118
+ "metadata": {
119
+ "ExecuteTime": {
120
+ "end_time": "2026-03-27T14:19:25.780735200Z",
121
+ "start_time": "2026-03-27T14:18:17.557680700Z"
122
+ }
123
+ },
124
+ "cell_type": "code",
125
+ "source": [
126
+ "import pandas as pd\n",
127
+ "import numpy as np\n",
128
+ "import re\n",
129
+ "import ast\n",
130
+ "from sklearn.model_selection import train_test_split\n",
131
+ "from sklearn.preprocessing import StandardScaler, MultiLabelBinarizer\n",
132
+ "from sklearn.multioutput import MultiOutputClassifier\n",
133
+ "from xgboost import XGBClassifier\n",
134
+ "\n",
135
+ "path = r\"C:\\Users\\VigneshSubramani\\Downloads\\model_ready_dataset_final.csv\"\n",
136
+ "df = pd.read_csv(path)\n",
137
+ "\n",
138
+ "def parse_subgenres(text):\n",
139
+ " if pd.isna(text) or text == \"\":\n",
140
+ " return []\n",
141
+ " match = re.search(r\"\\[(.*?)\\]\", str(text))\n",
142
+ " if match:\n",
143
+ " try:\n",
144
+ " genre_list = ast.literal_eval(\"[\" + match.group(1) + \"]\")\n",
145
+ " return [g.strip() for g in genre_list if g.strip()]\n",
146
+ " except:\n",
147
+ " return []\n",
148
+ " return []\n",
149
+ "\n",
150
+ "df['subgenres_list'] = df['genre_subgenre'].apply(parse_subgenres)\n",
151
+ "\n",
152
+ "mlb = MultiLabelBinarizer()\n",
153
+ "y = mlb.fit_transform(df['subgenres_list'])\n",
154
+ "\n",
155
+ "drop_cols = ['_id', 'genre', 'genre_subgenre', 'subgenres_list', 'labels']\n",
156
+ "feature_cols = [c for c in df.columns if c not in drop_cols]\n",
157
+ "X = df[feature_cols].copy()\n",
158
+ "\n",
159
+ "X = X.select_dtypes(include=[np.number]).fillna(0)\n",
160
+ "\n",
161
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
162
+ "\n",
163
+ "scaler = StandardScaler()\n",
164
+ "X_train_scaled = scaler.fit_transform(X_train)\n",
165
+ "X_test_scaled = scaler.transform(X_test)\n",
166
+ "\n",
167
+ "model = MultiOutputClassifier(\n",
168
+ " XGBClassifier(\n",
169
+ " n_estimators=800,\n",
170
+ " learning_rate=0.05,\n",
171
+ " max_depth=7,\n",
172
+ " subsample=0.8,\n",
173
+ " colsample_bytree=0.8,\n",
174
+ " random_state=42,\n",
175
+ " eval_metric='logloss'\n",
176
+ " ),\n",
177
+ " n_jobs=-1\n",
178
+ ")\n",
179
+ "\n",
180
+ "model.fit(X_train_scaled, y_train)"
181
+ ],
182
+ "id": "b73adeba8b23236b",
183
+ "outputs": [
184
+ {
185
+ "name": "stdout",
186
+ "output_type": "stream",
187
+ "text": [
188
+ "FINAL PREDICTIONS:\n",
189
+ "Rock -- Contemporary Singer Songwriter\n",
190
+ "Rock -- Indie Rock\n",
191
+ "Rock -- Indie Rock\n",
192
+ "Rock -- Indie Pop\n",
193
+ "Rock -- Classic Alternative Rock, Indie Pop, Modern Alternative Rock\n",
194
+ "Rock -- Indie Rock\n",
195
+ "Rock -- Classic Alternative Rock, Indie Rock\n",
196
+ "Rock -- Americana\n",
197
+ "Rock -- Classic Alternative Rock, Indie Rock\n",
198
+ "Rock -- Contemporary Country\n",
199
+ "Rock -- Indie Rock\n",
200
+ "Rock -- Indie Pop, Modern Alternative Rock\n",
201
+ "Rock -- Contemporary Singer Songwriter, Indie Pop\n",
202
+ "Rock -- Contemporary Singer Songwriter, Indie Electronic, Indie Pop\n",
203
+ "Rock -- Classic Alternative Rock, Indie Pop, Indie Rock\n"
204
+ ]
205
+ }
206
+ ],
207
+ "execution_count": 69
208
+ },
209
+ {
210
+ "metadata": {
211
+ "ExecuteTime": {
212
+ "end_time": "2026-03-27T14:23:46.993341800Z",
213
+ "start_time": "2026-03-27T14:23:36.236284900Z"
214
+ }
215
+ },
216
+ "cell_type": "code",
217
+ "source": [
218
+ "def get_accuracy(X_scaled, y_true, name=\"\"):\n",
219
+ " y_pred = model.predict(X_scaled)\n",
220
+ " subset_acc = np.mean(np.all(y_pred == y_true, axis=1))\n",
221
+ " micro_f1 = f1_score(y_true, y_pred, average='micro')\n",
222
+ " print(f\"{name} Subset Accuracy: {subset_acc:.4f}\")\n",
223
+ " print(f\"{name} Micro F1 Score: {micro_f1:.4f}\")\n",
224
+ "\n",
225
+ "print(\"=== MODEL ACCURACY ===\")\n",
226
+ "get_accuracy(X_train_scaled, y_train, \"Train\")\n",
227
+ "get_accuracy(X_test_scaled, y_test, \"Test\")"
228
+ ],
229
+ "id": "5f4d018a7e7c5f21",
230
+ "outputs": [
231
+ {
232
+ "name": "stdout",
233
+ "output_type": "stream",
234
+ "text": [
235
+ "=== MODEL ACCURACY ===\n",
236
+ "Train Subset Accuracy: 0.9568\n",
237
+ "Train Micro F1 Score: 0.9862\n",
238
+ "Test Subset Accuracy: 0.2026\n",
239
+ "Test Micro F1 Score: 0.2830\n"
240
+ ]
241
+ }
242
+ ],
243
+ "execution_count": 72
244
+ },
245
+ {
246
+ "metadata": {
247
+ "ExecuteTime": {
248
+ "end_time": "2026-03-27T14:24:41.561225300Z",
249
+ "start_time": "2026-03-27T14:24:41.503715100Z"
250
+ }
251
+ },
252
+ "cell_type": "code",
253
+ "source": [
254
+ "def manual_predict(row_features):\n",
255
+ " input_df = pd.DataFrame([row_features])\n",
256
+ " input_numeric = input_df.select_dtypes(include=[np.number]).fillna(0)\n",
257
+ " for col in X.columns:\n",
258
+ " if col not in input_numeric.columns:\n",
259
+ " input_numeric[col] = 0\n",
260
+ " input_numeric = input_numeric[X.columns]\n",
261
+ " input_scaled = scaler.transform(input_numeric)\n",
262
+ " probas = model.predict_proba(input_scaled)\n",
263
+ " threshold = 0.25\n",
264
+ " pred_binary = np.zeros((1, y.shape[1]), dtype=int)\n",
265
+ " for i in range(len(probas)):\n",
266
+ " if probas[i][0][1] > threshold:\n",
267
+ " pred_binary[0, i] = 1\n",
268
+ " subgenres = mlb.inverse_transform(pred_binary)[0]\n",
269
+ " if len(subgenres) == 0:\n",
270
+ " return \"None\"\n",
271
+ " return {', '.join(sorted(subgenres))}"
272
+ ],
273
+ "id": "d759399759dbe475",
274
+ "outputs": [],
275
+ "execution_count": 73
276
+ },
277
+ {
278
+ "metadata": {
279
+ "ExecuteTime": {
280
+ "end_time": "2026-03-27T14:27:13.980046900Z",
281
+ "start_time": "2026-03-27T14:27:13.695114900Z"
282
+ }
283
+ },
284
+ "cell_type": "code",
285
+ "source": [
286
+ "print(\"\\n=== FIRST ROW PREDICTION ===\")\n",
287
+ "first_row = df.iloc[0].to_dict()\n",
288
+ "pred_first = manual_predict(first_row)\n",
289
+ "print(pred_first)\n",
290
+ "\n",
291
+ "print(\"\\n=== LAST ROW PREDICTION ===\")\n",
292
+ "last_row = df.iloc[-1].to_dict()\n",
293
+ "pred_last = manual_predict(last_row)\n",
294
+ "print(pred_last)"
295
+ ],
296
+ "id": "59c4c35de1bde5ee",
297
+ "outputs": [
298
+ {
299
+ "name": "stdout",
300
+ "output_type": "stream",
301
+ "text": [
302
+ "\n",
303
+ "=== FIRST ROW PREDICTION ===\n",
304
+ "Rock -- Classic Alternative Rock, Indie Rock, Noise Pop\n",
305
+ "\n",
306
+ "=== LAST ROW PREDICTION ===\n",
307
+ "Rock -- Gothic Metal, Heavy Metal, Metalcore\n"
308
+ ]
309
+ }
310
+ ],
311
+ "execution_count": 76
312
+ },
313
+ {
314
+ "metadata": {
315
+ "ExecuteTime": {
316
+ "end_time": "2026-03-27T14:25:23.755520400Z",
317
+ "start_time": "2026-03-27T14:25:23.711767500Z"
318
+ }
319
+ },
320
+ "cell_type": "code",
321
+ "source": "df.tail(1)",
322
+ "id": "ed40ccd2d8a6769e",
323
+ "outputs": [
324
+ {
325
+ "data": {
326
+ "text/plain": [
327
+ " _id genre \\\n",
328
+ "41934 698c479e0fc160cc5e51bc64 Metal \n",
329
+ "\n",
330
+ " genre_subgenre \\\n",
331
+ "41934 Metal --- ['Gothic Metal', 'Heavy Metal', 'Met... \n",
332
+ "\n",
333
+ " trimmed_audio_duration_sec syllable_count word_count \\\n",
334
+ "41934 112.72127 573.0 455.0 \n",
335
+ "\n",
336
+ " spectral_contrast_mean (mix) chroma_mean (mix) \\\n",
337
+ "41934 19.482281 0.559816 \n",
338
+ "\n",
339
+ " melody_variability (vocals) rhythm_onset_rate (mix) ... \\\n",
340
+ "41934 0.693639 5.48255 ... \n",
341
+ "\n",
342
+ " vocab_richness loudness_integrated_lufs custom (mix) \\\n",
343
+ "41934 0.495 -14.21983 \n",
344
+ "\n",
345
+ " readability_score energy_essentia (mix) energy_librosa (mix) \\\n",
346
+ "41934 16.4 0.354772 0.558767 \n",
347
+ "\n",
348
+ " rms_energy_mean (mix) sentiment_score melody_complexity (vocals) \\\n",
349
+ "41934 0.562382 -0.002 2.916667 \n",
350
+ "\n",
351
+ " avg_word_length subgenres_list \n",
352
+ "41934 3.63 [Gothic Metal, Heavy Metal, Metalcore] \n",
353
+ "\n",
354
+ "[1 rows x 29 columns]"
355
+ ],
356
+ "text/html": [
357
+ "<div>\n",
358
+ "<style scoped>\n",
359
+ " .dataframe tbody tr th:only-of-type {\n",
360
+ " vertical-align: middle;\n",
361
+ " }\n",
362
+ "\n",
363
+ " .dataframe tbody tr th {\n",
364
+ " vertical-align: top;\n",
365
+ " }\n",
366
+ "\n",
367
+ " .dataframe thead th {\n",
368
+ " text-align: right;\n",
369
+ " }\n",
370
+ "</style>\n",
371
+ "<table border=\"1\" class=\"dataframe\">\n",
372
+ " <thead>\n",
373
+ " <tr style=\"text-align: right;\">\n",
374
+ " <th></th>\n",
375
+ " <th>_id</th>\n",
376
+ " <th>genre</th>\n",
377
+ " <th>genre_subgenre</th>\n",
378
+ " <th>trimmed_audio_duration_sec</th>\n",
379
+ " <th>syllable_count</th>\n",
380
+ " <th>word_count</th>\n",
381
+ " <th>spectral_contrast_mean (mix)</th>\n",
382
+ " <th>chroma_mean (mix)</th>\n",
383
+ " <th>melody_variability (vocals)</th>\n",
384
+ " <th>rhythm_onset_rate (mix)</th>\n",
385
+ " <th>...</th>\n",
386
+ " <th>vocab_richness</th>\n",
387
+ " <th>loudness_integrated_lufs custom (mix)</th>\n",
388
+ " <th>readability_score</th>\n",
389
+ " <th>energy_essentia (mix)</th>\n",
390
+ " <th>energy_librosa (mix)</th>\n",
391
+ " <th>rms_energy_mean (mix)</th>\n",
392
+ " <th>sentiment_score</th>\n",
393
+ " <th>melody_complexity (vocals)</th>\n",
394
+ " <th>avg_word_length</th>\n",
395
+ " <th>subgenres_list</th>\n",
396
+ " </tr>\n",
397
+ " </thead>\n",
398
+ " <tbody>\n",
399
+ " <tr>\n",
400
+ " <th>41934</th>\n",
401
+ " <td>698c479e0fc160cc5e51bc64</td>\n",
402
+ " <td>Metal</td>\n",
403
+ " <td>Metal --- ['Gothic Metal', 'Heavy Metal', 'Met...</td>\n",
404
+ " <td>112.72127</td>\n",
405
+ " <td>573.0</td>\n",
406
+ " <td>455.0</td>\n",
407
+ " <td>19.482281</td>\n",
408
+ " <td>0.559816</td>\n",
409
+ " <td>0.693639</td>\n",
410
+ " <td>5.48255</td>\n",
411
+ " <td>...</td>\n",
412
+ " <td>0.495</td>\n",
413
+ " <td>-14.21983</td>\n",
414
+ " <td>16.4</td>\n",
415
+ " <td>0.354772</td>\n",
416
+ " <td>0.558767</td>\n",
417
+ " <td>0.562382</td>\n",
418
+ " <td>-0.002</td>\n",
419
+ " <td>2.916667</td>\n",
420
+ " <td>3.63</td>\n",
421
+ " <td>[Gothic Metal, Heavy Metal, Metalcore]</td>\n",
422
+ " </tr>\n",
423
+ " </tbody>\n",
424
+ "</table>\n",
425
+ "<p>1 rows × 29 columns</p>\n",
426
+ "</div>"
427
+ ]
428
+ },
429
+ "execution_count": 75,
430
+ "metadata": {},
431
+ "output_type": "execute_result"
432
+ }
433
+ ],
434
+ "execution_count": 75
435
+ },
436
+ {
437
+ "metadata": {
438
+ "ExecuteTime": {
439
+ "end_time": "2026-03-27T14:32:46.590643700Z",
440
+ "start_time": "2026-03-27T14:32:46.540506300Z"
441
+ }
442
+ },
443
+ "cell_type": "code",
444
+ "source": "df.columns",
445
+ "id": "f581dd69d03fec27",
446
+ "outputs": [
447
+ {
448
+ "data": {
449
+ "text/plain": [
450
+ "Index(['_id', 'genre', 'genre_subgenre', 'trimmed_audio_duration_sec',\n",
451
+ " 'syllable_count', 'word_count', 'spectral_contrast_mean (mix)',\n",
452
+ " 'chroma_mean (mix)', 'melody_variability (vocals)',\n",
453
+ " 'rhythm_onset_rate (mix)', 'spectral_centroid_mean custom (mix)',\n",
454
+ " 'repetition_score custom (mix)', 'pitch_std (mix)',\n",
455
+ " 'tempo_bpm_corrected (mix)', 'tempo_bpm_original (mix)',\n",
456
+ " 'mfcc_mean_1 (mix)', 'mfcc_mean_2 (mix)',\n",
457
+ " 'loudness_range_lu custom (mix)', 'zero_crossing_rate (mix)',\n",
458
+ " 'vocab_richness', 'loudness_integrated_lufs custom (mix)',\n",
459
+ " 'readability_score', 'energy_essentia (mix)', 'energy_librosa (mix)',\n",
460
+ " 'rms_energy_mean (mix)', 'sentiment_score',\n",
461
+ " 'melody_complexity (vocals)', 'avg_word_length', 'subgenres_list'],\n",
462
+ " dtype='object')"
463
+ ]
464
+ },
465
+ "execution_count": 77,
466
+ "metadata": {},
467
+ "output_type": "execute_result"
468
+ }
469
+ ],
470
+ "execution_count": 77
471
+ },
472
+ {
473
+ "metadata": {},
474
+ "cell_type": "code",
475
+ "outputs": [],
476
+ "execution_count": null,
477
+ "source": "",
478
+ "id": "9b8e0e646877dffd"
479
+ },
480
+ {
481
+ "metadata": {},
482
+ "cell_type": "code",
483
+ "outputs": [],
484
+ "execution_count": null,
485
+ "source": "",
486
+ "id": "a2c6e3303920e7b1"
487
+ },
488
+ {
489
+ "metadata": {},
490
+ "cell_type": "code",
491
+ "outputs": [],
492
+ "execution_count": null,
493
+ "source": "",
494
+ "id": "f9be1b4975b13c44"
495
+ },
496
+ {
497
+ "metadata": {},
498
+ "cell_type": "code",
499
+ "outputs": [],
500
+ "execution_count": null,
501
+ "source": "",
502
+ "id": "561bf73932a24d3a"
503
+ },
504
+ {
505
+ "metadata": {},
506
+ "cell_type": "code",
507
+ "outputs": [],
508
+ "execution_count": null,
509
+ "source": "",
510
+ "id": "ed7e357983c3b734"
511
+ },
512
+ {
513
+ "metadata": {
514
+ "ExecuteTime": {
515
+ "end_time": "2026-03-29T11:07:41.453680300Z",
516
+ "start_time": "2026-03-29T11:07:41.256698100Z"
517
+ }
518
+ },
519
+ "cell_type": "code",
520
+ "source": [
521
+ "import ast, re\n",
522
+ "from sklearn.model_selection import train_test_split\n",
523
+ "from sklearn.preprocessing import StandardScaler, MultiLabelBinarizer\n",
524
+ "from sklearn.multioutput import MultiOutputClassifier\n",
525
+ "from xgboost import XGBClassifier\n",
526
+ "from sklearn.metrics import f1_score\n",
527
+ "import pandas as pd\n",
528
+ "\n",
529
+ "path = r\"C:\\Users\\VigneshSubramani\\Downloads\\model_ready_dataset_final.csv\"\n",
530
+ "df = pd.read_csv(path)\n",
531
+ "\n",
532
+ "def parse_subgenres_fast(text):\n",
533
+ " if pd.isna(text) or text == \"\":\n",
534
+ " return []\n",
535
+ "\n",
536
+ " text = str(text)\n",
537
+ " parts = text.split('---')\n",
538
+ " main_genre = parts[0].strip()\n",
539
+ "\n",
540
+ " if len(parts) > 1:\n",
541
+ " sub = parts[1].strip()\n",
542
+ " sub = sub.strip('[]')\n",
543
+ " if sub:\n",
544
+ " subgenres = [s.strip().strip(\"'\").strip('\"') for s in sub.split(',')]\n",
545
+ " else:\n",
546
+ " subgenres = []\n",
547
+ " else:\n",
548
+ " subgenres = []\n",
549
+ " return [main_genre] + subgenres\n",
550
+ "\n",
551
+ "df['subgenres_list'] = df['genre_subgenre'].apply(parse_subgenres_fast)"
552
+ ],
553
+ "id": "573c53470f0ef01f",
554
+ "outputs": [],
555
+ "execution_count": 8
556
+ },
557
+ {
558
+ "metadata": {
559
+ "ExecuteTime": {
560
+ "end_time": "2026-03-29T11:07:43.398574800Z",
561
+ "start_time": "2026-03-29T11:07:43.351108Z"
562
+ }
563
+ },
564
+ "cell_type": "code",
565
+ "source": "df['subgenres_list'][0]",
566
+ "id": "1b59c31bd0b780e0",
567
+ "outputs": [
568
+ {
569
+ "data": {
570
+ "text/plain": [
571
+ "['Rock', 'Noise Pop', 'Classic Alternative Rock', 'Indie Rock']"
572
+ ]
573
+ },
574
+ "execution_count": 9,
575
+ "metadata": {},
576
+ "output_type": "execute_result"
577
+ }
578
+ ],
579
+ "execution_count": 9
580
+ },
581
+ {
582
+ "metadata": {},
583
+ "cell_type": "code",
584
+ "outputs": [],
585
+ "execution_count": null,
586
+ "source": "",
587
+ "id": "9b1fa1d3b08915da"
588
+ }
589
+ ],
590
+ "metadata": {
591
+ "kernelspec": {
592
+ "display_name": "Python 3",
593
+ "language": "python",
594
+ "name": "python3"
595
+ },
596
+ "language_info": {
597
+ "codemirror_mode": {
598
+ "name": "ipython",
599
+ "version": 2
600
+ },
601
+ "file_extension": ".py",
602
+ "mimetype": "text/x-python",
603
+ "name": "python",
604
+ "nbconvert_exporter": "python",
605
+ "pygments_lexer": "ipython2",
606
+ "version": "2.7.6"
607
+ }
608
+ },
609
+ "nbformat": 4,
610
+ "nbformat_minor": 5
611
+ }
all_combine_code/genre_classification/main_file.ipynb ADDED
@@ -0,0 +1,2110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "id": "initial_id",
6
+ "metadata": {
7
+ "collapsed": true,
8
+ "ExecuteTime": {
9
+ "end_time": "2026-03-30T08:55:12.053860800Z",
10
+ "start_time": "2026-03-30T08:55:06.050743500Z"
11
+ }
12
+ },
13
+ "source": [
14
+ "import pandas as pd\n",
15
+ "import numpy as np\n",
16
+ "import re\n",
17
+ "import ast\n",
18
+ "from sklearn.model_selection import train_test_split\n",
19
+ "from sklearn.preprocessing import StandardScaler, MultiLabelBinarizer\n",
20
+ "from sklearn.multioutput import MultiOutputClassifier\n",
21
+ "from xgboost import XGBClassifier\n",
22
+ "from sklearn.metrics import f1_score\n",
23
+ "pd.set_option(\"display.max_columns\", None)\n",
24
+ "path = r\"C:\\Users\\VigneshSubramani\\Downloads\\model_ready_dataset_final.csv\"\n",
25
+ "df = pd.read_csv(path)\n",
26
+ "df.sample(3)"
27
+ ],
28
+ "outputs": [
29
+ {
30
+ "data": {
31
+ "text/plain": [
32
+ " _id genre \\\n",
33
+ "27949 69b29e6c05f5ce265b882008 Rock \n",
34
+ "40690 69b12fabebd9b3c1ca39e859 Country \n",
35
+ "18977 692969b1c5db350c7bdaf13d Rock \n",
36
+ "\n",
37
+ " genre_subgenre \\\n",
38
+ "27949 Rock --- ['Indie Folk', 'Country Folk', 'Indie... \n",
39
+ "40690 Country --- ['Americana', 'Country Rock', 'Alt... \n",
40
+ "18977 Rock --- ['Noise Pop', 'Classic Alternative Ro... \n",
41
+ "\n",
42
+ " trimmed_audio_duration_sec syllable_count word_count \\\n",
43
+ "27949 191.332000 287.0 197.0 \n",
44
+ "40690 215.146667 245.0 200.0 \n",
45
+ "18977 203.948458 145.0 118.0 \n",
46
+ "\n",
47
+ " spectral_contrast_mean (mix) chroma_mean (mix) \\\n",
48
+ "27949 19.647192 0.453619 \n",
49
+ "40690 19.253367 0.484467 \n",
50
+ "18977 18.226969 0.532658 \n",
51
+ "\n",
52
+ " melody_variability (vocals) rhythm_onset_rate (mix) \\\n",
53
+ "27949 0.479049 1.927778 \n",
54
+ "40690 0.600583 4.220377 \n",
55
+ "18977 0.647673 1.694444 \n",
56
+ "\n",
57
+ " spectral_centroid_mean custom (mix) repetition_score custom (mix) \\\n",
58
+ "27949 0.093420 0.006936 \n",
59
+ "40690 0.111674 0.005806 \n",
60
+ "18977 0.124794 0.006610 \n",
61
+ "\n",
62
+ " pitch_std (mix) tempo_bpm_corrected (mix) tempo_bpm_original (mix) \\\n",
63
+ "27949 56.905636 123.635239 123.635239 \n",
64
+ "40690 70.522621 84.574928 84.574928 \n",
65
+ "18977 145.266922 85.543114 85.543114 \n",
66
+ "\n",
67
+ " mfcc_mean_1 (mix) mfcc_mean_2 (mix) loudness_range_lu custom (mix) \\\n",
68
+ "27949 194.688721 -3.499274 6.624297 \n",
69
+ "40690 178.628433 -50.666416 3.653197 \n",
70
+ "18977 164.830765 -53.442635 1.909515 \n",
71
+ "\n",
72
+ " zero_crossing_rate (mix) vocab_richness \\\n",
73
+ "27949 0.032766 0.325 \n",
74
+ "40690 0.049714 0.345 \n",
75
+ "18977 0.059000 0.542 \n",
76
+ "\n",
77
+ " loudness_integrated_lufs custom (mix) readability_score \\\n",
78
+ "27949 -16.190006 76.6 \n",
79
+ "40690 -13.794414 2.1 \n",
80
+ "18977 -11.325106 1.4 \n",
81
+ "\n",
82
+ " energy_essentia (mix) energy_librosa (mix) rms_energy_mean (mix) \\\n",
83
+ "27949 0.259487 0.465622 0.466699 \n",
84
+ "40690 0.330070 0.547306 0.547923 \n",
85
+ "18977 0.439053 0.604893 0.661765 \n",
86
+ "\n",
87
+ " sentiment_score melody_complexity (vocals) avg_word_length \n",
88
+ "27949 0.035 2.666667 4.67 \n",
89
+ "40690 0.336 3.250000 3.76 \n",
90
+ "18977 -0.013 2.916667 3.42 "
91
+ ],
92
+ "text/html": [
93
+ "<div>\n",
94
+ "<style scoped>\n",
95
+ " .dataframe tbody tr th:only-of-type {\n",
96
+ " vertical-align: middle;\n",
97
+ " }\n",
98
+ "\n",
99
+ " .dataframe tbody tr th {\n",
100
+ " vertical-align: top;\n",
101
+ " }\n",
102
+ "\n",
103
+ " .dataframe thead th {\n",
104
+ " text-align: right;\n",
105
+ " }\n",
106
+ "</style>\n",
107
+ "<table border=\"1\" class=\"dataframe\">\n",
108
+ " <thead>\n",
109
+ " <tr style=\"text-align: right;\">\n",
110
+ " <th></th>\n",
111
+ " <th>_id</th>\n",
112
+ " <th>genre</th>\n",
113
+ " <th>genre_subgenre</th>\n",
114
+ " <th>trimmed_audio_duration_sec</th>\n",
115
+ " <th>syllable_count</th>\n",
116
+ " <th>word_count</th>\n",
117
+ " <th>spectral_contrast_mean (mix)</th>\n",
118
+ " <th>chroma_mean (mix)</th>\n",
119
+ " <th>melody_variability (vocals)</th>\n",
120
+ " <th>rhythm_onset_rate (mix)</th>\n",
121
+ " <th>spectral_centroid_mean custom (mix)</th>\n",
122
+ " <th>repetition_score custom (mix)</th>\n",
123
+ " <th>pitch_std (mix)</th>\n",
124
+ " <th>tempo_bpm_corrected (mix)</th>\n",
125
+ " <th>tempo_bpm_original (mix)</th>\n",
126
+ " <th>mfcc_mean_1 (mix)</th>\n",
127
+ " <th>mfcc_mean_2 (mix)</th>\n",
128
+ " <th>loudness_range_lu custom (mix)</th>\n",
129
+ " <th>zero_crossing_rate (mix)</th>\n",
130
+ " <th>vocab_richness</th>\n",
131
+ " <th>loudness_integrated_lufs custom (mix)</th>\n",
132
+ " <th>readability_score</th>\n",
133
+ " <th>energy_essentia (mix)</th>\n",
134
+ " <th>energy_librosa (mix)</th>\n",
135
+ " <th>rms_energy_mean (mix)</th>\n",
136
+ " <th>sentiment_score</th>\n",
137
+ " <th>melody_complexity (vocals)</th>\n",
138
+ " <th>avg_word_length</th>\n",
139
+ " </tr>\n",
140
+ " </thead>\n",
141
+ " <tbody>\n",
142
+ " <tr>\n",
143
+ " <th>27949</th>\n",
144
+ " <td>69b29e6c05f5ce265b882008</td>\n",
145
+ " <td>Rock</td>\n",
146
+ " <td>Rock --- ['Indie Folk', 'Country Folk', 'Indie...</td>\n",
147
+ " <td>191.332000</td>\n",
148
+ " <td>287.0</td>\n",
149
+ " <td>197.0</td>\n",
150
+ " <td>19.647192</td>\n",
151
+ " <td>0.453619</td>\n",
152
+ " <td>0.479049</td>\n",
153
+ " <td>1.927778</td>\n",
154
+ " <td>0.093420</td>\n",
155
+ " <td>0.006936</td>\n",
156
+ " <td>56.905636</td>\n",
157
+ " <td>123.635239</td>\n",
158
+ " <td>123.635239</td>\n",
159
+ " <td>194.688721</td>\n",
160
+ " <td>-3.499274</td>\n",
161
+ " <td>6.624297</td>\n",
162
+ " <td>0.032766</td>\n",
163
+ " <td>0.325</td>\n",
164
+ " <td>-16.190006</td>\n",
165
+ " <td>76.6</td>\n",
166
+ " <td>0.259487</td>\n",
167
+ " <td>0.465622</td>\n",
168
+ " <td>0.466699</td>\n",
169
+ " <td>0.035</td>\n",
170
+ " <td>2.666667</td>\n",
171
+ " <td>4.67</td>\n",
172
+ " </tr>\n",
173
+ " <tr>\n",
174
+ " <th>40690</th>\n",
175
+ " <td>69b12fabebd9b3c1ca39e859</td>\n",
176
+ " <td>Country</td>\n",
177
+ " <td>Country --- ['Americana', 'Country Rock', 'Alt...</td>\n",
178
+ " <td>215.146667</td>\n",
179
+ " <td>245.0</td>\n",
180
+ " <td>200.0</td>\n",
181
+ " <td>19.253367</td>\n",
182
+ " <td>0.484467</td>\n",
183
+ " <td>0.600583</td>\n",
184
+ " <td>4.220377</td>\n",
185
+ " <td>0.111674</td>\n",
186
+ " <td>0.005806</td>\n",
187
+ " <td>70.522621</td>\n",
188
+ " <td>84.574928</td>\n",
189
+ " <td>84.574928</td>\n",
190
+ " <td>178.628433</td>\n",
191
+ " <td>-50.666416</td>\n",
192
+ " <td>3.653197</td>\n",
193
+ " <td>0.049714</td>\n",
194
+ " <td>0.345</td>\n",
195
+ " <td>-13.794414</td>\n",
196
+ " <td>2.1</td>\n",
197
+ " <td>0.330070</td>\n",
198
+ " <td>0.547306</td>\n",
199
+ " <td>0.547923</td>\n",
200
+ " <td>0.336</td>\n",
201
+ " <td>3.250000</td>\n",
202
+ " <td>3.76</td>\n",
203
+ " </tr>\n",
204
+ " <tr>\n",
205
+ " <th>18977</th>\n",
206
+ " <td>692969b1c5db350c7bdaf13d</td>\n",
207
+ " <td>Rock</td>\n",
208
+ " <td>Rock --- ['Noise Pop', 'Classic Alternative Ro...</td>\n",
209
+ " <td>203.948458</td>\n",
210
+ " <td>145.0</td>\n",
211
+ " <td>118.0</td>\n",
212
+ " <td>18.226969</td>\n",
213
+ " <td>0.532658</td>\n",
214
+ " <td>0.647673</td>\n",
215
+ " <td>1.694444</td>\n",
216
+ " <td>0.124794</td>\n",
217
+ " <td>0.006610</td>\n",
218
+ " <td>145.266922</td>\n",
219
+ " <td>85.543114</td>\n",
220
+ " <td>85.543114</td>\n",
221
+ " <td>164.830765</td>\n",
222
+ " <td>-53.442635</td>\n",
223
+ " <td>1.909515</td>\n",
224
+ " <td>0.059000</td>\n",
225
+ " <td>0.542</td>\n",
226
+ " <td>-11.325106</td>\n",
227
+ " <td>1.4</td>\n",
228
+ " <td>0.439053</td>\n",
229
+ " <td>0.604893</td>\n",
230
+ " <td>0.661765</td>\n",
231
+ " <td>-0.013</td>\n",
232
+ " <td>2.916667</td>\n",
233
+ " <td>3.42</td>\n",
234
+ " </tr>\n",
235
+ " </tbody>\n",
236
+ "</table>\n",
237
+ "</div>"
238
+ ]
239
+ },
240
+ "execution_count": 1,
241
+ "metadata": {},
242
+ "output_type": "execute_result"
243
+ }
244
+ ],
245
+ "execution_count": 1
246
+ },
247
+ {
248
+ "metadata": {
249
+ "ExecuteTime": {
250
+ "end_time": "2026-03-30T08:56:11.085130700Z",
251
+ "start_time": "2026-03-30T08:56:10.592907600Z"
252
+ }
253
+ },
254
+ "cell_type": "code",
255
+ "source": [
256
+ "def parse_subgenres(text):\n",
257
+ " if pd.isna(text) or text == \"\":\n",
258
+ " return []\n",
259
+ " match = re.search(r\"\\[(.*?)\\]\", str(text))\n",
260
+ " if match:\n",
261
+ " try:\n",
262
+ " genre_list = ast.literal_eval(\"[\" + match.group(1) + \"]\")\n",
263
+ " return [g.strip() for g in genre_list if g.strip()]\n",
264
+ " except:\n",
265
+ " return []\n",
266
+ " return []\n",
267
+ "\n",
268
+ "df['subgenres_list'] = df['genre_subgenre'].apply(parse_subgenres)\n",
269
+ "\n"
270
+ ],
271
+ "id": "67098b6deadbc3ef",
272
+ "outputs": [],
273
+ "execution_count": 2
274
+ },
275
+ {
276
+ "metadata": {
277
+ "ExecuteTime": {
278
+ "end_time": "2026-03-30T08:56:23.372487600Z",
279
+ "start_time": "2026-03-30T08:56:23.315805900Z"
280
+ }
281
+ },
282
+ "cell_type": "code",
283
+ "source": "df['subgenres_list'][0]",
284
+ "id": "e0fc9a9de1291e67",
285
+ "outputs": [
286
+ {
287
+ "data": {
288
+ "text/plain": [
289
+ "['Noise Pop', 'Classic Alternative Rock', 'Indie Rock']"
290
+ ]
291
+ },
292
+ "execution_count": 3,
293
+ "metadata": {},
294
+ "output_type": "execute_result"
295
+ }
296
+ ],
297
+ "execution_count": 3
298
+ },
299
+ {
300
+ "metadata": {},
301
+ "cell_type": "code",
302
+ "outputs": [],
303
+ "execution_count": null,
304
+ "source": [
305
+ "mlb = MultiLabelBinarizer()\n",
306
+ "y = mlb.fit_transform(df['subgenres_list'])\n",
307
+ "\n",
308
+ "drop_cols = ['_id', 'genre', 'genre_subgenre', 'subgenres_list', 'labels',\n",
309
+ " 'trimmed_audio_duration_sec', 'syllable_count', 'word_count',\n",
310
+ " 'sentiment_score', 'avg_word_length']\n",
311
+ "\n",
312
+ "feature_cols = [c for c in df.columns if c not in drop_cols]\n",
313
+ "X = df[feature_cols].copy()\n",
314
+ "X = X.select_dtypes(include=[np.number]).fillna(0)\n",
315
+ "\n",
316
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
317
+ "\n",
318
+ "scaler = StandardScaler()\n",
319
+ "X_train_scaled = scaler.fit_transform(X_train)\n",
320
+ "X_test_scaled = scaler.transform(X_test)\n",
321
+ "\n",
322
+ "model = MultiOutputClassifier(\n",
323
+ " XGBClassifier(\n",
324
+ " n_estimators=1500,\n",
325
+ " learning_rate=0.03,\n",
326
+ " max_depth=8,\n",
327
+ " subsample=0.85,\n",
328
+ " colsample_bytree=0.8,\n",
329
+ " random_state=42,\n",
330
+ " eval_metric='logloss'\n",
331
+ " ),\n",
332
+ " n_jobs=-1\n",
333
+ ")\n",
334
+ "\n",
335
+ "model.fit(X_train_scaled, y_train)\n",
336
+ "\n",
337
+ "\n",
338
+ "def get_accuracy(X_scaled, y_true, name=\"\"):\n",
339
+ " y_pred = model.predict(X_scaled)\n",
340
+ " subset_acc = np.mean(np.all(y_pred == y_true, axis=1))\n",
341
+ " micro_f1 = f1_score(y_true, y_pred, average='micro')\n",
342
+ " print(f\"{name} Subset Accuracy: {subset_acc:.4f}\")\n",
343
+ " print(f\"{name} Micro F1 Score: {micro_f1:.4f}\")\n",
344
+ "\n",
345
+ "print(\"=== MODEL ACCURACY ===\")\n",
346
+ "get_accuracy(X_train_scaled, y_train, \"Train\")\n",
347
+ "get_accuracy(X_test_scaled, y_test, \"Test\")\n",
348
+ "\n",
349
+ "def manual_predict(row):\n",
350
+ " input_df = pd.DataFrame([row])\n",
351
+ " input_numeric = input_df.select_dtypes(include=[np.number]).fillna(0)\n",
352
+ "\n",
353
+ " for col in X.columns:\n",
354
+ " if col not in input_numeric.columns:\n",
355
+ " input_numeric[col] = 0\n",
356
+ "\n",
357
+ " input_numeric = input_numeric[X.columns]\n",
358
+ " input_scaled = scaler.transform(input_numeric)\n",
359
+ "\n",
360
+ " probas = model.predict_proba(input_scaled)\n",
361
+ " threshold = 0.20\n",
362
+ " pred_binary = np.zeros((1, y.shape[1]), dtype=int)\n",
363
+ "\n",
364
+ " for i in range(len(probas)):\n",
365
+ " if probas[i][0][1] > threshold:\n",
366
+ " pred_binary[0, i] = 1\n",
367
+ "\n",
368
+ " subgenres = mlb.inverse_transform(pred_binary)[0]\n",
369
+ " #main_genre = row.get('genre', 'Rock')\n",
370
+ "\n",
371
+ " if len(subgenres) == 0:\n",
372
+ " return f\"None\"\n",
373
+ " return f\"{', '.join(sorted(subgenres))}\""
374
+ ],
375
+ "id": "c1086565f89c486b"
376
+ },
377
+ {
378
+ "metadata": {
379
+ "ExecuteTime": {
380
+ "end_time": "2026-03-27T14:45:34.721003500Z",
381
+ "start_time": "2026-03-27T14:45:34.665870Z"
382
+ }
383
+ },
384
+ "cell_type": "code",
385
+ "source": "X.head()",
386
+ "id": "7036f929b47b9932",
387
+ "outputs": [
388
+ {
389
+ "data": {
390
+ "text/plain": [
391
+ " spectral_contrast_mean (mix) chroma_mean (mix) \\\n",
392
+ "0 18.938621 0.462013 \n",
393
+ "1 18.361412 0.515950 \n",
394
+ "2 17.545991 0.505095 \n",
395
+ "3 19.065528 0.532723 \n",
396
+ "4 18.549622 0.560616 \n",
397
+ "\n",
398
+ " melody_variability (vocals) rhythm_onset_rate (mix) \\\n",
399
+ "0 0.887239 0.550994 \n",
400
+ "1 0.553844 1.785610 \n",
401
+ "2 0.887161 1.691063 \n",
402
+ "3 0.917722 4.237960 \n",
403
+ "4 0.967337 0.847676 \n",
404
+ "\n",
405
+ " spectral_centroid_mean custom (mix) repetition_score custom (mix) \\\n",
406
+ "0 0.124953 0.015320 \n",
407
+ "1 0.178827 0.010278 \n",
408
+ "2 0.115442 0.010617 \n",
409
+ "3 0.134564 0.009808 \n",
410
+ "4 0.179751 0.008122 \n",
411
+ "\n",
412
+ " pitch_std (mix) tempo_bpm_corrected (mix) tempo_bpm_original (mix) \\\n",
413
+ "0 170.196686 117.459671 117.459671 \n",
414
+ "1 174.526428 93.132881 93.132881 \n",
415
+ "2 98.435074 105.041779 105.041779 \n",
416
+ "3 144.853897 94.973419 94.973419 \n",
417
+ "4 94.283890 108.527031 108.527031 \n",
418
+ "\n",
419
+ " mfcc_mean_1 (mix) mfcc_mean_2 (mix) loudness_range_lu custom (mix) \\\n",
420
+ "0 159.208038 -47.135399 17.099110 \n",
421
+ "1 124.982330 -54.048897 10.356828 \n",
422
+ "2 178.284927 -41.860600 4.583549 \n",
423
+ "3 166.110214 -79.556442 3.334763 \n",
424
+ "4 118.163094 -54.811043 0.855955 \n",
425
+ "\n",
426
+ " zero_crossing_rate (mix) vocab_richness \\\n",
427
+ "0 0.065750 0.842 \n",
428
+ "1 0.115698 0.312 \n",
429
+ "2 0.050672 0.462 \n",
430
+ "3 0.085350 0.273 \n",
431
+ "4 0.104765 0.350 \n",
432
+ "\n",
433
+ " loudness_integrated_lufs custom (mix) readability_score \\\n",
434
+ "0 -17.450430 1.6 \n",
435
+ "1 -14.291489 5.2 \n",
436
+ "2 -14.021948 18.8 \n",
437
+ "3 -11.496668 1.7 \n",
438
+ "4 -10.031396 52.0 \n",
439
+ "\n",
440
+ " energy_essentia (mix) energy_librosa (mix) rms_energy_mean (mix) \\\n",
441
+ "0 0.400498 0.541834 0.545006 \n",
442
+ "1 0.286286 0.491097 0.492117 \n",
443
+ "2 0.330273 0.539636 0.541554 \n",
444
+ "3 0.525465 0.693286 0.702081 \n",
445
+ "4 0.498986 0.696552 0.698920 \n",
446
+ "\n",
447
+ " melody_complexity (vocals) \n",
448
+ "0 2.833333 \n",
449
+ "1 2.333333 \n",
450
+ "2 3.083333 \n",
451
+ "3 3.166667 \n",
452
+ "4 3.500000 "
453
+ ],
454
+ "text/html": [
455
+ "<div>\n",
456
+ "<style scoped>\n",
457
+ " .dataframe tbody tr th:only-of-type {\n",
458
+ " vertical-align: middle;\n",
459
+ " }\n",
460
+ "\n",
461
+ " .dataframe tbody tr th {\n",
462
+ " vertical-align: top;\n",
463
+ " }\n",
464
+ "\n",
465
+ " .dataframe thead th {\n",
466
+ " text-align: right;\n",
467
+ " }\n",
468
+ "</style>\n",
469
+ "<table border=\"1\" class=\"dataframe\">\n",
470
+ " <thead>\n",
471
+ " <tr style=\"text-align: right;\">\n",
472
+ " <th></th>\n",
473
+ " <th>spectral_contrast_mean (mix)</th>\n",
474
+ " <th>chroma_mean (mix)</th>\n",
475
+ " <th>melody_variability (vocals)</th>\n",
476
+ " <th>rhythm_onset_rate (mix)</th>\n",
477
+ " <th>spectral_centroid_mean custom (mix)</th>\n",
478
+ " <th>repetition_score custom (mix)</th>\n",
479
+ " <th>pitch_std (mix)</th>\n",
480
+ " <th>tempo_bpm_corrected (mix)</th>\n",
481
+ " <th>tempo_bpm_original (mix)</th>\n",
482
+ " <th>mfcc_mean_1 (mix)</th>\n",
483
+ " <th>mfcc_mean_2 (mix)</th>\n",
484
+ " <th>loudness_range_lu custom (mix)</th>\n",
485
+ " <th>zero_crossing_rate (mix)</th>\n",
486
+ " <th>vocab_richness</th>\n",
487
+ " <th>loudness_integrated_lufs custom (mix)</th>\n",
488
+ " <th>readability_score</th>\n",
489
+ " <th>energy_essentia (mix)</th>\n",
490
+ " <th>energy_librosa (mix)</th>\n",
491
+ " <th>rms_energy_mean (mix)</th>\n",
492
+ " <th>melody_complexity (vocals)</th>\n",
493
+ " </tr>\n",
494
+ " </thead>\n",
495
+ " <tbody>\n",
496
+ " <tr>\n",
497
+ " <th>0</th>\n",
498
+ " <td>18.938621</td>\n",
499
+ " <td>0.462013</td>\n",
500
+ " <td>0.887239</td>\n",
501
+ " <td>0.550994</td>\n",
502
+ " <td>0.124953</td>\n",
503
+ " <td>0.015320</td>\n",
504
+ " <td>170.196686</td>\n",
505
+ " <td>117.459671</td>\n",
506
+ " <td>117.459671</td>\n",
507
+ " <td>159.208038</td>\n",
508
+ " <td>-47.135399</td>\n",
509
+ " <td>17.099110</td>\n",
510
+ " <td>0.065750</td>\n",
511
+ " <td>0.842</td>\n",
512
+ " <td>-17.450430</td>\n",
513
+ " <td>1.6</td>\n",
514
+ " <td>0.400498</td>\n",
515
+ " <td>0.541834</td>\n",
516
+ " <td>0.545006</td>\n",
517
+ " <td>2.833333</td>\n",
518
+ " </tr>\n",
519
+ " <tr>\n",
520
+ " <th>1</th>\n",
521
+ " <td>18.361412</td>\n",
522
+ " <td>0.515950</td>\n",
523
+ " <td>0.553844</td>\n",
524
+ " <td>1.785610</td>\n",
525
+ " <td>0.178827</td>\n",
526
+ " <td>0.010278</td>\n",
527
+ " <td>174.526428</td>\n",
528
+ " <td>93.132881</td>\n",
529
+ " <td>93.132881</td>\n",
530
+ " <td>124.982330</td>\n",
531
+ " <td>-54.048897</td>\n",
532
+ " <td>10.356828</td>\n",
533
+ " <td>0.115698</td>\n",
534
+ " <td>0.312</td>\n",
535
+ " <td>-14.291489</td>\n",
536
+ " <td>5.2</td>\n",
537
+ " <td>0.286286</td>\n",
538
+ " <td>0.491097</td>\n",
539
+ " <td>0.492117</td>\n",
540
+ " <td>2.333333</td>\n",
541
+ " </tr>\n",
542
+ " <tr>\n",
543
+ " <th>2</th>\n",
544
+ " <td>17.545991</td>\n",
545
+ " <td>0.505095</td>\n",
546
+ " <td>0.887161</td>\n",
547
+ " <td>1.691063</td>\n",
548
+ " <td>0.115442</td>\n",
549
+ " <td>0.010617</td>\n",
550
+ " <td>98.435074</td>\n",
551
+ " <td>105.041779</td>\n",
552
+ " <td>105.041779</td>\n",
553
+ " <td>178.284927</td>\n",
554
+ " <td>-41.860600</td>\n",
555
+ " <td>4.583549</td>\n",
556
+ " <td>0.050672</td>\n",
557
+ " <td>0.462</td>\n",
558
+ " <td>-14.021948</td>\n",
559
+ " <td>18.8</td>\n",
560
+ " <td>0.330273</td>\n",
561
+ " <td>0.539636</td>\n",
562
+ " <td>0.541554</td>\n",
563
+ " <td>3.083333</td>\n",
564
+ " </tr>\n",
565
+ " <tr>\n",
566
+ " <th>3</th>\n",
567
+ " <td>19.065528</td>\n",
568
+ " <td>0.532723</td>\n",
569
+ " <td>0.917722</td>\n",
570
+ " <td>4.237960</td>\n",
571
+ " <td>0.134564</td>\n",
572
+ " <td>0.009808</td>\n",
573
+ " <td>144.853897</td>\n",
574
+ " <td>94.973419</td>\n",
575
+ " <td>94.973419</td>\n",
576
+ " <td>166.110214</td>\n",
577
+ " <td>-79.556442</td>\n",
578
+ " <td>3.334763</td>\n",
579
+ " <td>0.085350</td>\n",
580
+ " <td>0.273</td>\n",
581
+ " <td>-11.496668</td>\n",
582
+ " <td>1.7</td>\n",
583
+ " <td>0.525465</td>\n",
584
+ " <td>0.693286</td>\n",
585
+ " <td>0.702081</td>\n",
586
+ " <td>3.166667</td>\n",
587
+ " </tr>\n",
588
+ " <tr>\n",
589
+ " <th>4</th>\n",
590
+ " <td>18.549622</td>\n",
591
+ " <td>0.560616</td>\n",
592
+ " <td>0.967337</td>\n",
593
+ " <td>0.847676</td>\n",
594
+ " <td>0.179751</td>\n",
595
+ " <td>0.008122</td>\n",
596
+ " <td>94.283890</td>\n",
597
+ " <td>108.527031</td>\n",
598
+ " <td>108.527031</td>\n",
599
+ " <td>118.163094</td>\n",
600
+ " <td>-54.811043</td>\n",
601
+ " <td>0.855955</td>\n",
602
+ " <td>0.104765</td>\n",
603
+ " <td>0.350</td>\n",
604
+ " <td>-10.031396</td>\n",
605
+ " <td>52.0</td>\n",
606
+ " <td>0.498986</td>\n",
607
+ " <td>0.696552</td>\n",
608
+ " <td>0.698920</td>\n",
609
+ " <td>3.500000</td>\n",
610
+ " </tr>\n",
611
+ " </tbody>\n",
612
+ "</table>\n",
613
+ "</div>"
614
+ ]
615
+ },
616
+ "execution_count": 6,
617
+ "metadata": {},
618
+ "output_type": "execute_result"
619
+ }
620
+ ],
621
+ "execution_count": 6
622
+ },
623
+ {
624
+ "metadata": {
625
+ "ExecuteTime": {
626
+ "end_time": "2026-03-27T14:55:56.357032800Z",
627
+ "start_time": "2026-03-27T14:55:56.275652600Z"
628
+ }
629
+ },
630
+ "cell_type": "code",
631
+ "source": "X.shape",
632
+ "id": "f7d2011c6b5f8699",
633
+ "outputs": [
634
+ {
635
+ "data": {
636
+ "text/plain": [
637
+ "(41935, 20)"
638
+ ]
639
+ },
640
+ "execution_count": 15,
641
+ "metadata": {},
642
+ "output_type": "execute_result"
643
+ }
644
+ ],
645
+ "execution_count": 15
646
+ },
647
+ {
648
+ "metadata": {
649
+ "ExecuteTime": {
650
+ "end_time": "2026-03-27T15:10:02.865313500Z",
651
+ "start_time": "2026-03-27T15:10:02.807027900Z"
652
+ }
653
+ },
654
+ "cell_type": "code",
655
+ "source": "df[[\"subgenres_list\",\"genre\"]].iloc[502,]",
656
+ "id": "d462801f323e88a",
657
+ "outputs": [
658
+ {
659
+ "data": {
660
+ "text/plain": [
661
+ "subgenres_list [Modern Alternative Rock, Indie Rock]\n",
662
+ "genre Rock\n",
663
+ "Name: 502, dtype: object"
664
+ ]
665
+ },
666
+ "execution_count": 43,
667
+ "metadata": {},
668
+ "output_type": "execute_result"
669
+ }
670
+ ],
671
+ "execution_count": 43
672
+ },
673
+ {
674
+ "metadata": {
675
+ "ExecuteTime": {
676
+ "end_time": "2026-03-27T15:13:50.114563800Z",
677
+ "start_time": "2026-03-27T15:13:50.060507800Z"
678
+ }
679
+ },
680
+ "cell_type": "code",
681
+ "source": [
682
+ "pd.set_option('display.max_columns', None)\n",
683
+ "df[(df[\"genre\"]==\"Metal\")].head(2)"
684
+ ],
685
+ "id": "1fbd1d7876fb8996",
686
+ "outputs": [
687
+ {
688
+ "data": {
689
+ "text/plain": [
690
+ " _id genre \\\n",
691
+ "41856 691b00908e8610886093475b Metal \n",
692
+ "41857 691c4b322bf3d72bd0be023e Metal \n",
693
+ "\n",
694
+ " genre_subgenre \\\n",
695
+ "41856 Metal --- ['Black Metal', 'Death Metal', 'Doom... \n",
696
+ "41857 Metal --- ['Heavy Metal', 'Thrash Metal'] \n",
697
+ "\n",
698
+ " trimmed_audio_duration_sec syllable_count word_count \\\n",
699
+ "41856 185.602971 185.0 158.0 \n",
700
+ "41857 131.819683 353.0 253.0 \n",
701
+ "\n",
702
+ " spectral_contrast_mean (mix) chroma_mean (mix) \\\n",
703
+ "41856 20.152063 0.408171 \n",
704
+ "41857 19.495984 0.517214 \n",
705
+ "\n",
706
+ " melody_variability (vocals) rhythm_onset_rate (mix) \\\n",
707
+ "41856 0.374856 2.027778 \n",
708
+ "41857 0.475671 5.803382 \n",
709
+ "\n",
710
+ " spectral_centroid_mean custom (mix) repetition_score custom (mix) \\\n",
711
+ "41856 0.093899 0.007882 \n",
712
+ "41857 0.168338 0.007993 \n",
713
+ "\n",
714
+ " pitch_std (mix) tempo_bpm_corrected (mix) tempo_bpm_original (mix) \\\n",
715
+ "41856 108.278755 104.605293 104.605293 \n",
716
+ "41857 134.086761 138.027847 138.027847 \n",
717
+ "\n",
718
+ " mfcc_mean_1 (mix) mfcc_mean_2 (mix) loudness_range_lu custom (mix) \\\n",
719
+ "41856 163.308655 22.928051 7.131201 \n",
720
+ "41857 113.692780 -26.532072 2.994879 \n",
721
+ "\n",
722
+ " zero_crossing_rate (mix) vocab_richness \\\n",
723
+ "41856 0.026933 0.582 \n",
724
+ "41857 0.069337 0.352 \n",
725
+ "\n",
726
+ " loudness_integrated_lufs custom (mix) readability_score \\\n",
727
+ "41856 -18.566132 27.8 \n",
728
+ "41857 -13.850066 3.0 \n",
729
+ "\n",
730
+ " energy_essentia (mix) energy_librosa (mix) rms_energy_mean (mix) \\\n",
731
+ "41856 0.153738 0.335069 0.346289 \n",
732
+ "41857 0.266081 0.441120 0.467461 \n",
733
+ "\n",
734
+ " sentiment_score melody_complexity (vocals) avg_word_length \\\n",
735
+ "41856 0.050 1.916667 3.97 \n",
736
+ "41857 0.128 2.250000 4.24 \n",
737
+ "\n",
738
+ " subgenres_list \n",
739
+ "41856 [Black Metal, Death Metal, Doom Metal, Heavy M... \n",
740
+ "41857 [Heavy Metal, Thrash Metal] "
741
+ ],
742
+ "text/html": [
743
+ "<div>\n",
744
+ "<style scoped>\n",
745
+ " .dataframe tbody tr th:only-of-type {\n",
746
+ " vertical-align: middle;\n",
747
+ " }\n",
748
+ "\n",
749
+ " .dataframe tbody tr th {\n",
750
+ " vertical-align: top;\n",
751
+ " }\n",
752
+ "\n",
753
+ " .dataframe thead th {\n",
754
+ " text-align: right;\n",
755
+ " }\n",
756
+ "</style>\n",
757
+ "<table border=\"1\" class=\"dataframe\">\n",
758
+ " <thead>\n",
759
+ " <tr style=\"text-align: right;\">\n",
760
+ " <th></th>\n",
761
+ " <th>_id</th>\n",
762
+ " <th>genre</th>\n",
763
+ " <th>genre_subgenre</th>\n",
764
+ " <th>trimmed_audio_duration_sec</th>\n",
765
+ " <th>syllable_count</th>\n",
766
+ " <th>word_count</th>\n",
767
+ " <th>spectral_contrast_mean (mix)</th>\n",
768
+ " <th>chroma_mean (mix)</th>\n",
769
+ " <th>melody_variability (vocals)</th>\n",
770
+ " <th>rhythm_onset_rate (mix)</th>\n",
771
+ " <th>spectral_centroid_mean custom (mix)</th>\n",
772
+ " <th>repetition_score custom (mix)</th>\n",
773
+ " <th>pitch_std (mix)</th>\n",
774
+ " <th>tempo_bpm_corrected (mix)</th>\n",
775
+ " <th>tempo_bpm_original (mix)</th>\n",
776
+ " <th>mfcc_mean_1 (mix)</th>\n",
777
+ " <th>mfcc_mean_2 (mix)</th>\n",
778
+ " <th>loudness_range_lu custom (mix)</th>\n",
779
+ " <th>zero_crossing_rate (mix)</th>\n",
780
+ " <th>vocab_richness</th>\n",
781
+ " <th>loudness_integrated_lufs custom (mix)</th>\n",
782
+ " <th>readability_score</th>\n",
783
+ " <th>energy_essentia (mix)</th>\n",
784
+ " <th>energy_librosa (mix)</th>\n",
785
+ " <th>rms_energy_mean (mix)</th>\n",
786
+ " <th>sentiment_score</th>\n",
787
+ " <th>melody_complexity (vocals)</th>\n",
788
+ " <th>avg_word_length</th>\n",
789
+ " <th>subgenres_list</th>\n",
790
+ " </tr>\n",
791
+ " </thead>\n",
792
+ " <tbody>\n",
793
+ " <tr>\n",
794
+ " <th>41856</th>\n",
795
+ " <td>691b00908e8610886093475b</td>\n",
796
+ " <td>Metal</td>\n",
797
+ " <td>Metal --- ['Black Metal', 'Death Metal', 'Doom...</td>\n",
798
+ " <td>185.602971</td>\n",
799
+ " <td>185.0</td>\n",
800
+ " <td>158.0</td>\n",
801
+ " <td>20.152063</td>\n",
802
+ " <td>0.408171</td>\n",
803
+ " <td>0.374856</td>\n",
804
+ " <td>2.027778</td>\n",
805
+ " <td>0.093899</td>\n",
806
+ " <td>0.007882</td>\n",
807
+ " <td>108.278755</td>\n",
808
+ " <td>104.605293</td>\n",
809
+ " <td>104.605293</td>\n",
810
+ " <td>163.308655</td>\n",
811
+ " <td>22.928051</td>\n",
812
+ " <td>7.131201</td>\n",
813
+ " <td>0.026933</td>\n",
814
+ " <td>0.582</td>\n",
815
+ " <td>-18.566132</td>\n",
816
+ " <td>27.8</td>\n",
817
+ " <td>0.153738</td>\n",
818
+ " <td>0.335069</td>\n",
819
+ " <td>0.346289</td>\n",
820
+ " <td>0.050</td>\n",
821
+ " <td>1.916667</td>\n",
822
+ " <td>3.97</td>\n",
823
+ " <td>[Black Metal, Death Metal, Doom Metal, Heavy M...</td>\n",
824
+ " </tr>\n",
825
+ " <tr>\n",
826
+ " <th>41857</th>\n",
827
+ " <td>691c4b322bf3d72bd0be023e</td>\n",
828
+ " <td>Metal</td>\n",
829
+ " <td>Metal --- ['Heavy Metal', 'Thrash Metal']</td>\n",
830
+ " <td>131.819683</td>\n",
831
+ " <td>353.0</td>\n",
832
+ " <td>253.0</td>\n",
833
+ " <td>19.495984</td>\n",
834
+ " <td>0.517214</td>\n",
835
+ " <td>0.475671</td>\n",
836
+ " <td>5.803382</td>\n",
837
+ " <td>0.168338</td>\n",
838
+ " <td>0.007993</td>\n",
839
+ " <td>134.086761</td>\n",
840
+ " <td>138.027847</td>\n",
841
+ " <td>138.027847</td>\n",
842
+ " <td>113.692780</td>\n",
843
+ " <td>-26.532072</td>\n",
844
+ " <td>2.994879</td>\n",
845
+ " <td>0.069337</td>\n",
846
+ " <td>0.352</td>\n",
847
+ " <td>-13.850066</td>\n",
848
+ " <td>3.0</td>\n",
849
+ " <td>0.266081</td>\n",
850
+ " <td>0.441120</td>\n",
851
+ " <td>0.467461</td>\n",
852
+ " <td>0.128</td>\n",
853
+ " <td>2.250000</td>\n",
854
+ " <td>4.24</td>\n",
855
+ " <td>[Heavy Metal, Thrash Metal]</td>\n",
856
+ " </tr>\n",
857
+ " </tbody>\n",
858
+ "</table>\n",
859
+ "</div>"
860
+ ]
861
+ },
862
+ "execution_count": 50,
863
+ "metadata": {},
864
+ "output_type": "execute_result"
865
+ }
866
+ ],
867
+ "execution_count": 50
868
+ },
869
+ {
870
+ "metadata": {
871
+ "ExecuteTime": {
872
+ "end_time": "2026-03-27T15:24:32.000214700Z",
873
+ "start_time": "2026-03-27T15:24:31.938664600Z"
874
+ }
875
+ },
876
+ "cell_type": "code",
877
+ "source": "X.iloc[41856].to_dict()",
878
+ "id": "37ef9304bdae300c",
879
+ "outputs": [
880
+ {
881
+ "data": {
882
+ "text/plain": [
883
+ "{'spectral_contrast_mean (mix)': 20.1520629356514,\n",
884
+ " 'chroma_mean (mix)': 0.408170998096466,\n",
885
+ " 'melody_variability (vocals)': 0.3748557548841115,\n",
886
+ " 'rhythm_onset_rate (mix)': 2.0277777777777777,\n",
887
+ " 'spectral_centroid_mean custom (mix)': 0.0938994687064515,\n",
888
+ " 'repetition_score custom (mix)': 0.0078822056596288,\n",
889
+ " 'pitch_std (mix)': 108.27875518798828,\n",
890
+ " 'tempo_bpm_corrected (mix)': 104.60529327392578,\n",
891
+ " 'tempo_bpm_original (mix)': 104.60529327392578,\n",
892
+ " 'mfcc_mean_1 (mix)': 163.30865478515625,\n",
893
+ " 'mfcc_mean_2 (mix)': 22.928050994873047,\n",
894
+ " 'loudness_range_lu custom (mix)': 7.131200790405273,\n",
895
+ " 'zero_crossing_rate (mix)': 0.02693298086524,\n",
896
+ " 'vocab_richness': 0.582,\n",
897
+ " 'loudness_integrated_lufs custom (mix)': -18.566131591796875,\n",
898
+ " 'readability_score': 27.8,\n",
899
+ " 'energy_essentia (mix)': 0.1537376961168162,\n",
900
+ " 'energy_librosa (mix)': 0.335069430512918,\n",
901
+ " 'rms_energy_mean (mix)': 0.3462888819034169,\n",
902
+ " 'melody_complexity (vocals)': 1.9166666666666667}"
903
+ ]
904
+ },
905
+ "execution_count": 58,
906
+ "metadata": {},
907
+ "output_type": "execute_result"
908
+ }
909
+ ],
910
+ "execution_count": 58
911
+ },
912
+ {
913
+ "metadata": {
914
+ "ExecuteTime": {
915
+ "end_time": "2026-03-27T15:32:09.375740200Z",
916
+ "start_time": "2026-03-27T15:32:08.891439Z"
917
+ }
918
+ },
919
+ "cell_type": "code",
920
+ "source": [
921
+ "print(\"\\n=== FIRST ROW PREDICTION ===\")\n",
922
+ "print(manual_predict(X.iloc[508].to_dict()))\n",
923
+ "\n",
924
+ "print(\"\\n=== LAST ROW PREDICTION ===\")\n",
925
+ "print(manual_predict(X.iloc[-1].to_dict()))"
926
+ ],
927
+ "id": "fef35c101a9a6ad",
928
+ "outputs": [
929
+ {
930
+ "name": "stdout",
931
+ "output_type": "stream",
932
+ "text": [
933
+ "\n",
934
+ "=== FIRST ROW PREDICTION ===\n",
935
+ "Indie Pop, Indie Rock, Modern Alternative Rock\n",
936
+ "\n",
937
+ "=== LAST ROW PREDICTION ===\n",
938
+ "Gothic Metal, Heavy Metal, Metalcore\n"
939
+ ]
940
+ }
941
+ ],
942
+ "execution_count": 63
943
+ },
944
+ {
945
+ "metadata": {
946
+ "ExecuteTime": {
947
+ "end_time": "2026-03-27T15:31:39.900012500Z",
948
+ "start_time": "2026-03-27T15:31:39.638999300Z"
949
+ }
950
+ },
951
+ "cell_type": "code",
952
+ "source": [
953
+ "audio_features_ = {\n",
954
+ " 'spectral_contrast_mean (mix)': 17.245,\n",
955
+ " 'chroma_mean (mix)': 0.482,\n",
956
+ " 'melody_variability (vocals)': 0.612,\n",
957
+ " 'rhythm_onset_rate (mix)': 2.145,\n",
958
+ " 'spectral_centroid_mean custom (mix)': 0.165,\n",
959
+ " 'repetition_score custom (mix)': 0.015,\n",
960
+ " 'pitch_std (mix)': 120.75,\n",
961
+ " 'tempo_bpm_corrected (mix)': 98.34,\n",
962
+ " 'tempo_bpm_original (mix)': 100.12,\n",
963
+ " 'mfcc_mean_1 (mix)': 128.56,\n",
964
+ " 'mfcc_mean_2 (mix)': -35.78,\n",
965
+ " 'loudness_range_lu custom (mix)': 8.45,\n",
966
+ " 'zero_crossing_rate (mix)': 0.089,\n",
967
+ " 'vocab_richness': 0.29,\n",
968
+ " 'loudness_integrated_lufs custom (mix)': -12.85,\n",
969
+ " 'readability_score': 48.5,\n",
970
+ " 'energy_essentia (mix)': 0.41,\n",
971
+ " 'energy_librosa (mix)': 0.52,\n",
972
+ " 'rms_energy_mean (mix)': 0.50,\n",
973
+ " 'melody_complexity (vocals)': 2.75\n",
974
+ "}\n",
975
+ "\n",
976
+ "answer = manual_predict(audio_features_)\n",
977
+ "print(answer)"
978
+ ],
979
+ "id": "6d8f315863783a68",
980
+ "outputs": [
981
+ {
982
+ "name": "stdout",
983
+ "output_type": "stream",
984
+ "text": [
985
+ "Indie Pop, Indie Rock, Modern Alternative Rock\n"
986
+ ]
987
+ }
988
+ ],
989
+ "execution_count": 61
990
+ },
991
+ {
992
+ "metadata": {
993
+ "ExecuteTime": {
994
+ "end_time": "2026-03-27T15:31:41.650948200Z",
995
+ "start_time": "2026-03-27T15:31:41.421694900Z"
996
+ }
997
+ },
998
+ "cell_type": "code",
999
+ "source": [
1000
+ "fake_audio_features_2 = {'spectral_contrast_mean (mix)': 20.1520629356514,\n",
1001
+ " 'chroma_mean (mix)': 0.408170998096466,\n",
1002
+ " 'melody_variability (vocals)': 0.3748557548841115,\n",
1003
+ " 'rhythm_onset_rate (mix)': 2.0277777777777777,\n",
1004
+ " 'spectral_centroid_mean custom (mix)': 0.0938994687064515,\n",
1005
+ " 'repetition_score custom (mix)': 0.0078822056596288,\n",
1006
+ " 'pitch_std (mix)': 108.27875518798828,\n",
1007
+ " 'tempo_bpm_corrected (mix)': 104.60529327392578,\n",
1008
+ " 'tempo_bpm_original (mix)': 104.60529327392578,\n",
1009
+ " 'mfcc_mean_1 (mix)': 163.30865478515625,\n",
1010
+ " 'mfcc_mean_2 (mix)': 22.928050994873047,\n",
1011
+ " 'loudness_range_lu custom (mix)': 7.131200790405273,\n",
1012
+ " 'zero_crossing_rate (mix)': 0.02693298086524,\n",
1013
+ " 'vocab_richness': 0.582,\n",
1014
+ " 'loudness_integrated_lufs custom (mix)': -18.566131591796875,\n",
1015
+ " 'readability_score': 27.8,\n",
1016
+ " 'energy_essentia (mix)': 0.1537376961168162,\n",
1017
+ " 'energy_librosa (mix)': 0.335069430512918,\n",
1018
+ " 'rms_energy_mean (mix)': 0.3462888819034169,\n",
1019
+ " 'melody_complexity (vocals)': 1.9166666666666667}\n",
1020
+ "answer = manual_predict(fake_audio_features_2)\n",
1021
+ "print(answer)"
1022
+ ],
1023
+ "id": "b998289b1d156dc2",
1024
+ "outputs": [
1025
+ {
1026
+ "name": "stdout",
1027
+ "output_type": "stream",
1028
+ "text": [
1029
+ "Black Metal, Death Metal, Doom Metal, Heavy Metal, Metalcore, Thrash Metal\n"
1030
+ ]
1031
+ }
1032
+ ],
1033
+ "execution_count": 62
1034
+ },
1035
+ {
1036
+ "metadata": {
1037
+ "ExecuteTime": {
1038
+ "end_time": "2026-03-27T15:24:02.082478Z",
1039
+ "start_time": "2026-03-27T15:24:01.848007Z"
1040
+ }
1041
+ },
1042
+ "cell_type": "code",
1043
+ "source": [
1044
+ "fake_metal_audio = {\n",
1045
+ " 'spectral_contrast_mean (mix)': 24.85,\n",
1046
+ " 'chroma_mean (mix)': 0.38,\n",
1047
+ " 'melody_variability (vocals)': 0.72,\n",
1048
+ " 'rhythm_onset_rate (mix)': 4.95,\n",
1049
+ " 'spectral_centroid_mean custom (mix)': 0.29,\n",
1050
+ " 'repetition_score custom (mix)': 0.006,\n",
1051
+ " 'pitch_std (mix)': 210.45,\n",
1052
+ " 'tempo_bpm_corrected (mix)': 145.60,\n",
1053
+ " 'tempo_bpm_original (mix)': 147.20,\n",
1054
+ " 'mfcc_mean_1 (mix)': 142.75,\n",
1055
+ " 'mfcc_mean_2 (mix)': -18.34,\n",
1056
+ " 'loudness_range_lu custom (mix)': 6.25,\n",
1057
+ " 'zero_crossing_rate (mix)': 0.18,\n",
1058
+ " 'vocab_richness': 0.27,\n",
1059
+ " 'loudness_integrated_lufs custom (mix)': -8.95,\n",
1060
+ " 'readability_score': 42.0,\n",
1061
+ " 'energy_essentia (mix)': 0.78,\n",
1062
+ " 'energy_librosa (mix)': 0.82,\n",
1063
+ " 'rms_energy_mean (mix)': 0.80,\n",
1064
+ " 'melody_complexity (vocals)': 3.95\n",
1065
+ "}\n",
1066
+ "\n",
1067
+ "answer = manual_predict(fake_metal_audio)\n",
1068
+ "print(answer)"
1069
+ ],
1070
+ "id": "b265ee987709c2a3",
1071
+ "outputs": [
1072
+ {
1073
+ "name": "stdout",
1074
+ "output_type": "stream",
1075
+ "text": [
1076
+ "Rock -- Americana\n"
1077
+ ]
1078
+ }
1079
+ ],
1080
+ "execution_count": 57
1081
+ },
1082
+ {
1083
+ "metadata": {
1084
+ "ExecuteTime": {
1085
+ "end_time": "2026-03-27T15:38:00.278309800Z",
1086
+ "start_time": "2026-03-27T15:37:57.734946600Z"
1087
+ }
1088
+ },
1089
+ "cell_type": "code",
1090
+ "source": "",
1091
+ "id": "228a47b066312e75",
1092
+ "outputs": [
1093
+ {
1094
+ "name": "stdout",
1095
+ "output_type": "stream",
1096
+ "text": [
1097
+ "✅ Model saved as genre_model.pkl\n"
1098
+ ]
1099
+ }
1100
+ ],
1101
+ "execution_count": 64
1102
+ },
1103
+ {
1104
+ "metadata": {
1105
+ "ExecuteTime": {
1106
+ "end_time": "2026-03-27T15:49:22.832888800Z",
1107
+ "start_time": "2026-03-27T15:49:20.228927800Z"
1108
+ }
1109
+ },
1110
+ "cell_type": "code",
1111
+ "source": [
1112
+ "import joblib\n",
1113
+ "bundle = {\n",
1114
+ " \"model\": model,\n",
1115
+ " \"scaler\": scaler,\n",
1116
+ " \"mlb\": mlb,\n",
1117
+ " \"feature_cols\": X.columns.tolist()\n",
1118
+ "}\n",
1119
+ "joblib.dump(bundle, \"genre_model1.pkl\")\n",
1120
+ "print(\"Model saved successfully!\")"
1121
+ ],
1122
+ "id": "694614771e1c62a9",
1123
+ "outputs": [
1124
+ {
1125
+ "name": "stdout",
1126
+ "output_type": "stream",
1127
+ "text": [
1128
+ "Model saved successfully!\n"
1129
+ ]
1130
+ }
1131
+ ],
1132
+ "execution_count": 68
1133
+ },
1134
+ {
1135
+ "metadata": {
1136
+ "ExecuteTime": {
1137
+ "end_time": "2026-03-27T15:49:54.380812800Z",
1138
+ "start_time": "2026-03-27T15:49:51.308892Z"
1139
+ }
1140
+ },
1141
+ "cell_type": "code",
1142
+ "source": [
1143
+ "import joblib\n",
1144
+ "bundle = joblib.load(\"genre_model1.pkl\")\n",
1145
+ "model = bundle[\"model\"]\n",
1146
+ "scaler = bundle[\"scaler\"]\n",
1147
+ "mlb = bundle[\"mlb\"]\n",
1148
+ "feature_cols = bundle[\"feature_cols\"]"
1149
+ ],
1150
+ "id": "bb9f24b4e0947c22",
1151
+ "outputs": [],
1152
+ "execution_count": 70
1153
+ },
1154
+ {
1155
+ "metadata": {
1156
+ "ExecuteTime": {
1157
+ "end_time": "2026-03-27T15:51:05.220558400Z",
1158
+ "start_time": "2026-03-27T15:51:05.157646Z"
1159
+ }
1160
+ },
1161
+ "cell_type": "code",
1162
+ "source": [
1163
+ "def predict_from_loaded(row, model, scaler, mlb, feature_cols):\n",
1164
+ " import pandas as pd\n",
1165
+ " import numpy as np\n",
1166
+ " input_df = pd.DataFrame([row])\n",
1167
+ " input_numeric = input_df.select_dtypes(include=[np.number]).fillna(0)\n",
1168
+ " for col in feature_cols:\n",
1169
+ " if col not in input_numeric.columns:\n",
1170
+ " input_numeric[col] = 0\n",
1171
+ " input_numeric = input_numeric[feature_cols]\n",
1172
+ " input_scaled = scaler.transform(input_numeric)\n",
1173
+ " probas = model.predict_proba(input_scaled)\n",
1174
+ " threshold = 0.20\n",
1175
+ " pred_binary = np.zeros((1, len(probas)), dtype=int)\n",
1176
+ " for i in range(len(probas)):\n",
1177
+ " if probas[i][0][1] > threshold:\n",
1178
+ " pred_binary[0, i] = 1\n",
1179
+ " subgenres = mlb.inverse_transform(pred_binary)[0]\n",
1180
+ " if len(subgenres) == 0:\n",
1181
+ " return \"None\"\n",
1182
+ " return \", \".join(sorted(subgenres))"
1183
+ ],
1184
+ "id": "5391f7697e93bcad",
1185
+ "outputs": [],
1186
+ "execution_count": 71
1187
+ },
1188
+ {
1189
+ "metadata": {
1190
+ "ExecuteTime": {
1191
+ "end_time": "2026-03-27T15:51:42.665671400Z",
1192
+ "start_time": "2026-03-27T15:51:42.509361Z"
1193
+ }
1194
+ },
1195
+ "cell_type": "code",
1196
+ "source": [
1197
+ "audio_features_ = {'spectral_contrast_mean (mix)': 20.1520629356514,\n",
1198
+ " 'chroma_mean (mix)': 0.408170998096466,\n",
1199
+ " 'melody_variability (vocals)': 0.3748557548841115,\n",
1200
+ " 'rhythm_onset_rate (mix)': 2.0277777777777777,\n",
1201
+ " 'spectral_centroid_mean custom (mix)': 0.0938994687064515,\n",
1202
+ " 'repetition_score custom (mix)': 0.0078822056596288,\n",
1203
+ " 'pitch_std (mix)': 108.27875518798828,\n",
1204
+ " 'tempo_bpm_corrected (mix)': 104.60529327392578,\n",
1205
+ " 'tempo_bpm_original (mix)': 104.60529327392578,\n",
1206
+ " 'mfcc_mean_1 (mix)': 163.30865478515625,\n",
1207
+ " 'mfcc_mean_2 (mix)': 22.928050994873047,\n",
1208
+ " 'loudness_range_lu custom (mix)': 7.131200790405273,\n",
1209
+ " 'zero_crossing_rate (mix)': 0.02693298086524,\n",
1210
+ " 'vocab_richness': 0.582,\n",
1211
+ " 'loudness_integrated_lufs custom (mix)': -18.566131591796875,\n",
1212
+ " 'readability_score': 27.8,\n",
1213
+ " 'energy_essentia (mix)': 0.1537376961168162,\n",
1214
+ " 'energy_librosa (mix)': 0.335069430512918,\n",
1215
+ " 'rms_energy_mean (mix)': 0.3462888819034169,\n",
1216
+ " 'melody_complexity (vocals)': 1.9166666666666667}\n",
1217
+ "\n",
1218
+ "result = predict_from_loaded(audio_features_, model, scaler, mlb, feature_cols)\n",
1219
+ "print(result)"
1220
+ ],
1221
+ "id": "cbc80439273b814f",
1222
+ "outputs": [
1223
+ {
1224
+ "name": "stdout",
1225
+ "output_type": "stream",
1226
+ "text": [
1227
+ "Black Metal, Death Metal, Doom Metal, Heavy Metal, Metalcore, Thrash Metal\n"
1228
+ ]
1229
+ }
1230
+ ],
1231
+ "execution_count": 73
1232
+ },
1233
+ {
1234
+ "metadata": {},
1235
+ "cell_type": "markdown",
1236
+ "source": "### 1) All Data",
1237
+ "id": "9a576cffaecfd507"
1238
+ },
1239
+ {
1240
+ "metadata": {
1241
+ "ExecuteTime": {
1242
+ "end_time": "2026-03-30T09:59:36.204702500Z",
1243
+ "start_time": "2026-03-30T09:59:34.858890200Z"
1244
+ }
1245
+ },
1246
+ "cell_type": "code",
1247
+ "source": [
1248
+ "import pandas as pd\n",
1249
+ "path = r\"C:\\Users\\VigneshSubramani\\Downloads\\features_dataset.csv\"\n",
1250
+ "pd.set_option(\"display.max_columns\", None)\n",
1251
+ "data = pd.read_csv(path,low_memory=False)\n",
1252
+ "data"
1253
+ ],
1254
+ "id": "16dcec42226f1c5d",
1255
+ "outputs": [
1256
+ {
1257
+ "data": {
1258
+ "text/plain": [
1259
+ " track_url genre \\\n",
1260
+ "0 https://open.spotify.com/track/38RJG8nLAwGycit... Country \n",
1261
+ "1 https://open.spotify.com/track/2uqYupMHANxnwge... Country \n",
1262
+ "2 https://open.spotify.com/track/48X5k2vce5rXckg... Country \n",
1263
+ "3 https://open.spotify.com/track/3SaS0Va6o6jYkWv... Country \n",
1264
+ "4 https://open.spotify.com/track/09hVlIVHtEE1nLU... Country \n",
1265
+ "... ... ... \n",
1266
+ "138685 https://open.spotify.com/track/6rZno3nqeT7hv2P... Latin Urban \n",
1267
+ "138686 https://open.spotify.com/track/0O4ZJKR2QDLGrBZ... Latin Urban \n",
1268
+ "138687 https://open.spotify.com/track/4oV82xuYkYgyM2n... Latin Urban \n",
1269
+ "138688 https://open.spotify.com/track/3jztoVZMBEaTX0b... Latin Urban \n",
1270
+ "138689 https://open.spotify.com/track/3mUr9hl2VhqorCu... Latin Urban \n",
1271
+ "\n",
1272
+ " name features.melody_complexity (vocals) \\\n",
1273
+ "0 Forever To Me 3.500000 \n",
1274
+ "1 Austin 3.250000 \n",
1275
+ "2 Dirt Cheap 2.750000 \n",
1276
+ "3 I Am Not Okay 2.333333 \n",
1277
+ "4 Burn It Down 3.166667 \n",
1278
+ "... ... ... \n",
1279
+ "138685 YOGURCITO NaN \n",
1280
+ "138686 Me Arriesgo Contigo NaN \n",
1281
+ "138687 Vocales NaN \n",
1282
+ "138688 Tímida NaN \n",
1283
+ "138689 Romeo Y Julieta NaN \n",
1284
+ "\n",
1285
+ " features.melody_range (vocals) features.melody_variability (vocals) \\\n",
1286
+ "0 41.0 0.751713 \n",
1287
+ "1 35.0 0.715152 \n",
1288
+ "2 34.0 0.453325 \n",
1289
+ "3 34.0 0.436993 \n",
1290
+ "4 38.0 0.531330 \n",
1291
+ "... ... ... \n",
1292
+ "138685 NaN NaN \n",
1293
+ "138686 NaN NaN \n",
1294
+ "138687 NaN NaN \n",
1295
+ "138688 NaN NaN \n",
1296
+ "138689 NaN NaN \n",
1297
+ "\n",
1298
+ " features.tempo_bpm_original (mix) features.danceability custom (mix) \\\n",
1299
+ "0 141.384491 0.580731 \n",
1300
+ "1 105.666534 0.747039 \n",
1301
+ "2 110.124130 0.694952 \n",
1302
+ "3 141.088547 0.602134 \n",
1303
+ "4 148.782501 0.603468 \n",
1304
+ "... ... ... \n",
1305
+ "138685 NaN NaN \n",
1306
+ "138686 NaN NaN \n",
1307
+ "138687 NaN NaN \n",
1308
+ "138688 NaN NaN \n",
1309
+ "138689 NaN NaN \n",
1310
+ "\n",
1311
+ " features.loudness_integrated_lufs custom (mix) \\\n",
1312
+ "0 -15.467463 \n",
1313
+ "1 -12.137867 \n",
1314
+ "2 -14.214710 \n",
1315
+ "3 -12.991043 \n",
1316
+ "4 -12.433587 \n",
1317
+ "... ... \n",
1318
+ "138685 NaN \n",
1319
+ "138686 NaN \n",
1320
+ "138687 NaN \n",
1321
+ "138688 NaN \n",
1322
+ "138689 NaN \n",
1323
+ "\n",
1324
+ " features.loudness_range_lu custom (mix) \\\n",
1325
+ "0 11.282878 \n",
1326
+ "1 3.665868 \n",
1327
+ "2 8.500771 \n",
1328
+ "3 3.841528 \n",
1329
+ "4 5.801809 \n",
1330
+ "... ... \n",
1331
+ "138685 NaN \n",
1332
+ "138686 NaN \n",
1333
+ "138687 NaN \n",
1334
+ "138688 NaN \n",
1335
+ "138689 NaN \n",
1336
+ "\n",
1337
+ " features.energy_librosa (mix) features.energy_librosa_std (mix) \\\n",
1338
+ "0 0.419014 0.191760 \n",
1339
+ "1 0.469624 0.169444 \n",
1340
+ "2 0.427623 0.194988 \n",
1341
+ "3 0.524690 0.185032 \n",
1342
+ "4 0.566157 0.145061 \n",
1343
+ "... ... ... \n",
1344
+ "138685 NaN NaN \n",
1345
+ "138686 NaN NaN \n",
1346
+ "138687 NaN NaN \n",
1347
+ "138688 NaN NaN \n",
1348
+ "138689 NaN NaN \n",
1349
+ "\n",
1350
+ " features.energy_essentia (mix) features.energy_essentia_std (mix) \\\n",
1351
+ "0 0.225257 0.167297 \n",
1352
+ "1 0.255954 0.162414 \n",
1353
+ "2 0.226500 0.159222 \n",
1354
+ "3 0.321317 0.172458 \n",
1355
+ "4 0.355688 0.138871 \n",
1356
+ "... ... ... \n",
1357
+ "138685 NaN NaN \n",
1358
+ "138686 NaN NaN \n",
1359
+ "138687 NaN NaN \n",
1360
+ "138688 NaN NaN \n",
1361
+ "138689 NaN NaN \n",
1362
+ "\n",
1363
+ " features.energy_combined (mix) \\\n",
1364
+ "0 0.322135 \n",
1365
+ "1 0.362789 \n",
1366
+ "2 0.327062 \n",
1367
+ "3 0.423004 \n",
1368
+ "4 0.460922 \n",
1369
+ "... ... \n",
1370
+ "138685 NaN \n",
1371
+ "138686 NaN \n",
1372
+ "138687 NaN \n",
1373
+ "138688 NaN \n",
1374
+ "138689 NaN \n",
1375
+ "\n",
1376
+ " features.spectral_centroid_mean custom (mix) \\\n",
1377
+ "0 0.128541 \n",
1378
+ "1 0.121481 \n",
1379
+ "2 0.120138 \n",
1380
+ "3 0.140651 \n",
1381
+ "4 0.127909 \n",
1382
+ "... ... \n",
1383
+ "138685 NaN \n",
1384
+ "138686 NaN \n",
1385
+ "138687 NaN \n",
1386
+ "138688 NaN \n",
1387
+ "138689 NaN \n",
1388
+ "\n",
1389
+ " features.mfcc_mean_1 (mix) features.mfcc_mean_2 (mix) \\\n",
1390
+ "0 144.864594 -7.119251 \n",
1391
+ "1 160.318756 -3.738415 \n",
1392
+ "2 156.815781 -13.236840 \n",
1393
+ "3 143.763428 -21.374804 \n",
1394
+ "4 156.998581 -24.430223 \n",
1395
+ "... ... ... \n",
1396
+ "138685 NaN NaN \n",
1397
+ "138686 NaN NaN \n",
1398
+ "138687 NaN NaN \n",
1399
+ "138688 NaN NaN \n",
1400
+ "138689 NaN NaN \n",
1401
+ "\n",
1402
+ " features.chroma_mean (mix) features.spectral_contrast_mean (mix) \\\n",
1403
+ "0 0.482208 19.678119 \n",
1404
+ "1 0.501811 19.144755 \n",
1405
+ "2 0.452644 19.641929 \n",
1406
+ "3 0.434028 19.570383 \n",
1407
+ "4 0.493009 19.436073 \n",
1408
+ "... ... ... \n",
1409
+ "138685 NaN NaN \n",
1410
+ "138686 NaN NaN \n",
1411
+ "138687 NaN NaN \n",
1412
+ "138688 NaN NaN \n",
1413
+ "138689 NaN NaN \n",
1414
+ "\n",
1415
+ " features.repetition_score custom (mix) features.pitch_mean (mix) \\\n",
1416
+ "0 0.007355 188.741852 \n",
1417
+ "1 0.007224 225.259796 \n",
1418
+ "2 0.006500 184.371185 \n",
1419
+ "3 0.006950 232.608765 \n",
1420
+ "4 0.005922 241.072388 \n",
1421
+ "... ... ... \n",
1422
+ "138685 NaN NaN \n",
1423
+ "138686 NaN NaN \n",
1424
+ "138687 NaN NaN \n",
1425
+ "138688 NaN NaN \n",
1426
+ "138689 NaN NaN \n",
1427
+ "\n",
1428
+ " features.pitch_std (mix) features.rms_energy_mean (mix) \\\n",
1429
+ "0 54.632015 0.432170 \n",
1430
+ "1 90.800995 0.477567 \n",
1431
+ "2 49.438427 0.436157 \n",
1432
+ "3 60.514282 0.537289 \n",
1433
+ "4 98.557098 0.579285 \n",
1434
+ "... ... ... \n",
1435
+ "138685 NaN NaN \n",
1436
+ "138686 NaN NaN \n",
1437
+ "138687 NaN NaN \n",
1438
+ "138688 NaN NaN \n",
1439
+ "138689 NaN NaN \n",
1440
+ "\n",
1441
+ " features.rms_energy_std (mix) features.zero_crossing_rate (mix) \\\n",
1442
+ "0 0.194504 0.042678 \n",
1443
+ "1 0.166947 0.040959 \n",
1444
+ "2 0.192781 0.046456 \n",
1445
+ "3 0.180169 0.056903 \n",
1446
+ "4 0.140620 0.053897 \n",
1447
+ "... ... ... \n",
1448
+ "138685 NaN NaN \n",
1449
+ "138686 NaN NaN \n",
1450
+ "138687 NaN NaN \n",
1451
+ "138688 NaN NaN \n",
1452
+ "138689 NaN NaN \n",
1453
+ "\n",
1454
+ " sub_genre[0] sub_genre[1] sub_genre[2] \\\n",
1455
+ "0 Contemporary Country NaN NaN \n",
1456
+ "1 NaN NaN NaN \n",
1457
+ "2 Modern Neo-Traditional Contemporary Country NaN \n",
1458
+ "3 Americana Country Rock Country Folk \n",
1459
+ "4 NaN NaN NaN \n",
1460
+ "... ... ... ... \n",
1461
+ "138685 Latin Urban NaN NaN \n",
1462
+ "138686 Latin Urban NaN NaN \n",
1463
+ "138687 Latin Urban NaN NaN \n",
1464
+ "138688 Latin Urban NaN NaN \n",
1465
+ "138689 Latin Urban NaN NaN \n",
1466
+ "\n",
1467
+ " sub_genre[3] sub_genre[4] sub_genre[5] sub_genre[6] \\\n",
1468
+ "0 NaN NaN NaN NaN \n",
1469
+ "1 NaN NaN NaN NaN \n",
1470
+ "2 NaN NaN NaN NaN \n",
1471
+ "3 Contemporary Country NaN NaN NaN \n",
1472
+ "4 NaN NaN NaN NaN \n",
1473
+ "... ... ... ... ... \n",
1474
+ "138685 NaN NaN NaN NaN \n",
1475
+ "138686 NaN NaN NaN NaN \n",
1476
+ "138687 NaN NaN NaN NaN \n",
1477
+ "138688 NaN NaN NaN NaN \n",
1478
+ "138689 NaN NaN NaN NaN \n",
1479
+ "\n",
1480
+ " sub_genre[7] sub_genre[8] sub_genre[9] sub_genre[10] sub_genre[11] \\\n",
1481
+ "0 NaN NaN NaN NaN NaN \n",
1482
+ "1 NaN NaN NaN NaN NaN \n",
1483
+ "2 NaN NaN NaN NaN NaN \n",
1484
+ "3 NaN NaN NaN NaN NaN \n",
1485
+ "4 NaN NaN NaN NaN NaN \n",
1486
+ "... ... ... ... ... ... \n",
1487
+ "138685 NaN NaN NaN NaN NaN \n",
1488
+ "138686 NaN NaN NaN NaN NaN \n",
1489
+ "138687 NaN NaN NaN NaN NaN \n",
1490
+ "138688 NaN NaN NaN NaN NaN \n",
1491
+ "138689 NaN NaN NaN NaN NaN \n",
1492
+ "\n",
1493
+ " sub_genre[12] sub_genre[13] sub_genre[14] sub_genre[15] \n",
1494
+ "0 NaN NaN NaN NaN \n",
1495
+ "1 NaN NaN NaN NaN \n",
1496
+ "2 NaN NaN NaN NaN \n",
1497
+ "3 NaN NaN NaN NaN \n",
1498
+ "4 NaN NaN NaN NaN \n",
1499
+ "... ... ... ... ... \n",
1500
+ "138685 NaN NaN NaN NaN \n",
1501
+ "138686 NaN NaN NaN NaN \n",
1502
+ "138687 NaN NaN NaN NaN \n",
1503
+ "138688 NaN NaN NaN NaN \n",
1504
+ "138689 NaN NaN NaN NaN \n",
1505
+ "\n",
1506
+ "[138690 rows x 42 columns]"
1507
+ ],
1508
+ "text/html": [
1509
+ "<div>\n",
1510
+ "<style scoped>\n",
1511
+ " .dataframe tbody tr th:only-of-type {\n",
1512
+ " vertical-align: middle;\n",
1513
+ " }\n",
1514
+ "\n",
1515
+ " .dataframe tbody tr th {\n",
1516
+ " vertical-align: top;\n",
1517
+ " }\n",
1518
+ "\n",
1519
+ " .dataframe thead th {\n",
1520
+ " text-align: right;\n",
1521
+ " }\n",
1522
+ "</style>\n",
1523
+ "<table border=\"1\" class=\"dataframe\">\n",
1524
+ " <thead>\n",
1525
+ " <tr style=\"text-align: right;\">\n",
1526
+ " <th></th>\n",
1527
+ " <th>track_url</th>\n",
1528
+ " <th>genre</th>\n",
1529
+ " <th>name</th>\n",
1530
+ " <th>features.melody_complexity (vocals)</th>\n",
1531
+ " <th>features.melody_range (vocals)</th>\n",
1532
+ " <th>features.melody_variability (vocals)</th>\n",
1533
+ " <th>features.tempo_bpm_original (mix)</th>\n",
1534
+ " <th>features.danceability custom (mix)</th>\n",
1535
+ " <th>features.loudness_integrated_lufs custom (mix)</th>\n",
1536
+ " <th>features.loudness_range_lu custom (mix)</th>\n",
1537
+ " <th>features.energy_librosa (mix)</th>\n",
1538
+ " <th>features.energy_librosa_std (mix)</th>\n",
1539
+ " <th>features.energy_essentia (mix)</th>\n",
1540
+ " <th>features.energy_essentia_std (mix)</th>\n",
1541
+ " <th>features.energy_combined (mix)</th>\n",
1542
+ " <th>features.spectral_centroid_mean custom (mix)</th>\n",
1543
+ " <th>features.mfcc_mean_1 (mix)</th>\n",
1544
+ " <th>features.mfcc_mean_2 (mix)</th>\n",
1545
+ " <th>features.chroma_mean (mix)</th>\n",
1546
+ " <th>features.spectral_contrast_mean (mix)</th>\n",
1547
+ " <th>features.repetition_score custom (mix)</th>\n",
1548
+ " <th>features.pitch_mean (mix)</th>\n",
1549
+ " <th>features.pitch_std (mix)</th>\n",
1550
+ " <th>features.rms_energy_mean (mix)</th>\n",
1551
+ " <th>features.rms_energy_std (mix)</th>\n",
1552
+ " <th>features.zero_crossing_rate (mix)</th>\n",
1553
+ " <th>sub_genre[0]</th>\n",
1554
+ " <th>sub_genre[1]</th>\n",
1555
+ " <th>sub_genre[2]</th>\n",
1556
+ " <th>sub_genre[3]</th>\n",
1557
+ " <th>sub_genre[4]</th>\n",
1558
+ " <th>sub_genre[5]</th>\n",
1559
+ " <th>sub_genre[6]</th>\n",
1560
+ " <th>sub_genre[7]</th>\n",
1561
+ " <th>sub_genre[8]</th>\n",
1562
+ " <th>sub_genre[9]</th>\n",
1563
+ " <th>sub_genre[10]</th>\n",
1564
+ " <th>sub_genre[11]</th>\n",
1565
+ " <th>sub_genre[12]</th>\n",
1566
+ " <th>sub_genre[13]</th>\n",
1567
+ " <th>sub_genre[14]</th>\n",
1568
+ " <th>sub_genre[15]</th>\n",
1569
+ " </tr>\n",
1570
+ " </thead>\n",
1571
+ " <tbody>\n",
1572
+ " <tr>\n",
1573
+ " <th>0</th>\n",
1574
+ " <td>https://open.spotify.com/track/38RJG8nLAwGycit...</td>\n",
1575
+ " <td>Country</td>\n",
1576
+ " <td>Forever To Me</td>\n",
1577
+ " <td>3.500000</td>\n",
1578
+ " <td>41.0</td>\n",
1579
+ " <td>0.751713</td>\n",
1580
+ " <td>141.384491</td>\n",
1581
+ " <td>0.580731</td>\n",
1582
+ " <td>-15.467463</td>\n",
1583
+ " <td>11.282878</td>\n",
1584
+ " <td>0.419014</td>\n",
1585
+ " <td>0.191760</td>\n",
1586
+ " <td>0.225257</td>\n",
1587
+ " <td>0.167297</td>\n",
1588
+ " <td>0.322135</td>\n",
1589
+ " <td>0.128541</td>\n",
1590
+ " <td>144.864594</td>\n",
1591
+ " <td>-7.119251</td>\n",
1592
+ " <td>0.482208</td>\n",
1593
+ " <td>19.678119</td>\n",
1594
+ " <td>0.007355</td>\n",
1595
+ " <td>188.741852</td>\n",
1596
+ " <td>54.632015</td>\n",
1597
+ " <td>0.432170</td>\n",
1598
+ " <td>0.194504</td>\n",
1599
+ " <td>0.042678</td>\n",
1600
+ " <td>Contemporary Country</td>\n",
1601
+ " <td>NaN</td>\n",
1602
+ " <td>NaN</td>\n",
1603
+ " <td>NaN</td>\n",
1604
+ " <td>NaN</td>\n",
1605
+ " <td>NaN</td>\n",
1606
+ " <td>NaN</td>\n",
1607
+ " <td>NaN</td>\n",
1608
+ " <td>NaN</td>\n",
1609
+ " <td>NaN</td>\n",
1610
+ " <td>NaN</td>\n",
1611
+ " <td>NaN</td>\n",
1612
+ " <td>NaN</td>\n",
1613
+ " <td>NaN</td>\n",
1614
+ " <td>NaN</td>\n",
1615
+ " <td>NaN</td>\n",
1616
+ " </tr>\n",
1617
+ " <tr>\n",
1618
+ " <th>1</th>\n",
1619
+ " <td>https://open.spotify.com/track/2uqYupMHANxnwge...</td>\n",
1620
+ " <td>Country</td>\n",
1621
+ " <td>Austin</td>\n",
1622
+ " <td>3.250000</td>\n",
1623
+ " <td>35.0</td>\n",
1624
+ " <td>0.715152</td>\n",
1625
+ " <td>105.666534</td>\n",
1626
+ " <td>0.747039</td>\n",
1627
+ " <td>-12.137867</td>\n",
1628
+ " <td>3.665868</td>\n",
1629
+ " <td>0.469624</td>\n",
1630
+ " <td>0.169444</td>\n",
1631
+ " <td>0.255954</td>\n",
1632
+ " <td>0.162414</td>\n",
1633
+ " <td>0.362789</td>\n",
1634
+ " <td>0.121481</td>\n",
1635
+ " <td>160.318756</td>\n",
1636
+ " <td>-3.738415</td>\n",
1637
+ " <td>0.501811</td>\n",
1638
+ " <td>19.144755</td>\n",
1639
+ " <td>0.007224</td>\n",
1640
+ " <td>225.259796</td>\n",
1641
+ " <td>90.800995</td>\n",
1642
+ " <td>0.477567</td>\n",
1643
+ " <td>0.166947</td>\n",
1644
+ " <td>0.040959</td>\n",
1645
+ " <td>NaN</td>\n",
1646
+ " <td>NaN</td>\n",
1647
+ " <td>NaN</td>\n",
1648
+ " <td>NaN</td>\n",
1649
+ " <td>NaN</td>\n",
1650
+ " <td>NaN</td>\n",
1651
+ " <td>NaN</td>\n",
1652
+ " <td>NaN</td>\n",
1653
+ " <td>NaN</td>\n",
1654
+ " <td>NaN</td>\n",
1655
+ " <td>NaN</td>\n",
1656
+ " <td>NaN</td>\n",
1657
+ " <td>NaN</td>\n",
1658
+ " <td>NaN</td>\n",
1659
+ " <td>NaN</td>\n",
1660
+ " <td>NaN</td>\n",
1661
+ " </tr>\n",
1662
+ " <tr>\n",
1663
+ " <th>2</th>\n",
1664
+ " <td>https://open.spotify.com/track/48X5k2vce5rXckg...</td>\n",
1665
+ " <td>Country</td>\n",
1666
+ " <td>Dirt Cheap</td>\n",
1667
+ " <td>2.750000</td>\n",
1668
+ " <td>34.0</td>\n",
1669
+ " <td>0.453325</td>\n",
1670
+ " <td>110.124130</td>\n",
1671
+ " <td>0.694952</td>\n",
1672
+ " <td>-14.214710</td>\n",
1673
+ " <td>8.500771</td>\n",
1674
+ " <td>0.427623</td>\n",
1675
+ " <td>0.194988</td>\n",
1676
+ " <td>0.226500</td>\n",
1677
+ " <td>0.159222</td>\n",
1678
+ " <td>0.327062</td>\n",
1679
+ " <td>0.120138</td>\n",
1680
+ " <td>156.815781</td>\n",
1681
+ " <td>-13.236840</td>\n",
1682
+ " <td>0.452644</td>\n",
1683
+ " <td>19.641929</td>\n",
1684
+ " <td>0.006500</td>\n",
1685
+ " <td>184.371185</td>\n",
1686
+ " <td>49.438427</td>\n",
1687
+ " <td>0.436157</td>\n",
1688
+ " <td>0.192781</td>\n",
1689
+ " <td>0.046456</td>\n",
1690
+ " <td>Modern Neo-Traditional</td>\n",
1691
+ " <td>Contemporary Country</td>\n",
1692
+ " <td>NaN</td>\n",
1693
+ " <td>NaN</td>\n",
1694
+ " <td>NaN</td>\n",
1695
+ " <td>NaN</td>\n",
1696
+ " <td>NaN</td>\n",
1697
+ " <td>NaN</td>\n",
1698
+ " <td>NaN</td>\n",
1699
+ " <td>NaN</td>\n",
1700
+ " <td>NaN</td>\n",
1701
+ " <td>NaN</td>\n",
1702
+ " <td>NaN</td>\n",
1703
+ " <td>NaN</td>\n",
1704
+ " <td>NaN</td>\n",
1705
+ " <td>NaN</td>\n",
1706
+ " </tr>\n",
1707
+ " <tr>\n",
1708
+ " <th>3</th>\n",
1709
+ " <td>https://open.spotify.com/track/3SaS0Va6o6jYkWv...</td>\n",
1710
+ " <td>Country</td>\n",
1711
+ " <td>I Am Not Okay</td>\n",
1712
+ " <td>2.333333</td>\n",
1713
+ " <td>34.0</td>\n",
1714
+ " <td>0.436993</td>\n",
1715
+ " <td>141.088547</td>\n",
1716
+ " <td>0.602134</td>\n",
1717
+ " <td>-12.991043</td>\n",
1718
+ " <td>3.841528</td>\n",
1719
+ " <td>0.524690</td>\n",
1720
+ " <td>0.185032</td>\n",
1721
+ " <td>0.321317</td>\n",
1722
+ " <td>0.172458</td>\n",
1723
+ " <td>0.423004</td>\n",
1724
+ " <td>0.140651</td>\n",
1725
+ " <td>143.763428</td>\n",
1726
+ " <td>-21.374804</td>\n",
1727
+ " <td>0.434028</td>\n",
1728
+ " <td>19.570383</td>\n",
1729
+ " <td>0.006950</td>\n",
1730
+ " <td>232.608765</td>\n",
1731
+ " <td>60.514282</td>\n",
1732
+ " <td>0.537289</td>\n",
1733
+ " <td>0.180169</td>\n",
1734
+ " <td>0.056903</td>\n",
1735
+ " <td>Americana</td>\n",
1736
+ " <td>Country Rock</td>\n",
1737
+ " <td>Country Folk</td>\n",
1738
+ " <td>Contemporary Country</td>\n",
1739
+ " <td>NaN</td>\n",
1740
+ " <td>NaN</td>\n",
1741
+ " <td>NaN</td>\n",
1742
+ " <td>NaN</td>\n",
1743
+ " <td>NaN</td>\n",
1744
+ " <td>NaN</td>\n",
1745
+ " <td>NaN</td>\n",
1746
+ " <td>NaN</td>\n",
1747
+ " <td>NaN</td>\n",
1748
+ " <td>NaN</td>\n",
1749
+ " <td>NaN</td>\n",
1750
+ " <td>NaN</td>\n",
1751
+ " </tr>\n",
1752
+ " <tr>\n",
1753
+ " <th>4</th>\n",
1754
+ " <td>https://open.spotify.com/track/09hVlIVHtEE1nLU...</td>\n",
1755
+ " <td>Country</td>\n",
1756
+ " <td>Burn It Down</td>\n",
1757
+ " <td>3.166667</td>\n",
1758
+ " <td>38.0</td>\n",
1759
+ " <td>0.531330</td>\n",
1760
+ " <td>148.782501</td>\n",
1761
+ " <td>0.603468</td>\n",
1762
+ " <td>-12.433587</td>\n",
1763
+ " <td>5.801809</td>\n",
1764
+ " <td>0.566157</td>\n",
1765
+ " <td>0.145061</td>\n",
1766
+ " <td>0.355688</td>\n",
1767
+ " <td>0.138871</td>\n",
1768
+ " <td>0.460922</td>\n",
1769
+ " <td>0.127909</td>\n",
1770
+ " <td>156.998581</td>\n",
1771
+ " <td>-24.430223</td>\n",
1772
+ " <td>0.493009</td>\n",
1773
+ " <td>19.436073</td>\n",
1774
+ " <td>0.005922</td>\n",
1775
+ " <td>241.072388</td>\n",
1776
+ " <td>98.557098</td>\n",
1777
+ " <td>0.579285</td>\n",
1778
+ " <td>0.140620</td>\n",
1779
+ " <td>0.053897</td>\n",
1780
+ " <td>NaN</td>\n",
1781
+ " <td>NaN</td>\n",
1782
+ " <td>NaN</td>\n",
1783
+ " <td>NaN</td>\n",
1784
+ " <td>NaN</td>\n",
1785
+ " <td>NaN</td>\n",
1786
+ " <td>NaN</td>\n",
1787
+ " <td>NaN</td>\n",
1788
+ " <td>NaN</td>\n",
1789
+ " <td>NaN</td>\n",
1790
+ " <td>NaN</td>\n",
1791
+ " <td>NaN</td>\n",
1792
+ " <td>NaN</td>\n",
1793
+ " <td>NaN</td>\n",
1794
+ " <td>NaN</td>\n",
1795
+ " <td>NaN</td>\n",
1796
+ " </tr>\n",
1797
+ " <tr>\n",
1798
+ " <th>...</th>\n",
1799
+ " <td>...</td>\n",
1800
+ " <td>...</td>\n",
1801
+ " <td>...</td>\n",
1802
+ " <td>...</td>\n",
1803
+ " <td>...</td>\n",
1804
+ " <td>...</td>\n",
1805
+ " <td>...</td>\n",
1806
+ " <td>...</td>\n",
1807
+ " <td>...</td>\n",
1808
+ " <td>...</td>\n",
1809
+ " <td>...</td>\n",
1810
+ " <td>...</td>\n",
1811
+ " <td>...</td>\n",
1812
+ " <td>...</td>\n",
1813
+ " <td>...</td>\n",
1814
+ " <td>...</td>\n",
1815
+ " <td>...</td>\n",
1816
+ " <td>...</td>\n",
1817
+ " <td>...</td>\n",
1818
+ " <td>...</td>\n",
1819
+ " <td>...</td>\n",
1820
+ " <td>...</td>\n",
1821
+ " <td>...</td>\n",
1822
+ " <td>...</td>\n",
1823
+ " <td>...</td>\n",
1824
+ " <td>...</td>\n",
1825
+ " <td>...</td>\n",
1826
+ " <td>...</td>\n",
1827
+ " <td>...</td>\n",
1828
+ " <td>...</td>\n",
1829
+ " <td>...</td>\n",
1830
+ " <td>...</td>\n",
1831
+ " <td>...</td>\n",
1832
+ " <td>...</td>\n",
1833
+ " <td>...</td>\n",
1834
+ " <td>...</td>\n",
1835
+ " <td>...</td>\n",
1836
+ " <td>...</td>\n",
1837
+ " <td>...</td>\n",
1838
+ " <td>...</td>\n",
1839
+ " <td>...</td>\n",
1840
+ " <td>...</td>\n",
1841
+ " </tr>\n",
1842
+ " <tr>\n",
1843
+ " <th>138685</th>\n",
1844
+ " <td>https://open.spotify.com/track/6rZno3nqeT7hv2P...</td>\n",
1845
+ " <td>Latin Urban</td>\n",
1846
+ " <td>YOGURCITO</td>\n",
1847
+ " <td>NaN</td>\n",
1848
+ " <td>NaN</td>\n",
1849
+ " <td>NaN</td>\n",
1850
+ " <td>NaN</td>\n",
1851
+ " <td>NaN</td>\n",
1852
+ " <td>NaN</td>\n",
1853
+ " <td>NaN</td>\n",
1854
+ " <td>NaN</td>\n",
1855
+ " <td>NaN</td>\n",
1856
+ " <td>NaN</td>\n",
1857
+ " <td>NaN</td>\n",
1858
+ " <td>NaN</td>\n",
1859
+ " <td>NaN</td>\n",
1860
+ " <td>NaN</td>\n",
1861
+ " <td>NaN</td>\n",
1862
+ " <td>NaN</td>\n",
1863
+ " <td>NaN</td>\n",
1864
+ " <td>NaN</td>\n",
1865
+ " <td>NaN</td>\n",
1866
+ " <td>NaN</td>\n",
1867
+ " <td>NaN</td>\n",
1868
+ " <td>NaN</td>\n",
1869
+ " <td>NaN</td>\n",
1870
+ " <td>Latin Urban</td>\n",
1871
+ " <td>NaN</td>\n",
1872
+ " <td>NaN</td>\n",
1873
+ " <td>NaN</td>\n",
1874
+ " <td>NaN</td>\n",
1875
+ " <td>NaN</td>\n",
1876
+ " <td>NaN</td>\n",
1877
+ " <td>NaN</td>\n",
1878
+ " <td>NaN</td>\n",
1879
+ " <td>NaN</td>\n",
1880
+ " <td>NaN</td>\n",
1881
+ " <td>NaN</td>\n",
1882
+ " <td>NaN</td>\n",
1883
+ " <td>NaN</td>\n",
1884
+ " <td>NaN</td>\n",
1885
+ " <td>NaN</td>\n",
1886
+ " </tr>\n",
1887
+ " <tr>\n",
1888
+ " <th>138686</th>\n",
1889
+ " <td>https://open.spotify.com/track/0O4ZJKR2QDLGrBZ...</td>\n",
1890
+ " <td>Latin Urban</td>\n",
1891
+ " <td>Me Arriesgo Contigo</td>\n",
1892
+ " <td>NaN</td>\n",
1893
+ " <td>NaN</td>\n",
1894
+ " <td>NaN</td>\n",
1895
+ " <td>NaN</td>\n",
1896
+ " <td>NaN</td>\n",
1897
+ " <td>NaN</td>\n",
1898
+ " <td>NaN</td>\n",
1899
+ " <td>NaN</td>\n",
1900
+ " <td>NaN</td>\n",
1901
+ " <td>NaN</td>\n",
1902
+ " <td>NaN</td>\n",
1903
+ " <td>NaN</td>\n",
1904
+ " <td>NaN</td>\n",
1905
+ " <td>NaN</td>\n",
1906
+ " <td>NaN</td>\n",
1907
+ " <td>NaN</td>\n",
1908
+ " <td>NaN</td>\n",
1909
+ " <td>NaN</td>\n",
1910
+ " <td>NaN</td>\n",
1911
+ " <td>NaN</td>\n",
1912
+ " <td>NaN</td>\n",
1913
+ " <td>NaN</td>\n",
1914
+ " <td>NaN</td>\n",
1915
+ " <td>Latin Urban</td>\n",
1916
+ " <td>NaN</td>\n",
1917
+ " <td>NaN</td>\n",
1918
+ " <td>NaN</td>\n",
1919
+ " <td>NaN</td>\n",
1920
+ " <td>NaN</td>\n",
1921
+ " <td>NaN</td>\n",
1922
+ " <td>NaN</td>\n",
1923
+ " <td>NaN</td>\n",
1924
+ " <td>NaN</td>\n",
1925
+ " <td>NaN</td>\n",
1926
+ " <td>NaN</td>\n",
1927
+ " <td>NaN</td>\n",
1928
+ " <td>NaN</td>\n",
1929
+ " <td>NaN</td>\n",
1930
+ " <td>NaN</td>\n",
1931
+ " </tr>\n",
1932
+ " <tr>\n",
1933
+ " <th>138687</th>\n",
1934
+ " <td>https://open.spotify.com/track/4oV82xuYkYgyM2n...</td>\n",
1935
+ " <td>Latin Urban</td>\n",
1936
+ " <td>Vocales</td>\n",
1937
+ " <td>NaN</td>\n",
1938
+ " <td>NaN</td>\n",
1939
+ " <td>NaN</td>\n",
1940
+ " <td>NaN</td>\n",
1941
+ " <td>NaN</td>\n",
1942
+ " <td>NaN</td>\n",
1943
+ " <td>NaN</td>\n",
1944
+ " <td>NaN</td>\n",
1945
+ " <td>NaN</td>\n",
1946
+ " <td>NaN</td>\n",
1947
+ " <td>NaN</td>\n",
1948
+ " <td>NaN</td>\n",
1949
+ " <td>NaN</td>\n",
1950
+ " <td>NaN</td>\n",
1951
+ " <td>NaN</td>\n",
1952
+ " <td>NaN</td>\n",
1953
+ " <td>NaN</td>\n",
1954
+ " <td>NaN</td>\n",
1955
+ " <td>NaN</td>\n",
1956
+ " <td>NaN</td>\n",
1957
+ " <td>NaN</td>\n",
1958
+ " <td>NaN</td>\n",
1959
+ " <td>NaN</td>\n",
1960
+ " <td>Latin Urban</td>\n",
1961
+ " <td>NaN</td>\n",
1962
+ " <td>NaN</td>\n",
1963
+ " <td>NaN</td>\n",
1964
+ " <td>NaN</td>\n",
1965
+ " <td>NaN</td>\n",
1966
+ " <td>NaN</td>\n",
1967
+ " <td>NaN</td>\n",
1968
+ " <td>NaN</td>\n",
1969
+ " <td>NaN</td>\n",
1970
+ " <td>NaN</td>\n",
1971
+ " <td>NaN</td>\n",
1972
+ " <td>NaN</td>\n",
1973
+ " <td>NaN</td>\n",
1974
+ " <td>NaN</td>\n",
1975
+ " <td>NaN</td>\n",
1976
+ " </tr>\n",
1977
+ " <tr>\n",
1978
+ " <th>138688</th>\n",
1979
+ " <td>https://open.spotify.com/track/3jztoVZMBEaTX0b...</td>\n",
1980
+ " <td>Latin Urban</td>\n",
1981
+ " <td>Tímida</td>\n",
1982
+ " <td>NaN</td>\n",
1983
+ " <td>NaN</td>\n",
1984
+ " <td>NaN</td>\n",
1985
+ " <td>NaN</td>\n",
1986
+ " <td>NaN</td>\n",
1987
+ " <td>NaN</td>\n",
1988
+ " <td>NaN</td>\n",
1989
+ " <td>NaN</td>\n",
1990
+ " <td>NaN</td>\n",
1991
+ " <td>NaN</td>\n",
1992
+ " <td>NaN</td>\n",
1993
+ " <td>NaN</td>\n",
1994
+ " <td>NaN</td>\n",
1995
+ " <td>NaN</td>\n",
1996
+ " <td>NaN</td>\n",
1997
+ " <td>NaN</td>\n",
1998
+ " <td>NaN</td>\n",
1999
+ " <td>NaN</td>\n",
2000
+ " <td>NaN</td>\n",
2001
+ " <td>NaN</td>\n",
2002
+ " <td>NaN</td>\n",
2003
+ " <td>NaN</td>\n",
2004
+ " <td>NaN</td>\n",
2005
+ " <td>Latin Urban</td>\n",
2006
+ " <td>NaN</td>\n",
2007
+ " <td>NaN</td>\n",
2008
+ " <td>NaN</td>\n",
2009
+ " <td>NaN</td>\n",
2010
+ " <td>NaN</td>\n",
2011
+ " <td>NaN</td>\n",
2012
+ " <td>NaN</td>\n",
2013
+ " <td>NaN</td>\n",
2014
+ " <td>NaN</td>\n",
2015
+ " <td>NaN</td>\n",
2016
+ " <td>NaN</td>\n",
2017
+ " <td>NaN</td>\n",
2018
+ " <td>NaN</td>\n",
2019
+ " <td>NaN</td>\n",
2020
+ " <td>NaN</td>\n",
2021
+ " </tr>\n",
2022
+ " <tr>\n",
2023
+ " <th>138689</th>\n",
2024
+ " <td>https://open.spotify.com/track/3mUr9hl2VhqorCu...</td>\n",
2025
+ " <td>Latin Urban</td>\n",
2026
+ " <td>Romeo Y Julieta</td>\n",
2027
+ " <td>NaN</td>\n",
2028
+ " <td>NaN</td>\n",
2029
+ " <td>NaN</td>\n",
2030
+ " <td>NaN</td>\n",
2031
+ " <td>NaN</td>\n",
2032
+ " <td>NaN</td>\n",
2033
+ " <td>NaN</td>\n",
2034
+ " <td>NaN</td>\n",
2035
+ " <td>NaN</td>\n",
2036
+ " <td>NaN</td>\n",
2037
+ " <td>NaN</td>\n",
2038
+ " <td>NaN</td>\n",
2039
+ " <td>NaN</td>\n",
2040
+ " <td>NaN</td>\n",
2041
+ " <td>NaN</td>\n",
2042
+ " <td>NaN</td>\n",
2043
+ " <td>NaN</td>\n",
2044
+ " <td>NaN</td>\n",
2045
+ " <td>NaN</td>\n",
2046
+ " <td>NaN</td>\n",
2047
+ " <td>NaN</td>\n",
2048
+ " <td>NaN</td>\n",
2049
+ " <td>NaN</td>\n",
2050
+ " <td>Latin Urban</td>\n",
2051
+ " <td>NaN</td>\n",
2052
+ " <td>NaN</td>\n",
2053
+ " <td>NaN</td>\n",
2054
+ " <td>NaN</td>\n",
2055
+ " <td>NaN</td>\n",
2056
+ " <td>NaN</td>\n",
2057
+ " <td>NaN</td>\n",
2058
+ " <td>NaN</td>\n",
2059
+ " <td>NaN</td>\n",
2060
+ " <td>NaN</td>\n",
2061
+ " <td>NaN</td>\n",
2062
+ " <td>NaN</td>\n",
2063
+ " <td>NaN</td>\n",
2064
+ " <td>NaN</td>\n",
2065
+ " <td>NaN</td>\n",
2066
+ " </tr>\n",
2067
+ " </tbody>\n",
2068
+ "</table>\n",
2069
+ "<p>138690 rows × 42 columns</p>\n",
2070
+ "</div>"
2071
+ ]
2072
+ },
2073
+ "execution_count": 2,
2074
+ "metadata": {},
2075
+ "output_type": "execute_result"
2076
+ }
2077
+ ],
2078
+ "execution_count": 2
2079
+ },
2080
+ {
2081
+ "metadata": {},
2082
+ "cell_type": "code",
2083
+ "outputs": [],
2084
+ "execution_count": null,
2085
+ "source": "",
2086
+ "id": "a73b0a2127420c43"
2087
+ }
2088
+ ],
2089
+ "metadata": {
2090
+ "kernelspec": {
2091
+ "display_name": "Python 3",
2092
+ "language": "python",
2093
+ "name": "python3"
2094
+ },
2095
+ "language_info": {
2096
+ "codemirror_mode": {
2097
+ "name": "ipython",
2098
+ "version": 2
2099
+ },
2100
+ "file_extension": ".py",
2101
+ "mimetype": "text/x-python",
2102
+ "name": "python",
2103
+ "nbconvert_exporter": "python",
2104
+ "pygments_lexer": "ipython2",
2105
+ "version": "2.7.6"
2106
+ }
2107
+ },
2108
+ "nbformat": 4,
2109
+ "nbformat_minor": 5
2110
+ }
all_combine_code/genre_classification/test1_file.ipynb ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "metadata": {
5
+ "ExecuteTime": {
6
+ "end_time": "2026-03-30T08:57:26.406625300Z",
7
+ "start_time": "2026-03-30T08:57:24.333887600Z"
8
+ }
9
+ },
10
+ "cell_type": "code",
11
+ "source": [
12
+ "import pandas as pd\n",
13
+ "import numpy as np\n",
14
+ "import re\n",
15
+ "import ast\n",
16
+ "from sklearn.model_selection import train_test_split\n",
17
+ "from sklearn.preprocessing import StandardScaler, MultiLabelBinarizer\n",
18
+ "from sklearn.multioutput import MultiOutputClassifier\n",
19
+ "from xgboost import XGBClassifier\n",
20
+ "from sklearn.metrics import f1_score\n",
21
+ "\n",
22
+ "path = r\"C:\\Users\\VigneshSubramani\\Downloads\\model_ready_dataset_final.csv\"\n",
23
+ "df = pd.read_csv(path)\n",
24
+ "\n"
25
+ ],
26
+ "id": "89e4f464fdeba6a0",
27
+ "outputs": [],
28
+ "execution_count": 1
29
+ },
30
+ {
31
+ "metadata": {
32
+ "ExecuteTime": {
33
+ "end_time": "2026-03-30T08:57:32.746112300Z",
34
+ "start_time": "2026-03-30T08:57:32.668847500Z"
35
+ }
36
+ },
37
+ "cell_type": "code",
38
+ "source": [
39
+ "def parse_subgenres_fast(text):\n",
40
+ " if pd.isna(text) or text == \"\":\n",
41
+ " return []\n",
42
+ " text = str(text)\n",
43
+ " parts = text.split('---')\n",
44
+ " main_genre = parts[0].strip()\n",
45
+ " if len(parts) > 1:\n",
46
+ " sub = parts[1].strip()\n",
47
+ " sub = sub.strip('[]')\n",
48
+ " if sub:\n",
49
+ " subgenres = [s.strip().strip(\"'\").strip('\"') for s in sub.split(',')]\n",
50
+ " else:\n",
51
+ " subgenres = []\n",
52
+ " else:\n",
53
+ " subgenres = []\n",
54
+ " return [main_genre] + subgenres\n",
55
+ "\n",
56
+ "df['subgenres_list'] = df['genre_subgenre'].apply(parse_subgenres_fast)\n",
57
+ "\n"
58
+ ],
59
+ "id": "a0e5f938aaea7104",
60
+ "outputs": [],
61
+ "execution_count": 2
62
+ },
63
+ {
64
+ "metadata": {
65
+ "ExecuteTime": {
66
+ "end_time": "2026-03-30T08:57:56.751354200Z",
67
+ "start_time": "2026-03-30T08:57:56.682503800Z"
68
+ }
69
+ },
70
+ "cell_type": "code",
71
+ "source": "df['subgenres_list'][0]",
72
+ "id": "a361e82d41548901",
73
+ "outputs": [
74
+ {
75
+ "data": {
76
+ "text/plain": [
77
+ "['Rock', 'Noise Pop', 'Classic Alternative Rock', 'Indie Rock']"
78
+ ]
79
+ },
80
+ "execution_count": 3,
81
+ "metadata": {},
82
+ "output_type": "execute_result"
83
+ }
84
+ ],
85
+ "execution_count": 3
86
+ },
87
+ {
88
+ "metadata": {
89
+ "ExecuteTime": {
90
+ "end_time": "2026-03-30T08:58:43.550962900Z",
91
+ "start_time": "2026-03-30T08:58:43.487876100Z"
92
+ }
93
+ },
94
+ "cell_type": "code",
95
+ "source": [
96
+ "mlb = MultiLabelBinarizer()\n",
97
+ "y = mlb.fit_transform(df['subgenres_list'])\n",
98
+ "y"
99
+ ],
100
+ "id": "895aab114eb128a5",
101
+ "outputs": [
102
+ {
103
+ "data": {
104
+ "text/plain": [
105
+ "array([[0, 0, 0, ..., 0, 0, 0],\n",
106
+ " [0, 0, 0, ..., 0, 0, 0],\n",
107
+ " [0, 0, 0, ..., 0, 0, 0],\n",
108
+ " ...,\n",
109
+ " [0, 0, 0, ..., 0, 0, 0],\n",
110
+ " [0, 0, 0, ..., 1, 0, 0],\n",
111
+ " [0, 0, 0, ..., 0, 0, 0]], shape=(41935, 112))"
112
+ ]
113
+ },
114
+ "execution_count": 4,
115
+ "metadata": {},
116
+ "output_type": "execute_result"
117
+ }
118
+ ],
119
+ "execution_count": 4
120
+ },
121
+ {
122
+ "metadata": {
123
+ "ExecuteTime": {
124
+ "end_time": "2026-03-30T09:02:12.058921100Z",
125
+ "start_time": "2026-03-30T08:59:11.763576600Z"
126
+ }
127
+ },
128
+ "cell_type": "code",
129
+ "source": [
130
+ "drop_cols = ['_id', 'genre', 'genre_subgenre', 'subgenres_list', 'labels',\n",
131
+ " 'trimmed_audio_duration_sec', 'syllable_count', 'word_count',\n",
132
+ " 'sentiment_score', 'avg_word_length']\n",
133
+ "\n",
134
+ "feature_cols = [c for c in df.columns if c not in drop_cols]\n",
135
+ "X = df[feature_cols].copy()\n",
136
+ "X = X.select_dtypes(include=[np.number]).fillna(0)\n",
137
+ "\n",
138
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
139
+ "\n",
140
+ "scaler = StandardScaler()\n",
141
+ "X_train_scaled = scaler.fit_transform(X_train)\n",
142
+ "X_test_scaled = scaler.transform(X_test)\n",
143
+ "\n",
144
+ "model = MultiOutputClassifier(\n",
145
+ " XGBClassifier(\n",
146
+ " n_estimators=1500,\n",
147
+ " learning_rate=0.03,\n",
148
+ " max_depth=8,\n",
149
+ " subsample=0.85,\n",
150
+ " colsample_bytree=0.8,\n",
151
+ " random_state=42,\n",
152
+ " eval_metric='logloss'\n",
153
+ " ),\n",
154
+ " n_jobs=-1\n",
155
+ ")\n",
156
+ "\n",
157
+ "model.fit(X_train_scaled, y_train)\n",
158
+ "\n",
159
+ "def get_accuracy(X_scaled, y_true, name=\"\"):\n",
160
+ " y_pred = model.predict(X_scaled)\n",
161
+ " subset_acc = np.mean(np.all(y_pred == y_true, axis=1))\n",
162
+ " micro_f1 = f1_score(y_true, y_pred, average='micro')\n",
163
+ " print(f\"{name} Subset Accuracy: {subset_acc:.4f}\")\n",
164
+ " print(f\"{name} Micro F1 Score: {micro_f1:.4f}\")\n",
165
+ "\n",
166
+ "print(\"=== MODEL ACCURACY ===\")\n",
167
+ "get_accuracy(X_train_scaled, y_train, \"Train\")\n",
168
+ "get_accuracy(X_test_scaled, y_test, \"Test\")\n",
169
+ "\n",
170
+ "def manual_predict(row):\n",
171
+ " input_df = pd.DataFrame([row])\n",
172
+ " input_numeric = input_df.select_dtypes(include=[np.number]).fillna(0)\n",
173
+ "\n",
174
+ " for col in X.columns:\n",
175
+ " if col not in input_numeric.columns:\n",
176
+ " input_numeric[col] = 0\n",
177
+ " input_numeric = input_numeric[X.columns]\n",
178
+ " input_scaled = scaler.transform(input_numeric)\n",
179
+ " probas = model.predict_proba(input_scaled)\n",
180
+ " threshold = 0.20\n",
181
+ " pred_binary = np.zeros((1, y.shape[1]), dtype=int)\n",
182
+ " for i in range(len(probas)):\n",
183
+ " if probas[i][0][1] > threshold:\n",
184
+ " pred_binary[0, i] = 1\n",
185
+ " subgenres = mlb.inverse_transform(pred_binary)[0]\n",
186
+ " if len(subgenres) == 0:\n",
187
+ " return f\"None\"\n",
188
+ " return f\"{', '.join(sorted(subgenres))}\""
189
+ ],
190
+ "id": "8ff23ad3daa3533f",
191
+ "outputs": [
192
+ {
193
+ "name": "stdout",
194
+ "output_type": "stream",
195
+ "text": [
196
+ "=== MODEL ACCURACY ===\n",
197
+ "Train Subset Accuracy: 0.9961\n",
198
+ "Train Micro F1 Score: 0.9992\n",
199
+ "Test Subset Accuracy: 0.1909\n",
200
+ "Test Micro F1 Score: 0.5527\n"
201
+ ]
202
+ }
203
+ ],
204
+ "execution_count": 5
205
+ },
206
+ {
207
+ "metadata": {
208
+ "ExecuteTime": {
209
+ "end_time": "2026-03-30T09:03:23.140843Z",
210
+ "start_time": "2026-03-30T09:03:23.087981400Z"
211
+ }
212
+ },
213
+ "cell_type": "code",
214
+ "source": "df[[\"subgenres_list\",\"genre\"]].iloc[501,]",
215
+ "id": "d8574c668e09217f",
216
+ "outputs": [
217
+ {
218
+ "data": {
219
+ "text/plain": [
220
+ "subgenres_list [Rock, Classic Alternative Rock]\n",
221
+ "genre Rock\n",
222
+ "Name: 501, dtype: object"
223
+ ]
224
+ },
225
+ "execution_count": 8,
226
+ "metadata": {},
227
+ "output_type": "execute_result"
228
+ }
229
+ ],
230
+ "execution_count": 8
231
+ },
232
+ {
233
+ "metadata": {
234
+ "ExecuteTime": {
235
+ "end_time": "2026-03-30T09:03:31.570593500Z",
236
+ "start_time": "2026-03-30T09:03:31.234859400Z"
237
+ }
238
+ },
239
+ "cell_type": "code",
240
+ "source": [
241
+ "print(\"\\n=== FIRST ROW PREDICTION ===\")\n",
242
+ "print(manual_predict(X.iloc[501].to_dict()))\n",
243
+ "\n",
244
+ "print(\"\\n=== LAST ROW PREDICTION ===\")\n",
245
+ "print(manual_predict(X.iloc[-1].to_dict()))"
246
+ ],
247
+ "id": "d311cd5e9a4aa105",
248
+ "outputs": [
249
+ {
250
+ "name": "stdout",
251
+ "output_type": "stream",
252
+ "text": [
253
+ "\n",
254
+ "=== FIRST ROW PREDICTION ===\n",
255
+ "Modern Alternative Rock, Rock\n",
256
+ "\n",
257
+ "=== LAST ROW PREDICTION ===\n",
258
+ "Gothic Metal, Heavy Metal, Metal, Metalcore\n"
259
+ ]
260
+ }
261
+ ],
262
+ "execution_count": 9
263
+ },
264
+ {
265
+ "metadata": {
266
+ "ExecuteTime": {
267
+ "end_time": "2026-03-30T09:04:23.092797900Z",
268
+ "start_time": "2026-03-30T09:04:20.630659500Z"
269
+ }
270
+ },
271
+ "cell_type": "code",
272
+ "source": [
273
+ "import joblib\n",
274
+ "bundle = {\n",
275
+ " \"model\": model,\n",
276
+ " \"scaler\": scaler,\n",
277
+ " \"mlb\": mlb,\n",
278
+ " \"feature_cols\": X.columns.tolist()\n",
279
+ "}\n",
280
+ "joblib.dump(bundle, \"genre_model1.pkl\")\n",
281
+ "print(\"Model saved successfully!\")"
282
+ ],
283
+ "id": "58b9f6372caf42f",
284
+ "outputs": [
285
+ {
286
+ "name": "stdout",
287
+ "output_type": "stream",
288
+ "text": [
289
+ "Model saved successfully!\n"
290
+ ]
291
+ }
292
+ ],
293
+ "execution_count": 10
294
+ },
295
+ {
296
+ "metadata": {},
297
+ "cell_type": "code",
298
+ "outputs": [],
299
+ "execution_count": null,
300
+ "source": "",
301
+ "id": "c3b93bc28cd98983"
302
+ }
303
+ ],
304
+ "metadata": {
305
+ "kernelspec": {
306
+ "display_name": "Python 3",
307
+ "language": "python",
308
+ "name": "python3"
309
+ },
310
+ "language_info": {
311
+ "codemirror_mode": {
312
+ "name": "ipython",
313
+ "version": 2
314
+ },
315
+ "file_extension": ".py",
316
+ "mimetype": "text/x-python",
317
+ "name": "python",
318
+ "nbconvert_exporter": "python",
319
+ "pygments_lexer": "ipython2",
320
+ "version": "2.7.6"
321
+ }
322
+ },
323
+ "nbformat": 4,
324
+ "nbformat_minor": 5
325
+ }
all_combine_code/genreclassification/.env ADDED
@@ -0,0 +1 @@
 
 
1
+ VIBERATE_API=rot6LA7vWzUCdb2ZxmjOQSiUYyPuXcZI
all_combine_code/genreclassification/Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copy requirements.txt and install dependencies
6
+ COPY requirements.txt .
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # Copy the entire project
10
+ COPY . .
11
+
12
+ # Ensure the songs_data directory exists and has write permissions
13
+ RUN mkdir -p songs_data && chmod -R 755 songs_data
14
+
15
+ # Set environment variable for unbuffered output
16
+ ENV PYTHONUNBUFFERED=1
17
+
18
+ # Expose port
19
+ EXPOSE 5003
20
+
21
+ # Command to run the app
22
+ CMD ["uvicorn", "main_app:app", "--host", "0.0.0.0", "--port", "5003"]
all_combine_code/genreclassification/chartmetric_summary_generator3.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, re, json
2
+ import pandas as pd
3
+ import numpy as np
4
+ import difflib
5
+
6
+ from viberate_api_report_generator2 import (
7
+ potential_fanbase_count,
8
+ similarity_songs_report,
9
+ )
10
+
11
+
12
+ def chartmetric_manual_generator(task_id: str, session_dir: str):
13
+ songs_list, df = similarity_songs_report(task_id)
14
+ if df is None or df.empty:
15
+ return None, None
16
+
17
+ excel_path = potential_fanbase_count(songs_list, session_dir)
18
+
19
+ if not isinstance(excel_path, str) or not excel_path.endswith(".xlsx"):
20
+ return None, None
21
+
22
+ if os.path.exists(excel_path) and os.path.getsize(excel_path) > 0:
23
+ try:
24
+ pd.ExcelFile(excel_path)
25
+ return excel_path, df
26
+ except Exception:
27
+ return None, None
28
+
29
+ return None, None
30
+
31
+
32
+ def final_score_calculation(task_id: str, session_dir: str) -> int | None:
33
+ workbook_path, similarity_dataframe = chartmetric_manual_generator(task_id, session_dir)
34
+ if workbook_path is None:
35
+ return None
36
+
37
+ ALIASES = {
38
+ "spotify_streams": ["spotify streams", "spotify stream", "spotify s"],
39
+ "youtube_views": ["youtube views", "youtube view", "youtube video"],
40
+ "soundcloud_streams": ["soundcloud streams", "soundcloud plays", "sound"],
41
+ }
42
+ RPF_LOW, RPF_MID, RPF_HIGH = 4.0, 2.5, 1.5
43
+ USE_DECAY = True
44
+ HALF_LIFE_YRS = 5
45
+ LIFT_WEIGHTS = {"youtube_views": 0.60, "soundcloud_streams": 0.40}
46
+ LAMBDA = 0.10
47
+ LIFT_MIN_MAX = (0.7, 1.5)
48
+ OVERLAP_BOUNDS = (0.30, 0.60)
49
+ OVERLAP_PENALTY_K = 2.5
50
+ RECENCY_EXPONENT = 1.0
51
+ AUDIENCE_SMOOTHING_LAMBDA = 0.15
52
+
53
+ # ── helpers ─────────────────────────────────────────────────────────────
54
+ def _norm(s):
55
+ return re.sub(r"\s+", " ", str(s).replace("\n", " ").strip().lower())
56
+
57
+ def resolve_col(cols, aliases):
58
+ norm_cols = {c: _norm(c) for c in cols}
59
+ alias_norms = [_norm(a) for a in aliases]
60
+ for c, nc in norm_cols.items():
61
+ for a in alias_norms:
62
+ if nc == a or nc.startswith(a) or a in nc:
63
+ return c
64
+ best_col, best_ratio = None, 0.0
65
+ for c, nc in norm_cols.items():
66
+ for a in alias_norms:
67
+ r = difflib.SequenceMatcher(None, nc, a).ratio()
68
+ if r > best_ratio:
69
+ best_ratio, best_col = r, c
70
+ return best_col if best_ratio >= 0.60 else None
71
+
72
+ def last_non_null(series):
73
+ s = pd.to_numeric(series, errors="coerce").dropna()
74
+ return s.iloc[-1] if not s.empty else np.nan
75
+
76
+ def robust_z(series):
77
+ s = pd.to_numeric(series, errors="coerce")
78
+ x = s.dropna()
79
+ if len(x) < 3:
80
+ return pd.Series(0.0, index=s.index)
81
+ med = x.median()
82
+ mad = (x - med).abs().median()
83
+ if mad == 0 or np.isnan(mad):
84
+ return pd.Series(0.0, index=s.index)
85
+ return (0.6745 * (s - med) / mad).clip(-5, 5).fillna(0.0)
86
+
87
+ def simpson_overlap(weights, bounds=(0.30, 0.60)):
88
+ w = pd.to_numeric(weights, errors="coerce").fillna(0.0).values
89
+ if w.sum() <= 0:
90
+ return float(np.mean(bounds))
91
+ w = w / w.sum()
92
+ K = len(w)
93
+ if K <= 1:
94
+ return float(bounds[1])
95
+ C = float((w ** 2).sum())
96
+ base = 1.0 / K
97
+ low, high = bounds
98
+ return float(np.clip(low + (high - low) * ((C - base) / (1 - base)), low, high))
99
+
100
+ def ensure_weights(sel_df, use_decay=True, half_life=5):
101
+ sel = sel_df.copy()
102
+ if "release_date" in sel.columns:
103
+ sel["release_date"] = (
104
+ pd.to_datetime(sel["release_date"], errors="coerce").dt.tz_localize(None)
105
+ )
106
+ age_years = (pd.Timestamp.today().normalize() - sel["release_date"]).dt.days / 365.25
107
+ if use_decay and sel["release_date"].notna().any():
108
+ sel["recency_decay"] = (
109
+ np.exp(-0.693 * (age_years.fillna(age_years.median()) / half_life))
110
+ ** RECENCY_EXPONENT
111
+ )
112
+ else:
113
+ sel["recency_decay"] = 1.0
114
+ else:
115
+ sel["recency_decay"] = 1.0
116
+
117
+ if "raw_weight" not in sel.columns:
118
+ sel["raw_weight"] = sel["final_boosted_score"] * sel["recency_decay"]
119
+
120
+ tot = sel["raw_weight"].sum()
121
+ sel["weight_norm"] = sel["raw_weight"] / tot if tot > 0 else 1.0 / len(sel)
122
+ sel = sel.sort_values("weight_norm", ascending=False)
123
+ sel["cum_weight"] = sel["weight_norm"].cumsum()
124
+ return sel
125
+
126
+ all_tracks = pd.read_excel(workbook_path, sheet_name=None)
127
+ rows = []
128
+ for sheet_name, df in all_tracks.items():
129
+ df.columns = (
130
+ df.columns.astype(str)
131
+ .str.replace("\n", " ", regex=True)
132
+ .str.replace(r"\s+", " ", regex=True)
133
+ .str.strip()
134
+ )
135
+ col_spotify = resolve_col(df.columns, ALIASES["spotify_streams"])
136
+ col_youtube = resolve_col(df.columns, ALIASES["youtube_views"])
137
+ col_soundcloud = resolve_col(df.columns, ALIASES["soundcloud_streams"])
138
+
139
+ spotify_streams = last_non_null(df[col_spotify]) if col_spotify else np.nan
140
+ youtube_views = last_non_null(df[col_youtube]) if col_youtube else np.nan
141
+ soundcloud_streams = last_non_null(df[col_soundcloud]) if col_soundcloud else np.nan
142
+
143
+ if pd.notna(spotify_streams):
144
+ listeners_low = spotify_streams / RPF_LOW
145
+ listeners_mid = spotify_streams / RPF_MID
146
+ listeners_high = spotify_streams / RPF_HIGH
147
+ else:
148
+ listeners_low = listeners_mid = listeners_high = np.nan
149
+
150
+ rows.append({
151
+ "track": sheet_name,
152
+ "spotify_streams": spotify_streams,
153
+ "listeners_low": listeners_low,
154
+ "listeners_mid": listeners_mid,
155
+ "listeners_high": listeners_high,
156
+ "youtube_views": youtube_views,
157
+ "soundcloud_streams": soundcloud_streams,
158
+ })
159
+
160
+ summary_df = pd.DataFrame(rows)
161
+
162
+ # ── part 3 – weighted score ──────────────────────────────────────────────
163
+ selected_tracks = similarity_dataframe.copy()
164
+ selected_tracks.columns = (
165
+ selected_tracks.columns.astype(str)
166
+ .str.replace("\n", " ", regex=True)
167
+ .str.replace(r"\s+", " ", regex=True)
168
+ .str.strip()
169
+ )
170
+
171
+ sel = ensure_weights(selected_tracks, use_decay=USE_DECAY, half_life=HALF_LIFE_YRS)
172
+ sel = sel.sort_values("weight_norm", ascending=False).reset_index(drop=True)
173
+ sel["rank"] = np.arange(1, len(sel) + 1)
174
+
175
+ p2 = summary_df.copy()
176
+ p2["rank"] = p2["track"].str.extract(r"^(\d+)").astype(float).astype("Int64")
177
+ if p2["rank"].isna().any():
178
+ seq = np.arange(1, len(p2) + 1)
179
+ p2.loc[p2["rank"].isna(), "rank"] = seq[p2["rank"].isna()]
180
+ p2["rank"] = p2["rank"].astype(int)
181
+
182
+ cols_keep_sel = [
183
+ "rank", "artist_names", "name", "album", "final_boosted_score",
184
+ "release_date", "weight_norm", "cum_weight", "track_url",
185
+ ]
186
+ merged = pd.merge(
187
+ sel[cols_keep_sel],
188
+ p2[["rank", "track",
189
+ "spotify_streams", "listeners_low", "listeners_mid", "listeners_high",
190
+ "youtube_views", "soundcloud_streams"]],
191
+ on="rank", how="inner",
192
+ )
193
+
194
+ for col in ["youtube_views", "soundcloud_streams"]:
195
+ merged[f"z_{col}"] = robust_z(merged[col])
196
+
197
+ def weighted_z(row):
198
+ num = den = 0.0
199
+ for col, w in LIFT_WEIGHTS.items():
200
+ z = row.get(f"z_{col}", 0.0)
201
+ if pd.notna(z):
202
+ num += w * z
203
+ den += w
204
+ return num / den if den > 0 else 0.0
205
+
206
+ merged["z_weighted"] = merged.apply(weighted_z, axis=1)
207
+ lo, hi = LIFT_MIN_MAX
208
+ merged["lift"] = (1.0 + LAMBDA * merged["z_weighted"]).clip(lower=lo, upper=hi)
209
+
210
+ def safe_mul(a, b, c):
211
+ if pd.isna(b): b = 0.0
212
+ return float(a) * float(b) * float(c)
213
+
214
+ merged["contrib_low"] = merged.apply(lambda r: safe_mul(r["weight_norm"], r["listeners_low"], r["lift"]), axis=1)
215
+ merged["contrib_mid"] = merged.apply(lambda r: safe_mul(r["weight_norm"], r["listeners_mid"], r["lift"]), axis=1)
216
+ merged["contrib_high"] = merged.apply(lambda r: safe_mul(r["weight_norm"], r["listeners_high"], r["lift"]), axis=1)
217
+
218
+ raw = {
219
+ "low": merged["contrib_low"].sum(),
220
+ "mid": merged["contrib_mid"].sum(),
221
+ "high": merged["contrib_high"].sum(),
222
+ }
223
+
224
+ analog_totals = (merged["listeners_mid"] * merged["lift"]).fillna(0)
225
+ AUDIENCE_BASELINE = int(np.median(analog_totals) * 20)
226
+
227
+ overlap = simpson_overlap(merged["weight_norm"], OVERLAP_BOUNDS)
228
+ overlap_penalty = min(1.0, OVERLAP_PENALTY_K * overlap)
229
+ final = {k: v * (1.0 - overlap_penalty) for k, v in raw.items()}
230
+
231
+ pred_audience = (
232
+ (1 - AUDIENCE_SMOOTHING_LAMBDA) * final["mid"] +
233
+ AUDIENCE_SMOOTHING_LAMBDA * AUDIENCE_BASELINE
234
+ )
235
+
236
+ summary_df.to_csv(os.path.join(session_dir, "part2_chartmetric_summary.csv"), index=False)
237
+
238
+ detail_cols = [
239
+ "rank", "artist_names", "name", "track", "weight_norm",
240
+ "listeners_low", "listeners_mid", "listeners_high",
241
+ "youtube_views", "soundcloud_streams",
242
+ "z_youtube_views", "z_soundcloud_streams",
243
+ "z_weighted", "lift", "contrib_low", "contrib_mid", "contrib_high",
244
+ ]
245
+ detail_df = merged[detail_cols].sort_values("rank")
246
+ for col in ["listeners_low", "listeners_mid", "listeners_high",
247
+ "contrib_low", "contrib_mid", "contrib_high"]:
248
+ detail_df[col] = detail_df[col].round(0).astype("Int64")
249
+ detail_df["weight_norm"] = detail_df["weight_norm"].round(4)
250
+ detail_df["lift"] = detail_df["lift"].round(3)
251
+ detail_df["z_weighted"] = detail_df["z_weighted"].round(3)
252
+ detail_df.to_csv(os.path.join(session_dir, "part3_contributions_detail.csv"), index=False)
253
+
254
+ summary_out = {
255
+ "overlap_weights_based": float(round(overlap, 6)),
256
+ "totals_raw": raw,
257
+ "totals_final": final,
258
+ "AUDIENCE_BASELINE": AUDIENCE_BASELINE,
259
+ "pred_audience_smooth": int(pred_audience),
260
+ }
261
+ with open(os.path.join(session_dir, "part3_summary.json"), "w") as f:
262
+ json.dump(summary_out, f, indent=2)
263
+
264
+ return int(pred_audience)
all_combine_code/genreclassification/main_app.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gc
2
+ import logging
3
+ import os
4
+ import shutil
5
+ import tempfile
6
+ import time
7
+
8
+ from fastapi import FastAPI, Request
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+
11
+ from chartmetric_summary_generator3 import final_score_calculation
12
+
13
+ app = FastAPI()
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"],
17
+ allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
23
+ SESSIONS_ROOT = os.path.join(BASE_DIR, "sessions") # all per-request dirs live here
24
+ os.makedirs(SESSIONS_ROOT, exist_ok=True)
25
+
26
+ logging.basicConfig(
27
+ level=logging.INFO,
28
+ format="%(asctime)s - %(levelname)s - %(message)s",
29
+ )
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def _cleanup_session(session_dir: str) -> None:
34
+ """Remove the entire per-request temp directory, tolerating locked files."""
35
+ if not os.path.exists(session_dir):
36
+ return
37
+ try:
38
+ gc.collect()
39
+ time.sleep(0.1)
40
+ shutil.rmtree(session_dir, ignore_errors=True)
41
+ logger.info(f"Cleaned up session dir: {session_dir}")
42
+ except Exception as e:
43
+ logger.warning(f"Could not fully clean up {session_dir}: {e}")
44
+
45
+
46
+ @app.get("/")
47
+ def read_root():
48
+ return {"Hello": "200 ok"}
49
+
50
+
51
+ @app.post("/fanbase_score")
52
+ async def fanbase_score(request: Request):
53
+ session_dir = tempfile.mkdtemp(dir=SESSIONS_ROOT)
54
+ logger.info(f"Created session dir: {session_dir}")
55
+
56
+ try:
57
+ data = await request.json()
58
+ task_id = data["track_id"].strip()
59
+ logger.info(f"Processing start – task_id={task_id} session={session_dir}")
60
+
61
+ fanbase_score_count = final_score_calculation(task_id, session_dir)
62
+ logger.info(f"Fan Base Count: {fanbase_score_count}")
63
+
64
+ return {"fanbase_count": fanbase_score_count}
65
+
66
+ except Exception as e:
67
+ logger.error(f"Request failed: {e}")
68
+ return {"error": str(e)}
69
+
70
+ finally:
71
+ _cleanup_session(session_dir)
all_combine_code/genreclassification/requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.119.0
2
+ uvicorn==0.37.0
3
+ pandas==2.3.3
4
+ XlsxWriter==3.2.9
5
+ aiohttp==3.12.15
6
+ python-dotenv== 1.1.1
7
+ openpyxl==3.1.5
8
+ requests==2.32.5
all_combine_code/genreclassification/songs_report_generator1.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+ warnings.filterwarnings("ignore")
3
+
4
+ import requests
5
+ import pandas as pd
6
+
7
+
8
+ def fetch_similar_tracks_df(task_id):
9
+ """
10
+ Fetch similar tracks for a given task_id and return as a pandas DataFrame.
11
+ """
12
+ url = f"https://aimusic-reprocessing.kovaionplay.com/api/v1/progress/task/{task_id}"
13
+ headers = {
14
+ "x-api-key": "$2b$12$VXkJYUlA2ZMxX25dZ47g7uNVfERnghpvd690vgpy0wVHef/KSDMPq",
15
+ "x-user-id": "6819fa6f63df2ba55d256ee1"
16
+ }
17
+ response = requests.get(url, headers=headers)
18
+
19
+ if response.status_code == 404:
20
+ url = f"https://aimusic-backend-stage.kovaionplay.com/api/v1/progress/task/{task_id}"
21
+ response = requests.get(url, headers=headers)
22
+
23
+ if response.status_code != 200:
24
+ raise Exception(f"Error: Received status code {response.status_code}, {response.text}")
25
+
26
+ data = response.json()
27
+ similar_tracks = data.get("similar_tracks", [])
28
+
29
+ if not similar_tracks:
30
+ return pd.DataFrame()
31
+
32
+ rows = []
33
+ for track in similar_tracks:
34
+ track_info = track.get("track_info", {})
35
+ similarity_scores = track.get("similarity_scores", {})
36
+ artist_info_list = track.get("artist_info", [])
37
+ row = {
38
+ "final_boosted_score": track.get("final_boosted_score", "-"),
39
+ "album": track_info.get("album", "-"),
40
+ "track_url": track_info.get("track_url", "-"),
41
+ "name": track_info.get("name", "-"),
42
+ "release_date": track_info.get("release_date", "-"),
43
+ "top_song": track_info.get("top_song", "-"),
44
+ "artist_names": "|".join(artist.get("artist_name", "-") for artist in artist_info_list),
45
+ "chord_progression_similarity": similarity_scores.get("chord_progression_similarity", "-"),
46
+ "general_audio_similarity": similarity_scores.get("general_audio_similarity", "-"),
47
+ "lyrics_similarity": similarity_scores.get("lyrics_similarity", "-"),
48
+ "timber_similarity": similarity_scores.get("timber_similarity", "-"),
49
+ "vocal_melody_similarity": similarity_scores.get("vocal_melody_similarity", "-"),
50
+ "rhythm_similarity": similarity_scores.get("rhythm_similarity", "-"),
51
+ "harmonic_similarity": similarity_scores.get("harmonic_similarity", "-"),
52
+ }
53
+ rows.append(row)
54
+
55
+ df = pd.DataFrame(rows)
56
+ return df
57
+
58
+ # if __name__ == "__main__":
59
+ # task_id = "69afc81760ed587a34dfc57a"
60
+ # df = fetch_similar_tracks_df(task_id)
61
+ # print(df)
62
+ #
63
+ # print(df.columns)
64
+ #
65
+ #
66
+ # print(df["name"].tolist())
all_combine_code/genreclassification/viberate_api_report_generator2.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, re, logging
2
+ import pandas as pd
3
+ import numpy as np
4
+ import aiohttp
5
+ import asyncio
6
+ from datetime import datetime, timedelta
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from dotenv import load_dotenv
9
+
10
+ load_dotenv()
11
+
12
+ pd.set_option('display.max_columns', None)
13
+
14
+ api_token = os.getenv("VIBERATE_API")
15
+ date_to = datetime.today().date()
16
+ date_from = date_to - timedelta(days=365)
17
+
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(__name__)
20
+
21
+ from songs_report_generator1 import fetch_similar_tracks_df
22
+
23
+
24
+ def similarity_songs_report(task_id):
25
+ df = fetch_similar_tracks_df(task_id)
26
+ if df.empty:
27
+ return {"message": "No similar tracks found"}, None
28
+ return df["name"].tolist(), df
29
+
30
+
31
+ class ViberateAPI:
32
+ """
33
+ All file I/O is scoped to `session_dir` so concurrent requests
34
+ never collide on the filesystem.
35
+ """
36
+
37
+ def __init__(self, session_dir: str):
38
+ self.uuid_cache = {}
39
+ self.base_folder = os.path.join(session_dir, "songs_data")
40
+ os.makedirs(self.base_folder, exist_ok=True)
41
+
42
+ async def get_uuid_from_song_name(self, session, song_name):
43
+ if song_name in self.uuid_cache:
44
+ return self.uuid_cache[song_name]
45
+ try:
46
+ url = "https://data.viberate.com/api/v1/track/search"
47
+ params = {"q": song_name, "limit": 5, "offset": 0}
48
+ headers = {"Access-Key": api_token}
49
+ async with session.get(url, params=params, headers=headers) as response:
50
+ if response.status == 200:
51
+ data = await response.json()
52
+ if not data or "data" not in data or not isinstance(data["data"], list) or not data["data"]:
53
+ return {"error": "Empty or invalid response"}
54
+ first_track = data["data"][0]
55
+ uuid = first_track["uuid"]
56
+ artist_name = first_track.get("artist_name", "Unknown Artist")
57
+ self.uuid_cache[song_name] = {"uuid": uuid, "name": artist_name}
58
+ return {"uuid": uuid, "name": artist_name}
59
+ return {"error": "Invalid response"}
60
+ except Exception as e:
61
+ return {"error": str(e)}
62
+
63
+ def is_valid_uuid(self, uuid_string):
64
+ uuid_pattern = re.compile(
65
+ r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
66
+ )
67
+ return uuid_string if uuid_pattern.fullmatch(uuid_string) else "UUID NOT VALID"
68
+
69
+ async def fetch_platform_data(self, session, uuid, artist_name, platform):
70
+ try:
71
+ url_map = {
72
+ "spotify": "https://data.viberate.com/api/v1/track/{uuid}/spotify/streams-historical",
73
+ "soundcloud": "https://data.viberate.com/api/v1/track/{uuid}/soundcloud/plays-historical",
74
+ "youtube": "https://data.viberate.com/api/v1/track/{uuid}/youtube/views-historical",
75
+ "beatport": "https://data.viberate.com/api/v1/track/{uuid}/beatport/ranks-historical",
76
+ }
77
+ column_name_map = {
78
+ "spotify": "spotify_streams",
79
+ "soundcloud": "soundcloud_streams",
80
+ "youtube": "youtube_streams",
81
+ "beatport": "beatport_streams",
82
+ }
83
+ column_name = column_name_map.get(platform, f"{platform}_streams")
84
+
85
+ if self.is_valid_uuid(uuid) == "UUID NOT VALID":
86
+ return {"error": "UUID NOT VALID"}, artist_name
87
+
88
+ url = url_map[platform].format(uuid=uuid)
89
+ headers = {"Accept": "application/json", "Access-Key": api_token}
90
+ params = {"date-from": date_from.isoformat(), "date-to": date_to.isoformat()}
91
+
92
+ async with session.get(url, headers=headers, params=params) as response:
93
+ if response.status == 200:
94
+ data = await response.json()
95
+ if data and "data" in data and "data" in data["data"]:
96
+ streams_data = data["data"]["data"]
97
+ df = pd.DataFrame(list(streams_data.items()), columns=["date", column_name])
98
+ df["date"] = pd.to_datetime(df["date"])
99
+ return df, artist_name
100
+ return pd.DataFrame(columns=["date", column_name]), artist_name
101
+ return {"error": "Invalid response"}, artist_name
102
+ except Exception as e:
103
+ return {"error": str(e)}, artist_name
104
+
105
+ async def final_result_from_song(self, song_name):
106
+ try:
107
+ async with aiohttp.ClientSession() as session:
108
+ uuid_data = await self.get_uuid_from_song_name(session, song_name)
109
+ if "error" in uuid_data:
110
+ return {"error": uuid_data["error"]}
111
+
112
+ uuid = uuid_data["uuid"]
113
+ artist_name = uuid_data["name"]
114
+ platforms = ["spotify", "youtube", "soundcloud"]
115
+ tasks = [
116
+ self.fetch_platform_data(session, uuid, artist_name, p)
117
+ for p in platforms
118
+ ]
119
+ results = await asyncio.gather(*tasks, return_exceptions=True)
120
+
121
+ final_output = None
122
+ for result, _ in results:
123
+ if isinstance(result, dict) and "error" in result:
124
+ return {"error": result["error"]}
125
+ if isinstance(result, pd.DataFrame):
126
+ if final_output is None:
127
+ final_output = result
128
+ else:
129
+ final_output = final_output.merge(result, on='date', how='outer')
130
+
131
+ if final_output is None:
132
+ return {"error": "No data retrieved"}
133
+
134
+ return final_output.sort_values(by='date').reset_index(drop=True)
135
+ except Exception as e:
136
+ return {"error": str(e)}
137
+
138
+ def process_song(self, song_name):
139
+ try:
140
+ loop = asyncio.new_event_loop()
141
+ asyncio.set_event_loop(loop)
142
+ df = loop.run_until_complete(self.final_result_from_song(song_name))
143
+ loop.close()
144
+
145
+ if isinstance(df, dict) and "error" in df:
146
+ return song_name, df
147
+
148
+ safe_song_name = re.sub(r'[\\/*?:"<>|]', "", song_name)
149
+ csv_path = os.path.join(self.base_folder, f"{safe_song_name}.csv")
150
+ df.to_csv(csv_path, index=False)
151
+ return song_name, {"success": csv_path}
152
+ except Exception as e:
153
+ return song_name, {"error": str(e)}
154
+
155
+
156
+ def combine_csv_to_excel(songs_list: list, session_dir: str) -> str:
157
+ """
158
+ Downloads per-song CSVs into `session_dir/songs_data/` and writes
159
+ `session_dir/songs_data_combined.xlsx`. Returns the xlsx path on
160
+ success or an error string.
161
+ """
162
+ try:
163
+ api = ViberateAPI(session_dir=session_dir)
164
+
165
+ if not songs_list:
166
+ return "No songs to process"
167
+
168
+ results = list(
169
+ ThreadPoolExecutor(max_workers=5).map(api.process_song, songs_list)
170
+ )
171
+
172
+ errors = [
173
+ f"Error for {song}: {res['error']}"
174
+ for song, res in results if "error" in res
175
+ ]
176
+ if errors:
177
+ logger.error("\n".join(errors))
178
+
179
+ successes = [res["success"] for _, res in results if "success" in res]
180
+ if not successes:
181
+ return "No CSV files generated"
182
+
183
+ excel_path = os.path.join(session_dir, "songs_data_combined.xlsx")
184
+ with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
185
+ for csv_file in successes:
186
+ df = pd.read_csv(csv_file, encoding='utf-8')
187
+ if df.empty:
188
+ continue
189
+ sheet_name = os.path.splitext(os.path.basename(csv_file))[0][:31]
190
+ df.to_excel(writer, sheet_name=sheet_name, index=False)
191
+
192
+ return excel_path # ← return path instead of a bare string
193
+
194
+ except Exception as e:
195
+ logger.error(f"combine_csv_to_excel failed: {e}")
196
+ return f"Process failed: {str(e)}"
197
+
198
+
199
+ def potential_fanbase_count(songs_list: list, session_dir: str):
200
+ """Returns the xlsx path on success or an error string."""
201
+ try:
202
+ os.makedirs(session_dir, exist_ok=True)
203
+ return combine_csv_to_excel(songs_list, session_dir)
204
+ except Exception as e:
205
+ return f"Process failed: {str(e)}"
all_combine_code/local_model_to_s3/bucket_inside_file_delete.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import boto3
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY")
8
+ AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_KEY")
9
+ AWS_REGION = os.getenv("AWS_REGION")
10
+
11
+ BUCKET_NAME = "delvant-ai-all-models"
12
+
13
+ s3 = boto3.client(
14
+ "s3",
15
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
16
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
17
+ region_name=AWS_REGION
18
+ )
19
+
20
+ # List and delete all objects
21
+ def delete_all_objects(bucket):
22
+ paginator = s3.get_paginator("list_objects_v2")
23
+ for page in paginator.paginate(Bucket=bucket):
24
+ if "Contents" in page:
25
+ for obj in page["Contents"]:
26
+ print(f"Deleting {obj['Key']}")
27
+ s3.delete_object(Bucket=bucket, Key=obj["Key"])
28
+ print("✅ All objects deleted")
29
+
30
+ delete_all_objects(BUCKET_NAME)
all_combine_code/local_model_to_s3/local_model_to_s3_bucket.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ load_dotenv()
3
+ import os
4
+ import boto3
5
+
6
+
7
+ AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY")
8
+ AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_KEY")
9
+ AWS_REGION = os.getenv("AWS_REGION")
10
+
11
+ s3 = boto3.client(
12
+ "s3",
13
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
14
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
15
+ region_name=AWS_REGION
16
+ )
17
+
18
+ folder = r"C:\Users\VigneshSubramani\PycharmProjects\delveant-ai-crm\all-MiniLM-L6-v2.pt"
19
+ bucket = "delvant-ai-all-models"
20
+
21
+ for root, dirs, files in os.walk(folder):
22
+ for file in files:
23
+ full_path = os.path.join(root, file)
24
+ s3_path = os.path.relpath(full_path, folder)
25
+
26
+ s3.upload_file(full_path, bucket, s3_path)
27
+
28
+ print("Upload complete 🚀")
all_combine_code/memory_/__init__.py ADDED
File without changes
all_combine_code/memory_/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (176 Bytes). View file
 
all_combine_code/memory_/__pycache__/chat_history_handle.cpython-313.pyc ADDED
Binary file (9.31 kB). View file
 
all_combine_code/memory_/__pycache__/prompt_agents.cpython-313.pyc ADDED
Binary file (4.28 kB). View file
 
all_combine_code/memory_/chat_history_handle.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os,json,re
2
+ import tempfile
3
+ import shutil
4
+ from uuid import uuid4
5
+ from typing import Dict, List
6
+ import boto3
7
+ import tiktoken
8
+ from datetime import datetime
9
+ from dotenv import load_dotenv
10
+
11
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
12
+ from models_.delveant_llm_model_.multi_agent_llm import ChatSynthesizer
13
+ from .prompt_agents import chat_history
14
+
15
+ load_dotenv()
16
+
17
+ AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY")
18
+ AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_KEY")
19
+ AWS_REGION = os.getenv("AWS_REGION")
20
+
21
+ S3_BUCKET = os.getenv("AI_CHAT_HISTORY_S3")
22
+ S3_BASE_PREFIX = "long_term_memory"
23
+
24
+ MAX_HISTORY = 25
25
+ MAX_FOLDERS = int(os.getenv("HISTORY_MAX_FOLDER_COUNT"))
26
+
27
+ ENCODING = tiktoken.get_encoding("o200k_base")
28
+
29
+ s3 = boto3.client(
30
+ "s3",
31
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
32
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
33
+ region_name=AWS_REGION
34
+ )
35
+
36
+ user_sessions: Dict[str, List] = {}
37
+
38
+
39
+
40
+ def truncate_to_tokens(text: str, max_tokens: int = 50) -> str:
41
+ if not text:
42
+ return ""
43
+ if not isinstance(text, str):
44
+ text = json.dumps(text, ensure_ascii=False)
45
+ tokens = ENCODING.encode(text)
46
+ return ENCODING.decode(tokens[:max_tokens])
47
+
48
+
49
+ async def llm(prompt):
50
+ llm = ChatSynthesizer()
51
+ return await llm.chat_synthesizer(prompt)
52
+
53
+
54
+ def messages_s3_key(user_id: str):
55
+ return f"{S3_BASE_PREFIX}/{user_id}/messages.json"
56
+
57
+
58
+ def new_session_id():
59
+ return str(uuid4())
60
+
61
+
62
+ def get_user_messages(user_id: str):
63
+ if user_id not in user_sessions:
64
+ user_sessions[user_id] = [SystemMessage(content="You are a helpful AI assistant.")]
65
+ load_messages_from_s3(user_id)
66
+ return user_sessions[user_id]
67
+
68
+
69
+ def trim_history(messages):
70
+ system_msg = messages[0]
71
+ history = messages[-MAX_HISTORY:]
72
+ return [system_msg] + history
73
+
74
+
75
+ def save_messages_to_s3(user_id: str):
76
+ temp = tempfile.mkdtemp()
77
+ path = os.path.join(temp, "messages.json")
78
+
79
+ user_sessions[user_id] = trim_history(user_sessions[user_id])
80
+
81
+ with open(path, "w") as f:
82
+ json.dump(
83
+ [{"type": m.__class__.__name__, "content": m.content} for m in user_sessions[user_id]],
84
+ f
85
+ )
86
+
87
+ s3.upload_file(path, S3_BUCKET, messages_s3_key(user_id))
88
+ shutil.rmtree(temp)
89
+
90
+
91
+ def load_messages_from_s3(user_id: str):
92
+ try:
93
+ temp = tempfile.mkdtemp()
94
+ path = os.path.join(temp, "messages.json")
95
+
96
+ s3.download_file(S3_BUCKET, messages_s3_key(user_id), path)
97
+
98
+ with open(path) as f:
99
+ data = json.load(f)
100
+
101
+ for m in data[1:]:
102
+ if m["type"] == "HumanMessage":
103
+ user_sessions[user_id].append(HumanMessage(content=m["content"]))
104
+ elif m["type"] == "AIMessage":
105
+ user_sessions[user_id].append(AIMessage(content=m["content"]))
106
+
107
+ shutil.rmtree(temp)
108
+ except:
109
+ pass
110
+
111
+
112
+ def delete_prefix(prefix: str):
113
+ paginator = s3.get_paginator("list_objects_v2")
114
+ for page in paginator.paginate(Bucket=S3_BUCKET, Prefix=prefix):
115
+ if "Contents" not in page:
116
+ continue
117
+ s3.delete_objects(
118
+ Bucket=S3_BUCKET,
119
+ Delete={"Objects": [{"Key": obj["Key"]} for obj in page["Contents"]]}
120
+ )
121
+
122
+
123
+ def cleanup_old_user_folders():
124
+ paginator = s3.get_paginator("list_objects_v2")
125
+
126
+ folders = []
127
+ for page in paginator.paginate(Bucket=S3_BUCKET, Prefix=f"{S3_BASE_PREFIX}/", Delimiter="/"):
128
+ for p in page.get("CommonPrefixes", []):
129
+ prefix = p["Prefix"]
130
+ resp = s3.list_objects_v2(Bucket=S3_BUCKET, Prefix=prefix)
131
+ if "Contents" in resp:
132
+ last_modified = max(obj["LastModified"] for obj in resp["Contents"])
133
+ folders.append((last_modified, prefix))
134
+
135
+ folders.sort(reverse=True)
136
+
137
+ for _, prefix in folders[MAX_FOLDERS:]:
138
+ delete_prefix(prefix)
139
+
140
+
141
+ def delete_user_history(user_id: str):
142
+ delete_prefix(f"{S3_BASE_PREFIX}/{user_id}/")
143
+ user_sessions.pop(user_id, None)
144
+
145
+ def create_runtime_user_id() -> str:
146
+ return f"{uuid4()}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
147
+
148
+ async def generate_response(user_input: str, user_id: str):
149
+ try:
150
+ if user_id == "new_chat":
151
+ cleanup_old_user_folders()
152
+ user_id = create_runtime_user_id()
153
+
154
+ elif re.fullmatch(r"[a-f0-9\-]{36}_\d{8}_\d{6}", user_id, re.IGNORECASE):
155
+ pass
156
+
157
+ else:
158
+ cleanup_old_user_folders()
159
+ user_id = create_runtime_user_id()
160
+
161
+ except Exception:
162
+ cleanup_old_user_folders()
163
+ user_id = create_runtime_user_id()
164
+
165
+ messages = get_user_messages(user_id)
166
+ messages.append(HumanMessage(content=user_input))
167
+ user_sessions[user_id] = trim_history(messages)
168
+ #print( "trim messages:" ,user_sessions[user_id])
169
+ prompt = chat_history(user_input, user_sessions[user_id])
170
+ response_content = await llm(prompt)
171
+ return {"user_id": user_id, "chat_history_query": response_content}
172
+
173
+
174
+ async def store_final_chat(user_id: str, user_input: str, final_reply: str):
175
+ messages = get_user_messages(user_id)
176
+ truncated_reply = truncate_to_tokens(final_reply)
177
+ messages.append(HumanMessage(content=user_input))
178
+ messages.append(AIMessage(content=truncated_reply))
179
+ user_sessions[user_id] = trim_history(messages)
180
+ save_messages_to_s3(user_id)
181
+
182
+
183
+ # async def chat():#
184
+ # user_id = "703af383-6b70-4b68-979c-f005d7ff866f"#
185
+ # while True:
186
+ # user_input = input("You: ")#
187
+ # if user_input.lower() == "exit":
188
+ # break#
189
+ # res = await generate_response(user_input, user_id)#
190
+ # user_id = res["user_id"]
191
+ # reply = res["chat_history_query"]#
192
+ # print("Bot:", reply)#
193
+ # await store_final_chat(user_id, user_input, reply)#
194
+ # asyncio.run(chat())
all_combine_code/memory_/prompt_agents.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from textwrap import dedent
2
+ def chat_history(user_input, history):
3
+ prompt = dedent(f"""
4
+ You are a 20+ award-winning elite Conversational Intent Synthesizer and Question Generated Architect.
5
+
6
+ You are given:
7
+ - The full previous conversation history
8
+ - The current user input
9
+
10
+ ---
11
+
12
+ ## PREVIOUS CONVERSATION HISTORY:
13
+ {history}
14
+
15
+
16
+ ## CURRENT USER INPUT:
17
+ {user_input}
18
+
19
+ ---
20
+
21
+ Your task is to generate exactly ONE precise, fully-qualified, executable command-style query that completely and accurately captures the user's active intent by intelligently synthesizing the current input with all relevant prior conversation context, without omission, paraphrasing of intent verbs, explanation, or additional commentary.
22
+
23
+ **Core behavior:**
24
+
25
+ 1. Deeply understand the `user input` and all Conversional history inside intent. Accurately find relation user input and previous chat history to make 100% required response.
26
+ 2. If the current user input does NOT introduce a clear new intent, assume it continues the previous active intent.
27
+ 3. Extract and accumulate all relevant entities and details across conversation turns (names, companies, attributes, requirements, conditions, constraints, etc.).
28
+ 4. Merge the active intent and all accumulated entities into a single, complete command-style query.
29
+ 5. Analyze the current user query in the context of previous conversation history to fully understand their relationship and intent before generating a response.
30
+
31
+
32
+ **Intent handling rules:**
33
+
34
+ - If the user clearly provides a new intent, discard the previous intent and start fresh.
35
+ - If the user input only provides details, corrections, or values, apply them to the existing intent.
36
+ - Do NOT drop previously collected information unless the user explicitly corrects or removes it.
37
+ - **If the user query contains explicit indicators like this is "new chat", "new query", "new question", or similar phrases, treat it as a completely new intent query and do NOT use Previous Chat History Query.**
38
+ eg. total lead count new chat ( or use new chat/ not consider previous chat history ot this similar)
39
+ your answer : total lead count?
40
+
41
+ **Output rules:**
42
+
43
+ - Preserve the original intent wording as much as possible.
44
+ - Do NOT paraphrase intent verbs.
45
+ - Do NOT generate explanations, summaries, or conversational responses.
46
+ - Do NOT ask questions.
47
+ - Do NOT output multiple options.
48
+ - Do NOT include system text or reasoning.
49
+ - Return ONLY the final synthesized query.
50
+
51
+ **Greeting and small-talk handling:**
52
+
53
+ - If the user input is a greeting, acknowledgement, or small talk
54
+ (e.g., hi, hello, hey, good morning, thanks, ok),
55
+ treat it as a NEW intent.
56
+ - In this case, return the greeting exactly as provided.
57
+ - Do NOT reuse or continue any previous intent.
58
+
59
+ **Special cases:**
60
+
61
+ - If the current input is already a complete standalone query, return it as-is.
62
+ - If the current input is unrelated to the previous conversation, treat it as a new intent.
63
+ - If names or entities were previously changed or corrected, always use the latest valid value.
64
+
65
+ **Very strict rule:**
66
+ - Before whole chat history and current message clearly understand to make the smart and professional query format.
67
+
68
+
69
+ Your output must always be a single, professional, command-style query that reflects the full conversation context.
70
+
71
+ *(NOTE: query inside any spell mistake, please correct it.)*
72
+
73
+ ### **VERY IMPORTANT NOTES:**
74
+
75
+ - Query inside mention fresh or new not consider history, this similar consider that is **new one query**.
76
+ - Query inside "name" or "mail" mention time return same mail and name. Never assume field name like (firstname, lastname, email 1).
77
+ - Strictly Follow this : always prioritize the most recent/last user message while accurately synthesizing it with relevant prior conversation context, unless explicitly instructed to treat it as a new query.
78
+ - Strictly avoid the SQL/MySQL format query.
79
+
80
+ Return ONLY the final query with plain/text.
81
+ """)
82
+ return prompt
all_combine_code/models_/__init__.py ADDED
File without changes
all_combine_code/models_/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (176 Bytes). View file
 
all_combine_code/models_/__pycache__/code_generation_model.cpython-313.pyc ADDED
Binary file (4.68 kB). View file
 
all_combine_code/models_/code_generation_model.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, re, sys, json,httpx
2
+ from openai import AsyncOpenAI
3
+ from dotenv import load_dotenv
4
+ import asyncio
5
+ load_dotenv()
6
+
7
+ client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"),
8
+ timeout=240.0)
9
+
10
+ class CodeGenerationLLM:
11
+ async def generate_response(self,prompt: str) -> str:
12
+ full_text = []
13
+ response = await client.chat.completions.create(
14
+ model="gpt-5-chat-latest",
15
+ temperature=0.1,
16
+ messages=[
17
+ {"role": "system", "content": "You are an expert in PyMongo query generation with valid JSON format."},
18
+ {"role": "user", "content": prompt}
19
+ ],
20
+ stream=True,
21
+ max_tokens=2500,
22
+ #max_completion_tokens=3800
23
+ response_format={"type": "json_object"}
24
+ )
25
+ async for chunk in response:
26
+ delta = chunk.choices[0].delta
27
+ if delta and delta.content:
28
+ token = delta.content
29
+ print(token, end="", flush=True)
30
+ full_text.append(token)
31
+ return "".join(full_text).strip()
32
+
33
+ async def generate_final_output(self,prompt) -> str:
34
+ llm_output = await self.generate_response(prompt)
35
+ return llm_output
36
+
37
+
38
+ class OpenRouterClient:
39
+ def __init__(self):
40
+ load_dotenv()
41
+
42
+ self.api_key = os.getenv("OPENROUTER_API_KEY")
43
+
44
+ self.url = "https://openrouter.ai/api/v1/chat/completions"
45
+
46
+ self.headers = {
47
+ "Authorization": f"Bearer {self.api_key}",
48
+ "Content-Type": "application/json",
49
+ }
50
+
51
+ async def stream_chat(self, prompt):
52
+ payload = {
53
+ "model": "mistralai/codestral-2508", # anthropic/claude-sonnet-4.6 ,mistralai/codestral-2508
54
+ "stream": True,
55
+ "max_tokens": 3000,
56
+ "temperature": 0.1,
57
+ "messages": [
58
+ {"role": "system", "content": "You are an expert in PyMongo query generation with valid JSON format."},
59
+ {"role": "user", "content": prompt}
60
+ ]
61
+ }
62
+
63
+ full_text = ""
64
+
65
+ async with httpx.AsyncClient(timeout=60) as client:
66
+ async with client.stream(
67
+ "POST",
68
+ self.url,
69
+ headers=self.headers,
70
+ json=payload
71
+ ) as r:
72
+
73
+ async for line in r.aiter_lines():
74
+
75
+ if not line or not line.startswith("data:"):
76
+ continue
77
+
78
+ data = line.removeprefix("data: ").strip()
79
+
80
+ if data == "[DONE]":
81
+ break
82
+
83
+ chunk = json.loads(data)
84
+ delta = chunk["choices"][0]["delta"].get("content")
85
+
86
+ if delta:
87
+ print(delta, end="", flush=True)
88
+ full_text += delta
89
+
90
+ return full_text
all_combine_code/models_/delveant_llm_model_/__init__.py ADDED
File without changes
all_combine_code/models_/delveant_llm_model_/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (192 Bytes). View file
 
all_combine_code/models_/delveant_llm_model_/__pycache__/multi_agent_llm.cpython-313.pyc ADDED
Binary file (15.8 kB). View file
 
all_combine_code/models_/delveant_llm_model_/multi_agent_llm.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, re, sys, json, gc
2
+ import asyncio,logging,random,time
3
+ from dotenv import load_dotenv
4
+ from openai import AsyncOpenAI
5
+ load_dotenv()
6
+
7
+ from textwrap import dedent
8
+ LLM_SEMAPHORE = asyncio.Semaphore(8)
9
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
10
+ OPENROUTER_URL = os.getenv("OPENROUTER_URL")
11
+ REQUEST_TIMEOUT = 40.0
12
+
13
+ client = AsyncOpenAI(
14
+ #base_url=OPENROUTER_URL,
15
+ api_key=os.getenv("OPENAI_API_KEY"),
16
+ timeout=120,
17
+ )
18
+
19
+
20
+ class MultiAgentLLM:
21
+ async def general_questions_answer(self,prompt):
22
+ response = await client.chat.completions.create(
23
+ model="gpt-3.5-turbo",
24
+ messages=[
25
+ {"role": "system", "content": "You are very helful AI assistant.Following Questions understand to make the smart response"},
26
+ {"role": "user", "content": prompt}
27
+ ],
28
+ )
29
+ message = response.choices[0].message.content
30
+ return message
31
+
32
+
33
+ async def generate_ai_response_greetings(self,user_prompt) -> str:
34
+ async with LLM_SEMAPHORE:
35
+ for attempt in range(3):
36
+ try:
37
+ system_prompt = dedent("""
38
+ You are DelveAnt CRM’s AI assistant developed by kovaion.ai.
39
+
40
+ Rules:
41
+ - Keep responses short, professional, and helpful.
42
+ - If the query mentions "Delvant CRM" or asks about the product,
43
+ give a brief 2–3 line overview of DelveAnt CRM, its purpose, and key benefits.
44
+ - Do not give long explanations.
45
+ - Do not sound casual or chatty.
46
+ - Always use correct spelling: "DelveAnt".
47
+ """).strip()
48
+ response = await client.chat.completions.create(
49
+ model="gpt-3.5-turbo",
50
+ messages=[
51
+ {"role": "system", "content":system_prompt},
52
+ {"role": "user", "content": user_prompt},
53
+ ])
54
+ return response.choices[0].message.content
55
+
56
+ except Exception as e:
57
+ logging.info(f"LLM attempt {attempt + 1} failed: {e}")
58
+ if attempt == 2:
59
+ raise
60
+ await asyncio.sleep(2 ** attempt + random.uniform(0.2, 0.6))
61
+
62
+ async def delveant_chatbot(self,user_prompt) -> str:
63
+ async with LLM_SEMAPHORE:
64
+ for attempt in range(3):
65
+ try:
66
+ system_prompt = dedent("""
67
+ You are the owner and official AI assistant of DelveAnt CRM.
68
+ Rules:
69
+ - Answer the user question briefly and professionally.
70
+ - Maximum 3 short sentences.
71
+ - Do NOT include lists, steps, or examples.
72
+ - Do NOT use line breaks.
73
+ - End the response with a full stop.
74
+ - Always use correct spelling: "DelveAnt".
75
+ """).strip()
76
+ response = await client.chat.completions.create(
77
+ model="gpt-4o-mini",
78
+ messages=[
79
+ {"role": "system", "content": system_prompt},
80
+ {"role": "user", "content": user_prompt},
81
+ ],
82
+ temperature=0.0,
83
+ max_tokens=150,
84
+ )
85
+ return response.choices[0].message.content
86
+
87
+ except Exception as e:
88
+ logging.info(f"LLM attempt {attempt + 1} failed: {e}")
89
+ if attempt == 2:
90
+ raise
91
+ await asyncio.sleep(2 ** attempt + random.uniform(0.2, 0.6))
92
+
93
+
94
+ async def plot_finder(self,user_prompt) -> str:
95
+ async with LLM_SEMAPHORE:
96
+ for attempt in range(3):
97
+ try:
98
+ system_prompt = "You are very highly Rule based **Decision Find Agent**."
99
+ response = await client.chat.completions.create(
100
+ model="gpt-5-chat-latest",
101
+ messages=[
102
+ {"role": "system", "content": system_prompt},
103
+ {"role": "user", "content": user_prompt},
104
+ ],
105
+ temperature=0.0,
106
+ max_tokens=150,
107
+ )
108
+ return response.choices[0].message.content
109
+
110
+ except Exception as e:
111
+ logging.info(f"LLM attempt {attempt + 1} failed: {e}")
112
+ if attempt == 2:
113
+ raise
114
+ await asyncio.sleep(2 ** attempt + random.uniform(0.2, 0.6))
115
+
116
+ async def empty_list_responser(self,user_prompt) -> str:
117
+ async with LLM_SEMAPHORE:
118
+ prompt = dedent(f"""
119
+ # You are a professional and well-structured responder. small or medium format text not long enough.
120
+
121
+ **User Input:**
122
+ - {user_prompt}
123
+
124
+ **MongoDB Query Output:**
125
+ []
126
+
127
+ **System Context:**
128
+ - The MongoDB query for the above user input returned an empty list ("[]").
129
+ - This means that no matching data was found in the database for the given query.
130
+
131
+ **Your Task:**
132
+ - Provide a clear, professional, and informative response to the user **based on the query and the empty result**.
133
+ - Clearly explain that no data was found for the requested query.
134
+ - If the user’s query is unclear or incomplete, politely suggest a refined version of the query or possible next steps.
135
+ - Keep the response concise, neutral, and easy to understand.
136
+ - Do **not** include any code, technical details, or system-related text in your final response.
137
+ - Your response must directly relate to the user’s query and the fact that the result was empty.
138
+ - Your response inside don't mention "Unfortunately" this word.
139
+ """)
140
+ for attempt in range(3):
141
+ try:
142
+ response = await client.chat.completions.create(
143
+ model="gpt-4o-mini",
144
+ messages=[
145
+ {"role": "system", "content": "You are a helpful AI assistant."},
146
+ {"role": "user", "content": prompt},
147
+ ],
148
+ temperature=0.0,
149
+ max_tokens=100,
150
+ )
151
+ return response.choices[0].message.content
152
+
153
+ except Exception as e:
154
+ logging.info(f"LLM attempt {attempt + 1} failed: {e}")
155
+ if attempt == 2:
156
+ raise
157
+ await asyncio.sleep(2 ** attempt + random.uniform(0.2, 0.6))
158
+
159
+
160
+ class CodeErrorFixer:
161
+ def __init__(self):
162
+ self.system_prompt = dedent("""
163
+ You are a senior Python engineer and runtime error fixer.
164
+ Your job is to:
165
+ 1. Analyze the error
166
+ 2. Identify the root cause
167
+ 3. Fix the error.
168
+ 4. Return corrected code.
169
+ 5. Do not add explanations unless asked
170
+ 6. Return corrected python code.
171
+ """)
172
+
173
+ async def code_debuger(self,prompt: str):
174
+ full_text = []
175
+ stream = await client.chat.completions.create(
176
+ model="gpt-4.1-2025-04-14",
177
+ messages=[
178
+ {"role": "system", "content": self.system_prompt},
179
+ {"role": "user", "content": prompt}
180
+ ],
181
+ max_tokens=4000,
182
+ stream=True,
183
+ )
184
+ async for chunk in stream:
185
+ delta = chunk.choices[0].delta
186
+ if delta and delta.content:
187
+ token = delta.content
188
+ print(token, end="", flush=True)
189
+ full_text.append(token)
190
+ return "".join(full_text)
191
+
192
+
193
+ class ChatSynthesizer:
194
+ def __init__(self):
195
+ self.model = "gpt-5.1"
196
+ async def chat_synthesizer(self,user_prompt) -> str:
197
+ async with LLM_SEMAPHORE:
198
+ for attempt in range(3):
199
+ try:
200
+ response = await client.chat.completions.create(
201
+ model=self.model,
202
+ messages=[
203
+ {"role": "system", "content":"You are 20+ award-winning Intelligent Conversational Intent Synthesizer"},
204
+ {"role": "user", "content": user_prompt},
205
+ ],
206
+ temperature=0,
207
+ top_p=1,
208
+ #reasoning_effort=None,
209
+ max_completion_tokens=300,
210
+ )
211
+ return response.choices[0].message.content
212
+
213
+ except Exception as e:
214
+ logging.info(f"LLM attempt {attempt + 1} failed: {e}")
215
+ if attempt == 2:
216
+ raise
217
+ await asyncio.sleep(2 ** attempt + random.uniform(0.2, 0.6))
218
+
219
+ class CollectionAndQueryFinder:
220
+ def __init__(self):
221
+ self.model = "gpt-5-chat-latest"
222
+ self.system_prompt = dedent("""
223
+ You are 20+ years award winning Accurate CRM Query Formatter and Database Collection Name Finder Agent
224
+ """)
225
+
226
+ async def generate_response(self,prompt,agents,user_id,person_id,collection_keywords,priveous_chat_history_query) -> str:
227
+ async with LLM_SEMAPHORE:
228
+ for attempt in range(3):
229
+ try:
230
+ formatted_prompt = agents(prompt, user_id, person_id, collection_keywords,
231
+ priveous_chat_history_query)
232
+ response = await client.chat.completions.create(
233
+ model=self.model,
234
+ messages=[
235
+ {"role": "system", "content":self.system_prompt},
236
+ {"role": "user", "content": formatted_prompt},
237
+ ],
238
+ temperature=0.1,
239
+ max_tokens=300,
240
+ #reasoning_effort="none"
241
+ )
242
+ return response.choices[0].message.content
243
+
244
+ except Exception as e:
245
+ logging.info(f"LLM attempt {attempt + 1} failed: {e}")
246
+ if attempt == 2:
247
+ raise
248
+ await asyncio.sleep(2 ** attempt + random.uniform(0.2, 0.6))
249
+
250
+
251
+ # async def main():
252
+ # chat = ChatSynthesizer()
253
+ # user_prompt = "what is java?"
254
+ #
255
+ # try:
256
+ # response = await chat.chat_synthesizer(user_prompt)
257
+ # print("LLM Response:", response)
258
+ # except Exception as e:
259
+ # print("Error:", e)
260
+ #
261
+ #
262
+ # # Run the async main function
263
+ # if __name__ == "__main__":
264
+ # asyncio.run(main())
all_combine_code/models_/delveant_llm_model_/tool_calling_model.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+ warnings.filterwarnings("ignore")
3
+
4
+ import os
5
+ import json
6
+ from dotenv import load_dotenv
7
+ from openai import AsyncOpenAI
8
+ from agents_.delveant_prompts.tool_clasification_agent import TOOLS, tool_classification_agent
9
+ load_dotenv()
10
+
11
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
12
+ OPENROUTER_BASE_URL = os.getenv("OPENROUTER_URL")
13
+
14
+
15
+ client = AsyncOpenAI(
16
+ api_key=OPENROUTER_API_KEY,
17
+ base_url=OPENROUTER_BASE_URL,
18
+ default_headers={"X-Title": "CRM-Tool-Agent"},
19
+ timeout=30.0
20
+ )
21
+
22
+ async def classify_and_call_tool(user_query):
23
+ response = await client.chat.completions.create(
24
+ model="anthropic/claude-sonnet-4.6",
25
+ messages=[
26
+ {"role": "system", "content": tool_classification_agent()},
27
+ {"role": "user", "content": user_query},
28
+ ],
29
+ tools=TOOLS,
30
+ tool_choice="auto",
31
+ temperature=0.1,
32
+ )
33
+ return response
34
+
35
+
36
+ def parse_tool_response(response):
37
+ message = response.choices[0].message
38
+ if hasattr(message, "tool_calls") and message.tool_calls:
39
+ tool_calls = []
40
+ for call in message.tool_calls:
41
+ args = call.function.arguments
42
+ if isinstance(args, str):
43
+ try:
44
+ args1 = json.loads(args)
45
+ except json.JSONDecodeError:
46
+ args1 = {}
47
+
48
+ tool_calls.append({
49
+ "tool_name": call.function.name,
50
+ "arguments": args,
51
+ })
52
+ return tool_calls, None
53
+ return None, message.content
54
+
55
+
56
+ async def tool_finder_llm1(previous_chat_history_query):
57
+ try:
58
+ response = await classify_and_call_tool(previous_chat_history_query)
59
+ tool_calls, content = parse_tool_response(response)
60
+ if tool_calls:
61
+ for call in tool_calls:
62
+ tool_name = call["tool_name"]
63
+ tool_args = call["arguments"]
64
+ return tool_name, tool_args
65
+
66
+ else:
67
+ if content:
68
+ return "no_tool_name", content
69
+
70
+ else:
71
+ return "no_tool_name", "no_tool_args"
72
+
73
+ except Exception as e:
74
+ return "no_tool_name", "Our techinical team working this related process, update soon."
75
+
76
+ from openai import AsyncOpenAI
77
+ from dotenv import load_dotenv
78
+ load_dotenv()
79
+ import os
80
+
81
+ client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))
82
+
83
+ async def tool_finder_llm(previous_chat_history_query):
84
+ response = await client.chat.completions.create(
85
+ model="gpt-5.2",
86
+ messages=[
87
+ {"role": "system", "content": tool_classification_agent()},
88
+ {"role": "user", "content": previous_chat_history_query}
89
+ ],
90
+ tools=TOOLS,
91
+ tool_choice="auto",
92
+ #temperature=0.0,
93
+ timeout=120.0,
94
+ reasoning_effort='none',
95
+ )
96
+ message = response.choices[0].message
97
+ if message.tool_calls and len(message.tool_calls) > 0:
98
+ tool_call = message.tool_calls[0]
99
+ tool_name = tool_call.function.name
100
+ tool_args = tool_call.function.arguments
101
+ return tool_name, tool_args
102
+ else:
103
+ content = message.content
104
+ if content:
105
+ return "no_tool_name", content
106
+
107
+ else:
108
+ return "no_tool_name", "no_tool_args"
109
+
110
+
111
+ # async def main():
112
+ # query = "create lead with email 'vicky@gmail.com', IBM"
113
+ # tool_name, tool_args = await tool_finder_llm(query)
114
+ # print("Tool:", tool_name)
115
+ # print("Arguments:", tool_args)
116
+ #
117
+ # asyncio.run(main())
all_combine_code/mongodb_data_chat/delveant_chatbot.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ from pymongo import MongoClient
5
+ from dotenv import load_dotenv
6
+
7
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
8
+ sys.path.append(project_root)
9
+
10
+ from tools_.delveant_all_tools_.crm_data_extraction import (extract_crm_lead)
11
+
12
+ from models_.code_generation_model import CodeGenerationLLM
13
+ from models_.delveant_llm_model_.multi_agent_llm import CollectionAndQueryFinder
14
+
15
+ load_dotenv()
16
+
17
+ MONGO_URI = os.getenv("MONGO_URI")
18
+ mongo_db_client = MongoClient(MONGO_URI)
19
+
20
+ class Delveant:
21
+ def __init__(self):
22
+ self.mongo_db_client = mongo_db_client
23
+
24
+ def extract_keywords(self,db_name):
25
+ db = self.mongo_db_client[db_name]
26
+ crm_leads_collection = db["CRM_AICollection_Keys"]
27
+ first_two = list(crm_leads_collection.find().sort([("_id", 1)]).limit(2))
28
+ last_two = list(crm_leads_collection.find().sort([("_id", -1)]).limit(2))
29
+ documents = first_two + last_two
30
+ formatted_output = []
31
+ for doc in documents:
32
+ for item in doc.get("keywords", []):
33
+ collection_name = item.get("collection_name", "")
34
+ keywords = item.get("keywords", [])
35
+
36
+ formatted_output.append({
37
+ "collection_name": collection_name,
38
+ "keywords": keywords
39
+ })
40
+ return formatted_output
41
+
42
+ def extract_lead_data_(self,input_message,collection_name,db_name,user_id,perso_id):
43
+ documents, field_name_datatypes, status, source = extract_crm_lead(
44
+ collection_name=collection_name,
45
+ db_name=db_name,
46
+ )
47
+ prompt = mongodb_query_generator_leads(input_message,documents,user_id,
48
+ field_name_datatypes,db_name,collection_name,source)
49
+ return prompt
50
+
51
+ def resolve_date_variables(self,final_json) -> dict:
52
+ python_code = final_json.get("python_code")
53
+ if not python_code:
54
+ return final_json
55
+ local_vars = {}
56
+ exec(python_code, {"datetime": datetime, "timedelta": timedelta, "timezone": timezone}, local_vars)
57
+
58
+ def replace_vars(obj):
59
+ if isinstance(obj, dict):
60
+ return {k: replace_vars(v) for k, v in obj.items()}
61
+ elif isinstance(obj, list):
62
+ return [replace_vars(i) for i in obj]
63
+ elif isinstance(obj, str) and obj in local_vars:
64
+ return local_vars[obj]
65
+ return obj
66
+
67
+ final_json["filter"] = replace_vars(final_json.get("filter", {}))
68
+ final_json["pipeline"] = replace_vars(final_json.get("pipeline", []))
69
+ return final_json
70
+
71
+ async def final_response_executer(self,prompt):
72
+ try:
73
+ answer = generate_response(prompt)
74
+ final_json = json.loads(answer)
75
+ final_json = resolve_date_variables(final_json)
76
+
77
+ pipeline = final_json.get("pipeline", [])
78
+ filter_ = final_json.get("filter", {})
79
+ operation = final_json.get("operation", "find")
80
+ projection = final_json.get("projection", {})
81
+ sort = final_json.get("sort", {})
82
+ limit = final_json.get("limit", 0)
83
+
84
+ if operation.lower() == "find":
85
+ cursor = crm_leads_collection.find(filter_, projection)
86
+ if sort:
87
+ cursor = cursor.sort(list(sort.items()))
88
+ if limit:
89
+ cursor = cursor.limit(limit)
90
+ results = list(cursor)
91
+
92
+ elif operation.lower() == "aggregate":
93
+ if sort and not any("$sort" in s for s in pipeline):
94
+ pipeline.append({"$sort": sort})
95
+ if limit and not any("$limit" in s for s in pipeline):
96
+ pipeline.append({"$limit": limit})
97
+ results = list(crm_leads_collection.aggregate(pipeline))
98
+
99
+ else:
100
+ print("Unknown operation:", operation)
101
+ results = []
102
+
103
+ json_data = json.loads(json_util.dumps(results))
104
+ cleaned_documents = []
105
+ for doc in json_data:
106
+ cleaned_doc = {
107
+ k: v
108
+ for k, v in doc.items()
109
+ if v not in (None, '') and k != "_id"
110
+ }
111
+ if cleaned_doc:
112
+ cleaned_documents.append(cleaned_doc)
113
+ return cleaned_documents
114
+
115
+ except Exception as e:
116
+ return "error"
117
+
118
+
119
+ async def generate_response(self,prompt,model="openai"):
120
+ if model == "openai":
121
+ print("openai model call")
122
+ llm = CodeGenerationLLM()
123
+ response = await llm.generate_final_output(prompt)
124
+
125
+ if model == "openrouter":
126
+ print("openrouter model call")
127
+ llm = OpenRouterClient()
128
+ response = await llm.stream_chat(prompt)
129
+
130
+ return response
131
+
132
+ async def collection_query_finader(self,prompt,agents,user_id,person_id,collection_keywords,priveous_chat_history_query):
133
+ llm = CollectionAndQueryFinder()
134
+ return await llm.generate_response(prompt,agents,user_id,person_id,collection_keywords,priveous_chat_history_query)
135
+
136
+ async def db_and_collection(self,db_name,collection_name):
137
+ db = self.mongo_db_client[db_name]
138
+ collection_name= db[collection_name]
139
+ return db, collection_name
140
+
141
+ async def final_output_extractor_and_responser(self, prompt,priveous_chat_history_query ,agents,db_name,user_id,person_id):
142
+ pass
143
+
144
+ async def run_file(self,user_input,priveous_chat_history_query,db_name, user_id, person_id):
145
+ answer = await self.final_output_extractor_and_responser(
146
+ user_input,priveous_chat_history_query, db_collection_find, db_name, user_id, person_id
147
+ )
148
+
149
+
150
+
151
+
152
+
all_combine_code/recommedation_engine/__pycache__/app.cpython-313.pyc ADDED
Binary file (4.77 kB). View file
 
all_combine_code/recommedation_engine/__pycache__/embeddings_.cpython-313.pyc ADDED
Binary file (4.95 kB). View file
 
all_combine_code/recommedation_engine/__pycache__/s3_file_uploader.cpython-313.pyc ADDED
Binary file (4.21 kB). View file
 
all_combine_code/recommedation_engine/app.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import asyncio
4
+ from typing import List
5
+ from fastapi import FastAPI, UploadFile, File, HTTPException
6
+ from dotenv import load_dotenv
7
+
8
+ from binary_classifier.classify import GeminiSportsClassifier
9
+ from embeddings_ import process_inputs
10
+ from s3_file_uploader import upload_to_s3
11
+
12
+ load_dotenv()
13
+
14
+ app = FastAPI(title="Sports Classification API")
15
+ classifier = GeminiSportsClassifier()
16
+
17
+ UPLOAD_DIR = "temp_uploads"
18
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
19
+
20
+ @app.post("/classify")
21
+ async def classify_media(files: List[UploadFile] = File(...)):
22
+ if not files:
23
+ raise HTTPException(status_code=400, detail="No files uploaded")
24
+ file_paths = []
25
+ path_to_original = {}
26
+ try:
27
+ for file in files:
28
+ ext = os.path.splitext(file.filename)[1]
29
+ temp_name = f"{uuid.uuid4()}{ext}"
30
+ temp_path = os.path.join(UPLOAD_DIR, temp_name)
31
+ with open(temp_path, "wb") as f:
32
+ content = await file.read()
33
+ f.write(content)
34
+ file_paths.append(temp_path)
35
+ path_to_original[temp_path] = file.filename
36
+ first_level_results = await classifier.classify_batch(file_paths)
37
+ allowed = {"not_sports", "error"}
38
+ filtered_items = []
39
+
40
+ for item in first_level_results:
41
+ if item.get("answer") not in allowed:
42
+ path = item["file"]
43
+ file_name = path_to_original.get(path)
44
+ ext = os.path.splitext(path)[1].lower()
45
+
46
+ media_type = "video" if ext in [".mp4", ".avi", ".mov"] else "image"
47
+
48
+ filtered_items.append({
49
+ "file_path": path,
50
+ "file_name": file_name,
51
+ "media_type": media_type,
52
+ "answer": item.get("answer")
53
+ })
54
+
55
+ if not filtered_items:
56
+ return []
57
+
58
+ embed_paths = [item["file_path"] for item in filtered_items]
59
+ embedding_result = process_inputs(embed_paths)
60
+ image_embeddings = embedding_result.get("images", [])
61
+ video_embeddings = embedding_result.get("videos", [])
62
+ img_idx = 0
63
+ vid_idx = 0
64
+
65
+ for item in filtered_items:
66
+ if item["media_type"] == "image":
67
+ item["embedding"] = image_embeddings[img_idx].tolist() if len(image_embeddings) > 0 else None
68
+ img_idx += 1
69
+ else:
70
+ item["embedding"] = video_embeddings[vid_idx].tolist() if len(video_embeddings) > 0 else None
71
+ vid_idx += 1
72
+
73
+ async def upload_single(item):
74
+ url = await upload_to_s3(item["file_path"], item["file_name"])
75
+ item["s3_url"] = url
76
+ return item
77
+
78
+ uploaded_results = await asyncio.gather(*[upload_single(item) for item in filtered_items])
79
+
80
+ return uploaded_results
81
+
82
+ finally:
83
+ for path in file_paths:
84
+ if os.path.exists(path):
85
+ os.remove(path)
all_combine_code/recommedation_engine/binary_classifier/__pycache__/classify.cpython-313.pyc ADDED
Binary file (6.61 kB). View file
 
all_combine_code/recommedation_engine/binary_classifier/classify.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, cv2,json
2
+ import asyncio
3
+ from PIL import Image
4
+ from google import genai
5
+ from google.genai import types
6
+ from textwrap import dedent
7
+
8
+ from dotenv import load_dotenv
9
+ load_dotenv()
10
+
11
+ client = genai.Client(api_key=os.getenv("GEMINI_API_KEY"))
12
+
13
+ class GeminiSportsClassifier:
14
+ def __init__(self):
15
+ self.categories = [
16
+ "a photo of a marathon race on city roads", "a photo of long distance marathon runners with bib numbers",
17
+ "a photo of a large group running marathon event", "a photo of Olympic marathon race",
18
+ "a photo of sprint running race on track", "a photo of Olympic track and field athletics",
19
+ "a photo of relay race baton passing", "a photo of hurdles race on track",
20
+ "a photo of high jump Olympic event",
21
+ "a photo of pole vault Olympic event", "a photo of long jump Olympic event",
22
+ "a photo of javelin throw Olympic event",
23
+ "a photo of discus throw Olympic event", "a photo of shot put Olympic event",
24
+ "a photo of Olympic swimming competition",
25
+ "a photo of Olympic cycling road race", "a photo of Olympic track cycling",
26
+ "a photo of Olympic gymnastics performance",
27
+ "a photo of Olympic boxing match", "a photo of Olympic wrestling match",
28
+ "a photo of Olympic weightlifting competition",
29
+ "a photo of cricket match on stadium", "a photo of cricket players batting and bowling",
30
+ "a photo of football match in stadium",
31
+ "a photo of soccer players playing match", "a photo of football goal scoring moment",
32
+ "a photo of basketball game in indoor court",
33
+ "a photo of volleyball match on court", "a photo of tennis match on court",
34
+ "a photo of badminton match indoor stadium"
35
+ ]
36
+ def _prepare_image(self, img):
37
+ img.thumbnail((512, 512))
38
+ return img
39
+
40
+ def _extract_frames(self, video_path, num_frames=2):
41
+ cap = cv2.VideoCapture(video_path)
42
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
43
+ frames = []
44
+ for i in range(num_frames):
45
+ pos = int((i + 1) * total_frames / (num_frames + 1))
46
+ cap.set(cv2.CAP_PROP_POS_FRAMES, pos)
47
+ ret, frame = cap.read()
48
+ if ret:
49
+ frame = cv2.resize(frame, (480, 270))
50
+ img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
51
+ frames.append(img)
52
+ cap.release()
53
+ return frames
54
+
55
+ async def classify(self, file_path):
56
+ ext = os.path.splitext(file_path)[1].lower()
57
+ try:
58
+ media = []
59
+ if ext in ['.jpg', '.jpeg', '.png', ".webp"]:
60
+ media.append(self._prepare_image(Image.open(file_path)))
61
+ elif ext in ['.mp4', '.avi', '.mov']:
62
+ media = self._extract_frames(file_path)
63
+ else:
64
+ return {"file": file_path, "answer": "not_sports"}
65
+
66
+ prompt = dedent(f"""
67
+ Analyze the provided media (image or video frames).
68
+ 1. If it matches one of these categories, return the category: {self.categories}
69
+ 2. If it is not related to these sports, return "not_sports".
70
+ Output strictly in JSON: {{"answer": "category_name"}}
71
+ """)
72
+
73
+ response = await client.aio.models.generate_content(
74
+ model="gemini-2.0-flash",
75
+ contents=[prompt, *media],
76
+ config=types.GenerateContentConfig(
77
+ response_mime_type='application/json'
78
+ )
79
+ )
80
+
81
+ data = json.loads(response.text)
82
+ if isinstance(data, dict):
83
+ return {"file": file_path, **data}
84
+ else:
85
+ return {"file": file_path, "answer": str(data)}
86
+
87
+ except Exception as e:
88
+ return {"file": file_path, "answer": "error", "details": str(e)}
89
+
90
+ async def classify_batch(self, file_paths, max_concurrent=5):
91
+ semaphore = asyncio.Semaphore(max_concurrent)
92
+
93
+ async def classify_with_limit(path):
94
+ async with semaphore:
95
+ return await self.classify(path)
96
+
97
+ tasks = [classify_with_limit(path) for path in file_paths]
98
+ return await asyncio.gather(*tasks)
99
+
100
+
all_combine_code/recommedation_engine/embeddings_.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import clip
3
+ import cv2
4
+ from PIL import Image
5
+ from concurrent.futures import ThreadPoolExecutor
6
+
7
+ device = "cpu"
8
+
9
+ model, preprocess = clip.load("ViT-B/16", device=device)
10
+ model.eval()
11
+
12
+ SUPPORTED_IMAGE_EXTS = (".jpg", ".jpeg", ".png")
13
+ SUPPORTED_VIDEO_EXTS = (".mp4", ".avi", ".mov")
14
+
15
+ def preprocess_image(path):
16
+ img = Image.open(path).convert("RGB")
17
+ return preprocess(img)
18
+
19
+ def load_images_parallel(image_paths, max_workers=4):
20
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
21
+ images = list(executor.map(preprocess_image, image_paths))
22
+ return torch.stack(images)
23
+
24
+ def images_to_embeddings_cpu(image_paths, batch_size=32):
25
+ outputs = []
26
+
27
+ for i in range(0, len(image_paths), batch_size):
28
+ batch_paths = image_paths[i:i + batch_size]
29
+ images = load_images_parallel(batch_paths)
30
+
31
+ with torch.no_grad():
32
+ emb = model.encode_image(images)
33
+ emb = emb / emb.norm(dim=-1, keepdim=True)
34
+ outputs.append(emb)
35
+ return torch.cat(outputs).numpy()
36
+
37
+ def extract_frames(video_path, sample_rate=1):
38
+ cap = cv2.VideoCapture(video_path)
39
+ fps = cap.get(cv2.CAP_PROP_FPS)
40
+ interval = int(max(1, fps * sample_rate))
41
+
42
+ frames = []
43
+ count = 0
44
+
45
+ while cap.isOpened():
46
+ ret, frame = cap.read()
47
+ if not ret:
48
+ break
49
+
50
+ if count % interval == 0:
51
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
52
+ frames.append(Image.fromarray(frame))
53
+
54
+ count += 1
55
+
56
+ cap.release()
57
+ return frames
58
+
59
+
60
+ def video_to_embedding_cpu(video_path):
61
+ frames = extract_frames(video_path)
62
+
63
+ if not frames:
64
+ return None
65
+
66
+ images = torch.stack([preprocess(f) for f in frames])
67
+
68
+ with torch.no_grad():
69
+ emb = model.encode_image(images)
70
+ emb = emb / emb.norm(dim=-1, keepdim=True)
71
+
72
+ return emb.mean(dim=0).numpy()
73
+
74
+
75
+ def process_videos_parallel(video_files, max_workers=2):
76
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
77
+ return list(executor.map(video_to_embedding_cpu, video_files))
78
+
79
+
80
+ def process_inputs(files):
81
+ image_files = []
82
+ video_files = []
83
+
84
+ for f in files:
85
+ f_lower = f.lower()
86
+ if f_lower.endswith(SUPPORTED_IMAGE_EXTS):
87
+ image_files.append(f)
88
+ elif f_lower.endswith(SUPPORTED_VIDEO_EXTS):
89
+ video_files.append(f)
90
+
91
+ results = {}
92
+ if image_files:
93
+ results["images"] = images_to_embeddings_cpu(image_files)
94
+ if video_files:
95
+ results["videos"] = process_videos_parallel(video_files)
96
+ return results
97
+
98
+