godsastray commited on
Commit
ed3f256
·
verified ·
1 Parent(s): 247a8cb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +240 -249
app.py CHANGED
@@ -2,128 +2,148 @@ import gradio as gr
2
  import pandas as pd
3
  import numpy as np
4
  from datetime import datetime
5
- from typing import List, Dict, Tuple
 
6
  import requests
7
  import json
8
  import plotly.graph_objects as go
9
  import random
 
 
 
10
 
11
- class GeminiAPI:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def __init__(self, api_key: str):
13
  self.api_key = api_key
14
  self.headers = {"Content-Type": "application/json"}
 
 
15
 
16
- def analyze_personality(self, responses: List[str]) -> Dict:
 
 
 
 
 
17
  try:
18
- if not responses or len(responses) == 0:
 
19
  raise ValueError("Không có câu trả lời để phân tích")
20
 
21
- prompt = f"""
22
- Dựa trên các câu trả lời sau của người dùng, hãy phân tích và xác định:
23
- 1. Kiểu tính cách MBTI
24
- 2. Điểm mạnh và điểm yếu
25
- 3. Các nghề nghiệp phù hợp nhất
26
- 4. Lộ trình phát triển đề xuất
27
-
28
- Câu trả lời của người dùng:
29
- {json.dumps(responses, ensure_ascii=False)}
30
-
31
- Trả về kết quả theo định dạng JSON với các trường:
32
- {{
33
- "mbti_type": "string",
34
- "strengths": ["string"],
35
- "weaknesses": ["string"],
36
- "recommended_careers": ["string"],
37
- "development_path": ["string"]
38
- }}
39
- """
40
-
41
- payload = {
42
- "contents": [{
43
- "parts": [{
44
- "text": prompt
45
- }]
46
- }]
47
- }
48
-
49
- response = requests.post(
50
- f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key={self.api_key}",
51
- headers=self.headers,
52
- json=payload,
53
- timeout=10
54
- )
55
 
56
- if response.status_code == 200:
57
- result = response.json()
58
- return self._parse_gemini_response(result)
59
- print(f"API Error: {response.status_code} - {response.text}")
60
- return self.get_fallback_analysis()
61
 
62
  except Exception as e:
63
- print(f"Error in analyze_personality: {str(e)}")
64
  return self.get_fallback_analysis()
65
 
66
- def get_career_advice(self, question: str, context: Dict = None) -> str:
67
  try:
68
  if not question.strip():
69
  return "Vui lòng nhập câu hỏi của bạn"
70
 
71
- prompt = f"""
72
- Vai trò: Bạn là một chuyên gia tư vấn nghề nghiệp với nhiều năm kinh nghiệm.
73
-
74
- Thông tin người dùng:
75
- {json.dumps(context, ensure_ascii=False) if context else "Chưa có thông tin"}
76
-
77
- Câu hỏi: {question}
78
-
79
- Hãy tư vấn chi tiết về:
80
- 1. Phân tích vấn đề
81
- 2. Giải pháp cụ thể
82
- 3. Các bước thực hiện
83
- 4. Ví dụ thực tế
84
- """
85
-
86
- payload = {
87
- "contents": [{
88
- "parts": [{
89
- "text": prompt
90
- }]
91
- }]
92
- }
93
-
94
- response = requests.post(
95
- f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key={self.api_key}",
96
- headers=self.headers,
97
- json=payload,
98
- timeout=10
99
- )
100
 
101
- if response.status_code == 200:
102
- result = response.json()
103
- return result['candidates'][0]['content']['parts'][0]['text']
104
- print(f"API Error: {response.status_code} - {response.text}")
105
- return "Xin lỗi, hiện tại tôi không thể xử lý yêu cầu. Vui lòng thử lại sau."
106
 
107
  except Exception as e:
108
- print(f"Error in get_career_advice: {str(e)}")
109
  return "Đã xảy ra lỗi. Vui lòng thử lại sau."
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  def _parse_gemini_response(self, result: Dict) -> Dict:
112
  try:
113
  text_response = result['candidates'][0]['content']['parts'][0]['text']
114
- try:
115
- return json.loads(text_response)
116
- except json.JSONDecodeError as e:
117
- print(f"JSON Decode Error: {e}")
118
- return self.get_fallback_analysis()
119
- except KeyError as e:
120
- print(f"KeyError: {e}")
121
- return self.get_fallback_analysis()
122
- except Exception as e:
123
- print(f"Unexpected Error: {e}")
124
  return self.get_fallback_analysis()
