aidlmrza commited on
Commit
c87c1bd
·
verified ·
1 Parent(s): c5efeb6

Upload llm_engine.py

Browse files
Files changed (1) hide show
  1. app/services/llm_engine.py +75 -32
app/services/llm_engine.py CHANGED
@@ -8,11 +8,56 @@ load_dotenv()
8
 
9
  class LLMEngine:
10
  def __init__(self):
11
- self.api_key = os.getenv("GROQ_API_KEY")
12
- if not self.api_key:
13
- raise ValueError("GROQ_API_KEY not found in .env file")
14
 
15
- self.client = AsyncGroq(api_key=self.api_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  async def process_user_intent(self, user_text: str, available_skills: list):
18
  # Ubah list skill jadi string biar AI tau menu apa aja yang ada
@@ -53,27 +98,26 @@ class LLMEngine:
53
  """
54
 
55
  try:
56
- chat_completion = await self.client.chat.completions.create(
 
57
  messages=[
58
  {"role": "system", "content": system_prompt},
59
  {"role": "user", "content": user_text}
60
  ],
61
- model="llama-3.3-70b-versatile", # Wajib model 70b biar pinter mapping
62
- temperature=0.0, # Wajib 0 agar tidak kreatif/halu
63
  response_format={"type": "json_object"}
64
  )
65
-
66
- response_content = chat_completion.choices[0].message.content
67
- print(f"DEBUG AI MAPPING: {response_content}") # Cek di terminal mappingnya bener gak
68
  return json.loads(response_content)
69
  except Exception as e:
70
- print(f"Error Intent: {e}")
71
  return {"action": "CASUAL_CHAT", "detected_skills": []}
72
 
73
  async def generate_question(self, topics: list, level: str):
74
  topics_str = ", ".join(topics)
75
  prompt = f"""
76
- Buatkan 1 soal esai pendek untuk menguji pemahaman user tentang topik: {topics_str}.
 
77
  Tingkat Kesulitan: {level}.
78
  Bahasa: Indonesia.
79
 
@@ -87,15 +131,15 @@ class LLMEngine:
87
  }}
88
  """
89
  try:
90
- completion = await self.client.chat.completions.create(
91
  messages=[{"role": "user", "content": prompt}],
92
- # UPDATE MODEL
93
- model="llama-3.3-70b-versatile",
94
  response_format={"type": "json_object"}
95
  )
96
- return json.loads(completion.choices[0].message.content)
97
- except:
98
- return None
99
 
100
  async def evaluate_answer(self, user_answer: str, question_context: dict):
101
  prompt = f"""
@@ -117,13 +161,14 @@ class LLMEngine:
117
  }}
118
  """
119
  try:
120
- completion = await self.client.chat.completions.create(
121
  messages=[{"role": "user", "content": prompt}],
122
- # UPDATE MODEL
123
- model="llama-3.3-70b-versatile",
124
  response_format={"type": "json_object"}
125
  )
126
- return json.loads(completion.choices[0].message.content)
 
 
127
  except:
128
  return {"score": 0, "feedback": "Error menilai.", "is_correct": False}
129
 
@@ -140,7 +185,7 @@ class LLMEngine:
140
  2. Fokus jawabanmu HANYA pada keyword tersebut.
141
  3. Gunakan analogi sederhana jika perlu. Jangan terlalu kaku seperti buku teks, tapi tetap akurat.
142
  4. Gaya bahasa: Ramah, Suportif, Mentor IT.
143
-
144
  """
145
  else:
146
  # SKENARIO 2: Keyword Tidak Ditemukan (TOLAK)
@@ -150,9 +195,9 @@ class LLMEngine:
150
 
151
  TUGAS:
152
  1. **TOLAK** untuk menjawab pertanyaan ini.
153
- 2. Katakan dengan sopan: "Maaf, topik ini tidak ada dalam database skill yang saya pelajari."
154
  3. JANGAN mencoba menjawab atau menebak, meskipun kamu tahu jawabannya secara umum. Patuhi whitelist dataset.
155
- 4. Tawarkan user untuk bertanya tentang skill teknis pemrograman (seperti Python, Java, SQL, dll).
156
  """
157
 
158
  prompt_template = f"""
@@ -183,12 +228,11 @@ class LLMEngine:
183
  messages.append({"role": "user", "content": user_text})
184
 
185
  try:
186
- completion = await self.client.chat.completions.create(
187
  messages=messages,
188
  model="llama-3.3-70b-versatile",
189
- temperature=0.3
190
  )
191
- return completion.choices[0].message.content
192
  except Exception as e:
193
  return f"Maaf, otak saya sedang error. (Error: {str(e)})"
194
 
