bat-6 commited on
Commit
d1e4586
·
1 Parent(s): 27b965c
src/similarity_model/llm_feature_extractor.py DELETED
@@ -1,263 +0,0 @@
1
- import json
2
- import re
3
- import time
4
-
5
- from src.recommendation_engine.llm_client import generate_text
6
-
7
-
8
- def build_feature_prompt(text: str):
9
-
10
- return f"""
11
- You are an expert graduation project analyst.
12
-
13
- TASK:
14
- Extract only technical features explicitly mentioned in the project.
15
-
16
- RULES:
17
- - Do not invent anything.
18
- - Do not generate new ideas.
19
- - Extract technologies.
20
- - Extract algorithms.
21
- - Extract modules.
22
- - Extract engineering capabilities.
23
- - Ignore goals and benefits.
24
- Return between 2 and 10 features.
25
-
26
- If fewer than 5 technical features exist,
27
- return only the available features.
28
-
29
-
30
-
31
- Technologies such as:
32
- React,
33
- Angular,
34
- Vue,
35
- MySQL,
36
- PostgreSQL,
37
- MongoDB,
38
- Firebase,
39
- Django,
40
- Flask,
41
- Node.js
42
-
43
- must be included if explicitly mentioned.
44
-
45
- IMPORTANT:
46
- Every feature must be copied or paraphrased from the project text.
47
- Never infer, assume, or generate new capabilities.
48
- If a capability is not explicitly mentioned, do not include it.
49
-
50
- IMPORTANT:
51
-
52
- Do not extract:
53
- - goals
54
- - benefits
55
- - outcomes
56
- - business objectives
57
- - deployment statements
58
-
59
- Return SHORT canonical features.
60
-
61
- Maximum 4 words per feature.
62
-
63
- Examples:
64
- computer vision
65
- deep learning
66
- internet of things
67
- route optimization
68
- waste classification
69
-
70
- Do not include long descriptive phrases.
71
-
72
-
73
- IMPORTANT:
74
-
75
- Prefer domain-specific capabilities over generic AI terms.
76
-
77
- BAD:
78
- - machine learning
79
- - deep learning
80
- - neural networks
81
-
82
- GOOD:
83
- - waste classification
84
- - route optimization
85
- - bin fill level monitoring
86
- - waste generation forecasting
87
-
88
- If a specific capability exists, return it instead of the generic AI technology behind it.
89
-
90
- Maximum 4 words per feature.
91
-
92
- Only extract concrete technical capabilities.
93
-
94
- Return JSON only.
95
-
96
- Format:
97
-
98
- {{
99
- "features": [
100
- "feature 1",
101
- "feature 2"
102
- ]
103
- }}
104
-
105
- PROJECT:
106
-
107
- {text}
108
- """
109
-
110
-
111
- def extract_features_llm(text: str):
112
-
113
- print("CALLING GEMINI")
114
-
115
- prompt = build_feature_prompt(text)
116
-
117
- # Short delay to prevent hitting rate limits
118
- time.sleep(1)
119
-
120
- try:
121
- response = generate_text(
122
- prompt,
123
- task="feature",
124
- temperature = 0
125
- )
126
-
127
- return parse_features_response(
128
- response
129
- )
130
- except Exception as e:
131
- print(f"Gemini feature extraction failed, falling back to local dictionary-based keyword matching. Error: {e}")
132
- try:
133
- import pandas as pd
134
- import numpy as np
135
- from pathlib import Path
136
-
137
- possible_paths = [
138
- Path(__file__).resolve().parents[2] / "models" / "metadata.parquet",
139
- Path("models/metadata.parquet"),
140
- Path("../models/metadata.parquet"),
141
- ]
142
-
143
- features_dict = set()
144
- for path in possible_paths:
145
- if path.exists():
146
- df = pd.read_parquet(path)
147
- for f_list in df['features']:
148
- if isinstance(f_list, (list, np.ndarray, set, tuple)):
149
- features_dict.update([str(f).strip().lower() for f in f_list])
150
- break
151
-
152
- text_lower = text.lower()
153
- matched = []
154
-
155
- # Sort by length descending to match longer keywords first (e.g. 'deep learning' before 'learning')
156
- for f in sorted(list(features_dict), key=len, reverse=True):
157
- if len(f) > 2:
158
- pattern = r'\b' + re.escape(f) + r'\b'
159
- if re.search(pattern, text_lower):
160
- matched.append(f)
161
- if len(matched) >= 10:
162
- break
163
- print(f"Local fallback matched features: {matched}")
164
- return matched
165
- except Exception as local_err:
166
- print(f"Local feature extraction fallback failed: {local_err}")
167
- return []
168
-
169
-
170
- def parse_features_response(response: str):
171
-
172
- if not response:
173
- return []
174
-
175
- try:
176
-
177
- # remove markdown json blocks
178
- response = response.strip()
179
-
180
- response = re.sub(
181
- r"^```(?:json)?",
182
- "",
183
- response,
184
- flags=re.I
185
- )
186
-
187
- response = re.sub(
188
- r"```$",
189
- "",
190
- response
191
- )
192
-
193
- response = response.strip()
194
-
195
-
196
- response = response.strip()
197
-
198
- start = response.find("{")
199
- end = response.rfind("}")
200
-
201
- if start != -1 and end != -1:
202
- response = response[start:end + 1]
203
-
204
- data = json.loads(response)
205
-
206
- features = data.get(
207
- "features",
208
- []
209
- )
210
-
211
- if not isinstance(features, list):
212
- return []
213
-
214
- cleaned = []
215
-
216
- NORMALIZATION_MAP = {
217
- "computer vision techniques": "computer vision",
218
- "deep learning models": "deep learning",
219
- "deep neural networks": "deep learning",
220
- "machine learning models": "machine learning",
221
- "iot sensor networks": "internet of things",
222
- "sensor networks": "internet of things",
223
- "predictive trends": "predictive analytics",
224
- "operational insights": "analytics"
225
- }
226
-
227
- for x in features:
228
-
229
- feat = str(x).strip().lower()
230
-
231
- feat = NORMALIZATION_MAP.get(
232
- feat,
233
- feat
234
- )
235
-
236
- if not feat:
237
- continue
238
-
239
- if len(feat) < 2:
240
- continue
241
-
242
- cleaned.append(feat)
243
-
244
- cleaned = list(dict.fromkeys(cleaned))
245
-
246
- print("PARSED FEATURES:")
247
- print(cleaned)
248
-
249
- return cleaned[:10]
250
-
251
- except Exception as e:
252
-
253
- print("=" * 50)
254
- print("JSON PARSE ERROR")
255
- print(e)
256
- print("=" * 50)
257
-
258
- print("RAW RESPONSE:")
259
- print(response)
260
-
261
- print("=" * 50)
262
-
263
- return []