125
-
126
- def get_fallback_analysis(self) -> Dict:
 
127
  return {
128
  "mbti_type": "ENTJ",
129
  "strengths": [
@@ -154,12 +174,14 @@ class GeminiAPI:
154
  class DataProcessor:
155
  @staticmethod
156
  def validate_responses(responses: List[str]) -> bool:
 
157
  if not responses:
158
  return False
159
  return all(isinstance(response, str) and response.strip() for response in responses)
160
 
161
  @staticmethod
162
  def calculate_scores(responses: List[str]) -> Dict[str, float]:
 
163
  if not responses:
164
  return {}
165
 
@@ -170,62 +192,62 @@ class DataProcessor:
170
  'J-P': 0 # Judging-Perceiving
171
  }
172
 
 
 
 
 
 
 
 
173
  for i, response in enumerate(responses):
174
- response = response.lower().strip()
175
- if i == 0: # E-I
176
- dimensions['E-I'] += 1 if "nhóm" in response else -1
177
- elif i == 1: # S-N
178
- dimensions['S-N'] += 1 if "dữ liệu" in response or "logic" in response else -1
179
- elif i == 2: # T-F
180
- dimensions['T-F'] += 1 if "quy trình" in response or "chặt chẽ" in response else -1
181
- elif i == 3: # J-P
182
- dimensions['J-P'] += 1 if "lý thuyết" in response or "khái niệm" in response else -1
183
 
184
  return dimensions
185
 
186
  class CareerGuidanceSystem:
187
  def __init__(self, api_key: str):
188
- self.gemini = GeminiAPI(api_key)
189
  self.data_processor = DataProcessor()
190
- self.current_analysis = None
191
- self.chat_history = []
192
 
193
  self.personality_questions = [
194
- {
195
- "question": "Bạn thích làm việc một mình hay theo nhóm?",
196
- "choices": ["Thích làm việc một mình", "Thích làm việc nhóm"],
197
- "required": True
198
- },
199
- {
200
- "question": "Khi giải quyết vấn đề, bạn thường:",
201
- "choices": ["Dựa vào dữ liệu và logic", "Dựa vào trực giác và cảm nhận"],
202
- "required": True
203
- },
204
- {
205
- "question": "Trong công việc, bạn ưu tiên:",
206
- "choices": ["Tuân thủ quy trình chặt chẽ", "Linh hoạt và sáng tạo"],
207
- "required": True
208
- },
209
- {
210
- "question": "Bạn học hỏi tốt nhất thông qua:",
211
- "choices": ["Lý thuyết và khái niệm", "Thực hành và trải nghiệm"],
212
- "required": True
213
- },
214
- {
215
- "question": "Trong tình huống mới, bạn thường:",
216
- "choices": ["Lập kế hoạch chi tiết trước", "Ứng biến theo tình huống"],
217
- "required": True
218
- }
219
  ]
220
 
221
  def analyze_user_responses(self, responses: List[str]) -> Dict:
 
222
  if not self.data_processor.validate_responses(responses):
223
  raise ValueError("Vui lòng trả lời tất cả các câu hỏi")
224
 
225
- self.current_analysis = self.gemini.analyze_personality(responses)
 
226
  return self.current_analysis
227
 
228
  def create_personality_report(self, analysis: Dict) -> str:
 
229
  return f"""
230
  # Báo cáo Phân tích Tính cách và Định hướng Nghề nghiệp
231
 
@@ -238,19 +260,34 @@ class CareerGuidanceSystem:
238
  {chr(10).join(['- ' + w for w in analysis['weaknesses']])}
239
 
240
  ## Nghề nghiệp phù hợp:
241
- {chr(10).join(['1. ' + c for c in analysis['recommended_careers']])}
242
 
243
  ## Lộ trình phát triển đề xuất:
244
- {chr(10).join(['1. ' + d for d in analysis['development_path']])}
245
 
246
  Báo cáo được tạo ngày: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
247
  """
248
-
249
  def get_advice(self, question: str) -> str:
250
- return self.gemini.get_career_advice(question, self.current_analysis)
 
251
 
252
- def create_interface():
253
- with gr.Blocks(theme=gr.themes.Soft()) as interface:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  gr.Markdown("""
255
  # 🎯 Hệ thống Tư vấn Hướng nghiệp AI
256
 
@@ -258,12 +295,11 @@ def create_interface():
258
  1. Nhập API key của bạn
259
  2. Trao đổi với AI để được tư vấn về tính cách, nghề nghiệp, và lộ trình phát triển
260
  """)
261
-
262
- # Tab Nhập API Key và Tư vấn Chi tiết
263
  with gr.Tab("🔑 & 💬 Nhập API Key & Tư vấn Chi tiết"):
264
  with gr.Row():
265
  with gr.Column(scale=1):
266
- gr.Markdown("### Nhập API Key của bạn")
267
  api_key_input = gr.Textbox(
268
  label="API Key",
269
  placeholder="Dán API Key ở đây",
@@ -272,12 +308,10 @@ def create_interface():
272
  )
273
 
274
  with gr.Column(scale=2):
275
- gr.Markdown("### Trò chuyện với Chuyên gia Tư vấn AI")
276
  chat_history = gr.Chatbot(
277
  label="Cuộc trao đổi",
278
  height=400,
279
- avatar_images=["user.png", "advisor.png"],
280
- type="messages"
281
  )
282
 
283
  with gr.Row():
@@ -289,129 +323,86 @@ def create_interface():
289
  send_btn = gr.Button("📤 Gửi", variant="primary", scale=1)
290
 
291
  clear_btn = gr.Button("🗑️ Xóa lịch sử")
292
-
293
- # Tab Phân tích Tính Cách
 
 
 
294
  with gr.Tab("📈 Phân tích Tính Cách"):
295
- gr.Markdown("### Phân tích Tính Cách")
296
  with gr.Row():
297
  with gr.Column(scale=1):
298
- gr.Markdown("#### Câu hỏi về Tính Cách")
299
- personality_questions = CareerGuidanceSystem("").personality_questions
300
  radio_buttons = []
301
- for i, q in enumerate(personality_questions):
302
- radio = gr.Radio(label=f"{i+1}. {q['question']}", choices=q["choices"], interactive=True)
 
 
 
 
 
303
  radio_buttons.append(radio)
 
304
  with gr.Column(scale=2):
305
- gr.Markdown("#### Kết quả Phân tích")
306
  personality_report = gr.Markdown("Kết quả sẽ hiển thị ở đây.")
307
 
308
  analyze_btn = gr.Button("Phân tích", variant="primary")
309
 
310
- def analyze_personality(api_key, *responses):
311
- if not api_key.strip():
312
- return "⚠️ Vui lòng nhập API key trước.", ""
313
-
314
- career_system = CareerGuidanceSystem(api_key)
315
- try:
316
- analysis = career_system.analyze_user_responses(list(responses))
317
- report = career_system.create_personality_report(analysis)
318
- return "", report
319
- except ValueError as ve:
320
- return str(ve), ""
321
- except Exception as e:
322
- return f"⚠️ Có lỗi xảy ra: {str(e)}", ""
323
-
324
- analyze_btn.click(
325
- analyze_personality,
326
- inputs=[api_key_input] + radio_buttons,
327
- outputs=[gr.Markdown(), personality_report]
328
- )
329
-
330
- # Event handlers
331
- def send_message(api_key, history, message):
332
  if not message.strip():
333
- return history, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  try:
336
- if not api_key.strip():
337
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "⚠️ Vui lòng nhập API key trước."}], ""
338
-
339
- career_system = CareerGuidanceSystem(api_key)
340
-
341
- # Kiểm tra xem người dùng có muốn phân tích tính cách hay không
342
- if "tính cách" in message.lower() or "phân tích" in message.lower():
343
- questions = career_system.personality_questions
344
- question_texts = [q["question"] for q in questions]
345
- response_text = "Để phân tích tính cách của bạn, vui lòng trả lời các câu hỏi sau:\n\n"
346
- response_text += "\n".join([f"{i+1}. {q}" for i, q in enumerate(question_texts)])
347
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": response_text}], ""
348
-
349
- # Kiểm tra xem người dùng có trả lời các câu hỏi về tính cách hay không
350
- if any(str(i+1) + ". " in message for i in range(len(career_system.personality_questions))):
351
- responses = []
352
- for i in range(len(career_system.personality_questions)):
353
- question = career_system.personality_questions[i]["question"]
354
- for line in message.split("\n"):
355
- if f"{i+1}. " in line:
356
- response = line.split(f"{i+1}. ")[1].strip()
357
- responses.append(response)
358
- break
359
- if len(responses) != len(career_system.personality_questions):
360
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "⚠️ Bạn chưa trả lời đầy đủ các câu hỏi. Vui lòng trả lời lại."}], ""
361
-
362
- # Phân tích tính cách
363
- analysis = career_system.analyze_user_responses(responses)
364
- report = career_system.create_personality_report(analysis)
365
-
366
- # Hiển thị báo cáo trong cuộc trò chuyện
367
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": report}], ""
368
-
369
- # Kiểm tra xem người dùng có cung cấp kiểu tính cách cụ thể hay không
370
- if "lộ trình phát triển" in message.lower() and "kiểu tính cách" in message.lower():
371
- mbti_type = None
372
- for line in message.split():
373
- if line.upper() in ["ENTJ", "INTJ", "ENFJ", "INFJ", "ENTP", "INTP", "ENFP", "INFP",
374
- "ESTJ", "ISTJ", "ESFJ", "ISFJ", "ESTP", "ISTP", "ESFP", "ISFP"]:
375
- mbti_type = line.upper()
376
- break
377
-
378
- if mbti_type:
379
- question = f"Lộ trình phát triển nghề nghiệp cho kiểu tính cách: {mbti_type}"
380
- advice = career_system.get_advice(question)
381
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": advice}], ""
382
- else:
383
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "⚠️ Vui lòng cung cấp kiểu tính cách hợp lệ (ví dụ: ENTJ)." }], ""
384
-
385
- # Nếu không phải phân tích tính cách, gửi câu hỏi tới AI
386
- advice = career_system.get_advice(message)
387
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": advice}], ""
388
  except Exception as e:
389
- print(f"Error in send_message: {str(e)}")
390
- return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "⚠️ Có lỗi xảy ra khi gửi tin nhắn"}], ""
 
 
 
 
 
391
 
392
- def clear_chat():
393
  return [], ""
394
 
395
- send_btn.click(
396
- send_message,
397
- inputs=[api_key_input, chat_history, msg],
398
- outputs=[chat_history, msg]
399
- )
400
-
401
- msg.submit(
402
- send_message,
403
- inputs=[api_key_input, chat_history, msg],
404
- outputs=[chat_history, msg]
405
- )
406
-
407
- clear_btn.click(
408
- clear_chat,
409
- inputs=[],
410
- outputs=[chat_history, msg]
411
- )
412
 
413
- return interface
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
- if __name__ == "__main__":
416
- interface = create_interface()
417
- interface.launch()
 
2
  import pandas as pd
3
  import numpy as np
4
  from datetime import datetime
5
+ from typing import List, Dict, Tuple, Optional
6
+ from dataclasses import dataclass
7
  import requests
8
  import json
9
  import plotly.graph_objects as go
10
  import random
11
+ from abc import ABC, abstractmethod
12
+ import logging
13
+ from functools import lru_cache
14
 
15
+ # Configure logging
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
19
+ )
20
+ logger = logging.getLogger(__name__)
21
+
22
+ @dataclass
23
+ class PersonalityQuestion:
24
+ question: str
25
+ choices: List[str]
26
+ required: bool = True
27
+
28
+ class APIClient(ABC):
29
+ @abstractmethod
30
+ def analyze_personality(self, responses: List[str]) -> Dict:
31
+ pass
32
+
33
+ @abstractmethod
34
+ def get_career_advice(self, question: str, context: Optional[Dict] = None) -> str:
35
+ pass
36
+
37
+ class GeminiAPI(APIClient):
38
  def __init__(self, api_key: str):