@@ -218,13 +262,12 @@ class LLMEngine:
218
  """
219
 
220
  try:
221
- chat_completion = self.client.chat.completions.create(
222
  messages=[{"role": "user", "content": prompt}],
223
  model="llama-3.1-8b-instant",
224
  temperature=0.7
225
  )
226
- return chat_completion.choices[0].message.content
227
- except Exception as e:
228
- return f"Berdasarkan jawabanmu, kamu sangat cocok menjadi {role}! Semangat belajar ya! 🚀"
229
 
230
  llm_engine = LLMEngine()
 
8
 
9
  class LLMEngine:
10
  def __init__(self):
11
+ # List untuk menampung client AsyncGroq
12
+ self.clients = []
 
13
 
14
+ # 1. Load Token Utama
15
+ key1 = os.getenv("GROQ_API_KEY")
16
+ if key1:
17
+ try:
18
+ self.clients.append(AsyncGroq(api_key=key1))
19
+ except Exception as e:
20
+ print(f"⚠️ Gagal memuat Token Utama: {e}")
21
+
22
+ # 2. Load Token Backup
23
+ key2 = os.getenv("GROQ_API_KEY_BACKUP")
24
+ if key2:
25
+ try:
26
+ self.clients.append(AsyncGroq(api_key=key2))
27
+ except Exception as e:
28
+ print(f"⚠️ Gagal memuat Token Backup: {e}")
29
+
30
+ print(f"✅ LLM Engine (Async) siap dengan {len(self.clients)} Client aktif.")
31
+
32
+ # --- FUNGSI RETRY (VERSI ASYNC) ---
33
+ async def _execute_with_retry(self, messages, model, temperature=0.5, response_format=None):
34
+ """
35
+ Mencoba request Async secara bergantian.
36
+ """
37
+ if not self.clients:
38
+ raise Exception("Tidak ada API Key Groq yang terdeteksi di .env!")
39
+
40
+ last_error = Exception("Unknown Error")
41
+
42
+ for i, client in enumerate(self.clients):
43
+ try:
44
+ # PERUBAHAN PENTING: Pakai 'await' di sini
45
+ completion = await client.chat.completions.create(
46
+ messages=messages,
47
+ model=model,
48
+ temperature=temperature,
49
+ response_format=response_format
50
+ )
51
+ return completion.choices[0].message.content
52
+
53
+ except Exception as e:
54
+ print(f"⚠️ Token ke-{i+1} Gagal. Error: {e}")
55
+ last_error = e
56
+ # Lanjut ke client berikutnya...
57
+ continue
58
+
59
+ print("❌ Semua Token Gagal/Habis.")
60
+ raise last_error
61
 
62
  async def process_user_intent(self, user_text: str, available_skills: list):
63
  # Ubah list skill jadi string biar AI tau menu apa aja yang ada
 
98
  """
99
 
100
  try:
101
+ # Panggil retry dengan await
102
+ response_content = await self._execute_with_retry(
103
  messages=[
104
  {"role": "system", "content": system_prompt},
105
  {"role": "user", "content": user_text}
106
  ],
107
+ model="llama-3.3-70b-versatile",
108
+ temperature=0.0,
109
  response_format={"type": "json_object"}
110
  )
 
 
 
111
  return json.loads(response_content)
112
  except Exception as e:
113
+ print(f"Error Router: {e}")
114
  return {"action": "CASUAL_CHAT", "detected_skills": []}
115
 
116
  async def generate_question(self, topics: list, level: str):
117
  topics_str = ", ".join(topics)
118
  prompt = f"""
119
+ Buatkan 1 soal esai pendek dengan konsep how, what, why untuk menguji pemahaman user
120
+ Tentang topik: {topics_str}.
121
  Tingkat Kesulitan: {level}.
122
  Bahasa: Indonesia.
123
 
 
131
  }}
132
  """
133
  try:
134
+ response_content = await self._execute_with_retry(
135
  messages=[{"role": "user", "content": prompt}],
136
+ model="llama-3.3-8b-instant",
137
+ temperature=0.5,
138
  response_format={"type": "json_object"}
139
  )
140
+ return json.loads(response_content)
141
+ except Exception as e:
142
+ return {"question_text": "Error generate soal.", "grading_rubric": {}}
143
 
144
  async def evaluate_answer(self, user_answer: str, question_context: dict):
145
  prompt = f"""
 
161
  }}
162
  """
163
  try:
164
+ response_content = await self._execute_with_retry(
165
  messages=[{"role": "user", "content": prompt}],
166
+ model="llama-3.3-70b-versatile", # Tetap pakai 70b biar penilaian akurat
 
167
  response_format={"type": "json_object"}
168
  )
169
+
170
+ # Parsing string JSON menjadi Dictionary Python
171
+ return json.loads(response_content)
172
  except:
173
  return {"score": 0, "feedback": "Error menilai.", "is_correct": False}
174
 
 
185
  2. Fokus jawabanmu HANYA pada keyword tersebut.
186
  3. Gunakan analogi sederhana jika perlu. Jangan terlalu kaku seperti buku teks, tapi tetap akurat.
187
  4. Gaya bahasa: Ramah, Suportif, Mentor IT.
188
+ 5. Giring user untuk menggunakan fitur belajar seperti tanya tentang skill teknis, Ujian/Tes sub skill, cek progres, rekomendasi belajar.
189
  """
190
  else:
191
  # SKENARIO 2: Keyword Tidak Ditemukan (TOLAK)
 
195
 
196
  TUGAS:
197
  1. **TOLAK** untuk menjawab pertanyaan ini.
198
+ 2. Katakan dengan sopan seperti: "Maaf, topik ini tidak ada dalam database skill yang saya pelajari."
199
  3. JANGAN mencoba menjawab atau menebak, meskipun kamu tahu jawabannya secara umum. Patuhi whitelist dataset.
200
+ 5. Tawarkan user untuk menggunakan fitur belajar seperti tanya tentang skill teknis, Ujian/Tes sub skill, cek progres, rekomendasi belajar.
201
  """
202
 
203
  prompt_template = f"""
 
228
  messages.append({"role": "user", "content": user_text})
229
 
230
  try:
231
+ return await self._execute_with_retry(
232
  messages=messages,
233
  model="llama-3.3-70b-versatile",
234
+ temperature=0.3
235
  )
 
236
  except Exception as e:
237
  return f"Maaf, otak saya sedang error. (Error: {str(e)})"
238
 
 
262
  """
263
 
264
  try:
265
+ return await self._execute_with_retry(
266
  messages=[{"role": "user", "content": prompt}],
267
  model="llama-3.1-8b-instant",
268
  temperature=0.7
269
  )
270
+ except:
271
+ return f"Kamu cocok jadi {role}!"
 
272
 
273
  llm_engine = LLMEngine()