39
  self.api_key = api_key
40
  self.headers = {"Content-Type": "application/json"}
41
+ self.base_url = "https://generativelanguage.googleapis.com/v1beta"
42
+ self.model = "gemini-1.5-flash-latest"
43
 
44
+ @lru_cache(maxsize=100)
45
+ def analyze_personality(self, responses: Tuple[str]) -> Dict:
46
+ """
47
+ Analyze personality based on user responses.
48
+ Using tuple as input for lru_cache compatibility.
49
+ """
50
  try:
51
+ responses_list = list(responses)
52
+ if not responses_list:
53
  raise ValueError("Không có câu trả lời để phân tích")
54
 
55
+ prompt = self._create_personality_prompt(responses_list)
56
+ result = self._make_api_request(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ return self._parse_gemini_response(result)
 
 
 
 
59
 
60
  except Exception as e:
61
+ logger.error(f"Error in analyze_personality: {str(e)}")
62
  return self.get_fallback_analysis()
63
 
64
+ def get_career_advice(self, question: str, context: Optional[Dict] = None) -> str:
65
  try:
66
  if not question.strip():
67
  return "Vui lòng nhập câu hỏi của bạn"
68
 
69
+ prompt = self._create_advice_prompt(question, context)
70
+ result = self._make_api_request(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ return result['candidates'][0]['content']['parts'][0]['text']
 
 
 
 
73
 
74
  except Exception as e:
75
+ logger.error(f"Error in get_career_advice: {str(e)}")
76
  return "Đã xảy ra lỗi. Vui lòng thử lại sau."
77
 
78
+ def _make_api_request(self, prompt: str) -> Dict:
79
+ payload = {
80
+ "contents": [{
81
+ "parts": [{
82
+ "text": prompt
83
+ }]
84
+ }]
85
+ }
86
+
87
+ response = requests.post(
88
+ f"{self.base_url}/models/{self.model}:generateContent?key={self.api_key}",
89
+ headers=self.headers,
90
+ json=payload,
91
+ timeout=10
92
+ )
93
+
94
+ if response.status_code != 200:
95
+ logger.error(f"API Error: {response.status_code} - {response.text}")
96
+ raise Exception(f"API request failed with status {response.status_code}")
97
+
98
+ return response.json()
99
+
100
+ def _create_personality_prompt(self, responses: List[str]) -> str:
101
+ return f"""
102
+ Dựa trên các câu trả lời sau của người dùng, hãy phân tích và xác định:
103
+ 1. Kiểu tính cách MBTI
104
+ 2. Điểm mạnh và điểm yếu
105
+ 3. Các nghề nghiệp phù hợp nhất
106
+ 4. Lộ trình phát triển đề xuất
107
+
108
+ Câu trả lời của người dùng:
109
+ {json.dumps(responses, ensure_ascii=False)}
110
+
111
+ Trả về kết quả theo định dạng JSON với các trường:
112
+ {{
113
+ "mbti_type": "string",
114
+ "strengths": ["string"],
115
+ "weaknesses": ["string"],
116
+ "recommended_careers": ["string"],
117
+ "development_path": ["string"]
118
+ }}
119
+ """
120
+
121
+ def _create_advice_prompt(self, question: str, context: Optional[Dict]) -> str:
122
+ return f"""
123
+ Vai trò: Bạn là một chuyên gia tư vấn nghề nghiệp với nhiều năm kinh nghiệm.
124
+
125
+ Thông tin người dùng:
126
+ {json.dumps(context, ensure_ascii=False) if context else "Chưa có thông tin"}
127
+
128
+ Câu hỏi: {question}
129
+
130
+ Hãy tư vấn chi tiết về:
131
+ 1. Phân tích vấn đề
132
+ 2. Giải pháp cụ thể
133
+ 3. Các bước thực hiện
134
+ 4. Ví dụ thực tế
135
+ """
136
+
137
  def _parse_gemini_response(self, result: Dict) -> Dict:
138
  try:
139
  text_response = result['candidates'][0]['content']['parts'][0]['text']
140
+ return json.loads(text_response)
141
+ except (json.JSONDecodeError, KeyError) as e:
142
+ logger.error(f"Error parsing response: {str(e)}")
 
 
 
 
 
 
 
143
  return self.get_fallback_analysis()
144
+
145
+ @staticmethod
146
+ def get_fallback_analysis() -> Dict:
147
  return {
148
  "mbti_type": "ENTJ",
149
  "strengths": [
 
174
  class DataProcessor:
175
  @staticmethod
176
  def validate_responses(responses: List[str]) -> bool:
177
+ """Validate user responses"""
178
  if not responses:
179
  return False
180
  return all(isinstance(response, str) and response.strip() for response in responses)
181
 
182
  @staticmethod
183
  def calculate_scores(responses: List[str]) -> Dict[str, float]:
184
+ """Calculate personality dimension scores"""
185
  if not responses:
186
  return {}
187
 
 
192
  'J-P': 0 # Judging-Perceiving
193
  }
194
 
195
+ scoring_rules = {
196
+ 0: ('nhóm', 'E-I'),
197
+ 1: ('dữ liệu|logic', 'S-N'),
198
+ 2: ('quy trình|chặt chẽ', 'T-F'),
199
+ 3: ('lý thuyết|khái niệm', 'J-P')
200
+ }
201
+
202
  for i, response in enumerate(responses):
203
+ if i in scoring_rules:
204
+ keywords, dimension = scoring_rules[i]
205
+ matches_keyword = any(kw in response.lower() for kw in keywords.split('|'))
206
+ dimensions[dimension] += 1 if matches_keyword else -1
 
 
 
 
 
207
 
208
  return dimensions
209
 
210
  class CareerGuidanceSystem:
211
  def __init__(self, api_key: str):
212
+ self.api_client = GeminiAPI(api_key)
213
  self.data_processor = DataProcessor()
214
+ self.current_analysis: Optional[Dict] = None
215
+ self.chat_history: List[Dict] = []
216
 
217
  self.personality_questions = [
218
+ PersonalityQuestion(
219
+ "Bạn thích làm việc một mình hay theo nhóm?",
220
+ ["Thích làm việc một mình", "Thích làm việc nhóm"]
221
+ ),
222
+ PersonalityQuestion(
223
+ "Khi giải quyết vấn đề, bạn thường:",
224
+ ["Dựa vào dữ liệu và logic", "Dựa vào trực giác cảm nhận"]
225
+ ),
226
+ PersonalityQuestion(
227
+ "Trong công việc, bạn ưu tiên:",
228
+ ["Tuân thủ quy trình chặt chẽ", "Linh hoạt và sáng tạo"]
229
+ ),
230
+ PersonalityQuestion(
231
+ "Bạn học hỏi tốt nhất thông qua:",
232
+ ["Lý thuyết và khái niệm", "Thực hành và trải nghiệm"]
233
+ ),
234
+ PersonalityQuestion(
235
+ "Trong tình huống mới, bạn thường:",
236
+ ["Lập kế hoạch chi tiết trước", "Ứng biến theo tình huống"]
237
+ )
 
 
 
 
 
238
  ]
239
 
240
  def analyze_user_responses(self, responses: List[str]) -> Dict:
241
+ """Analyze user responses and update current analysis"""
242
  if not self.data_processor.validate_responses(responses):
243
  raise ValueError("Vui lòng trả lời tất cả các câu hỏi")
244
 
245
+ # Convert list to tuple for caching
246
+ self.current_analysis = self.api_client.analyze_personality(tuple(responses))
247
  return self.current_analysis
248
 
249
  def create_personality_report(self, analysis: Dict) -> str:
250
+ """Create a formatted personality analysis report"""
251
  return f"""
252
  # Báo cáo Phân tích Tính cách và Định hướng Nghề nghiệp
253
 
 
260
  {chr(10).join(['- ' + w for w in analysis['weaknesses']])}
261
 
262
  ## Nghề nghiệp phù hợp:
263
+ {chr(10).join([f'1. {c}' for c in analysis['recommended_careers']])}
264
 
265
  ## Lộ trình phát triển đề xuất:
266
+ {chr(10).join([f'1. {d}' for d in analysis['development_path']])}
267
 
268
  Báo cáo được tạo ngày: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
269
  """
270
+
271
  def get_advice(self, question: str) -> str:
272
+ """Get career advice based on question and current analysis"""
273
+ return self.api_client.get_career_advice(question, self.current_analysis)
274
 
275
+ class GradioInterface:
276
+ def __init__(self):
277
+ self.interface = self.create_interface()
278
+ self.system = None # CareerGuidanceSystem object will be initialized with API key
279
+
280
+ def create_interface(self) -> gr.Blocks:
281
+ with gr.Blocks(theme=gr.themes.Soft()) as interface:
282
+ self._create_header()
283
+
284
+ with gr.Tabs():
285
+ self._create_chat_tab()
286
+ self._create_personality_tab()
287
+
288
+ return interface
289
+
290
+ def _create_header(self):
291
  gr.Markdown("""
292
  # 🎯 Hệ thống Tư vấn Hướng nghiệp AI
293
 
 
295
  1. Nhập API key của bạn
296
  2. Trao đổi với AI để được tư vấn về tính cách, nghề nghiệp, và lộ trình phát triển
297
  """)
298
+
299
+ def _create_chat_tab(self):
300
  with gr.Tab("🔑 & 💬 Nhập API Key & Tư vấn Chi tiết"):
301
  with gr.Row():
302
  with gr.Column(scale=1):
 
303
  api_key_input = gr.Textbox(
304
  label="API Key",
305
  placeholder="Dán API Key ở đây",
 
308
  )
309
 
310
  with gr.Column(scale=2):
 
311
  chat_history = gr.Chatbot(
312
  label="Cuộc trao đổi",
313
  height=400,
314
+ avatar_images=["user.png", "advisor.png"]
 
315
  )
316
 
317
  with gr.Row():
 
323
  send_btn = gr.Button("📤 Gửi", variant="primary", scale=1)
324
 
325
  clear_btn = gr.Button("🗑️ Xóa lịch sử")
326
+
327
+ # Add event handlers
328
+ self._add_chat_handlers(api_key_input, chat_history, msg, send_btn, clear_btn)
329
+
330
+ def _create_personality_tab(self):
331
  with gr.Tab("📈 Phân tích Tính Cách"):
 
332
  with gr.Row():
333
  with gr.Column(scale=1):
 
 
334
  radio_buttons = []
335
+ system = CareerGuidanceSystem("") # Temporarily use an empty API key
336
+ for i, q in enumerate(system.personality_questions):
337
+ radio = gr.Radio(
338
+ label=f"{i+1}. {q.question}",
339
+ choices=q.choices,
340
+ interactive=True
341
+ )
342
  radio_buttons.append(radio)
343
+
344
  with gr.Column(scale=2):
 
345
  personality_report = gr.Markdown("Kết quả sẽ hiển thị ở đây.")
346
 
347
  analyze_btn = gr.Button("Phân tích", variant="primary")
348
 
349
+ # Add personality analysis handler
350
+ self._add_personality_handlers(analyze_btn, radio_buttons, personality_report)
351
+
352
+ def _add_chat_handlers(self, api_key_input, chat_history, msg, send_btn, clear_btn):
353
+ def send_message(api_key: str, history: List[Dict], message: str) -> Tuple[List[Dict], str]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  if not message.strip():
355
+ return history, "Vui lòng nhập câu hỏi của bạn."
356
+
357
+ if not api_key.strip():
358
+ return history, "Vui lòng nhập API Key."
359
+
360
+ if not self.system or self.system.api_client.api_key != api_key:
361
+ self.system = CareerGuidanceSystem(api_key)
362
+
363
+ user_message = {
364
+ "type": "user",
365
+ "text": message
366
+ }
367
+
368
+ history.append(user_message)
369
 
370
  try:
371
+ response = self.system.get_advice(message)
372
+ advisor_message = {
373
+ "type": "advisor",
374
+ "text": response
375
+ }
376
+ history.append(advisor_message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  except Exception as e:
378
+ advisor_message = {
379
+ "type": "advisor",
380
+ "text": f"Đã xảy ra lỗi: {str(e)}"
381
+ }
382
+ history.append(advisor_message)
383
+
384
+ return history, ""
385
 
386
+ def clear_history():
387
  return [], ""
388
 
389
+ send_btn.click(send_message, inputs=[api_key_input, chat_history, msg], outputs=[chat_history, msg])
390
+ clear_btn.click(clear_history, outputs=[chat_history, msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
 
392
+ def _add_personality_handlers(self, analyze_btn, radio_buttons, personality_report):
393
+ def analyze_personality(*responses) -> str:
394
+ if not all(responses):
395
+ return "Vui lòng trả lời tất cả các câu hỏi."
396
+
397
+ try:
398
+ analysis = self.system.analyze_user_responses(responses)
399
+ report = self.system.create_personality_report(analysis)
400
+ return report
401
+ except Exception as e:
402
+ return f"Đã xảy ra lỗi: {str(e)}"
403
+
404
+ analyze_btn.click(analyze_personality, inputs=radio_buttons, outputs=personality_report)
405
 
406
+ # Khởi tạo giao diện
407
+ iface = GradioInterface()
408
+ iface.interface.launch()