Famazo commited on
Commit
3ba2a7e
·
1 Parent(s): d7535dd

Update backend/api.py

Browse files
Files changed (1) hide show
  1. backend/api.py +64 -32
backend/api.py CHANGED
@@ -3,16 +3,17 @@ from fastapi.middleware.cors import CORSMiddleware
3
  from pydantic import BaseModel
4
  from transformers import AutoTokenizer
5
  import onnxruntime as ort
6
- import torch
7
  import pandas as pd
8
  from pathlib import Path
 
9
 
10
  app = FastAPI()
11
 
12
  # === CORS untuk frontend di Vercel ===
13
  app.add_middleware(
14
  CORSMiddleware,
15
- allow_origins=["*"],
16
  allow_credentials=True,
17
  allow_methods=["*"],
18
  allow_headers=["*"],
@@ -20,27 +21,47 @@ app.add_middleware(
20
 
21
  # === Path setup ===
22
  BASE_DIR = Path(__file__).resolve().parent
 
 
 
23
  MODEL_PATH = BASE_DIR / "models" / "bert_chatbot.onnx"
24
- TOKENIZER_PATH = BASE_DIR / "models" / "bert-base-multilingual-cased"
25
  DATASET_PATH = BASE_DIR / "dataset_chatbot_template.xlsx"
26
 
27
- # === Load tokenizer dan model ===
28
- print("🚀 Loading ONNX model...")
29
- tokenizer = AutoTokenizer.from_pretrained(str(TOKENIZER_PATH))
30
- session = ort.InferenceSession(str(MODEL_PATH), providers=["CPUExecutionProvider"])
31
- print("✅ ONNX model loaded!")
32
 
33
- # === Load dataset (optional) ===
34
  try:
 
 
 
 
 
 
 
 
 
35
  df_jawaban = pd.read_excel(DATASET_PATH)
36
- except Exception:
37
- df_jawaban = pd.DataFrame(columns=["Intent", "Jawaban_ID"])
 
 
 
 
 
 
 
 
 
38
 
39
  # === Default responses ===
40
  responses = {
41
  "about_me": "I am a passionate developer specializing in AI and web development.",
42
  "skills": "My main skills are HTML5, CSS3, JavaScript, Laravel, Node.js, TensorFlow, and PyTorch.",
43
- "projects": "Some of my projects include Bald Detection and Portfolio Website.",
44
  "experience": "I have worked as IT Support, AI Engineer, and Freelancer.",
45
  "career_goal": "My career goal is to become a Full Stack Developer and ML Engineer.",
46
  "greeting": "Hello! How can I help you regarding this portfolio?",
@@ -57,35 +78,46 @@ async def root():
57
 
58
  @app.post("/chatbot")
59
  async def chatbot(req: ChatRequest):
 
 
 
 
60
  try:
61
- # Tokenize input
62
- inputs = tokenizer(req.text, return_tensors="pt", padding=True, truncation=True, max_length=128)
63
-
64
- # Convert to numpy for ONNX
65
- ort_inputs = {k: v.cpu().numpy() for k, v in inputs.items()}
 
 
 
 
 
 
66
  ort_outputs = session.run(None, ort_inputs)
67
- logits = torch.tensor(ort_outputs[0])
68
- pred_id = torch.argmax(logits, dim=1).item()
69
-
70
- # === Mapping ID ke label ===
 
 
71
  id2label = {
72
- 0: "about_me",
73
- 1: "skills",
74
- 2: "projects",
75
- 3: "experience",
76
- 4: "career_goal",
77
- 5: "greeting",
78
  }
79
  intent = id2label.get(pred_id, "fallback")
80
-
81
  # === Ambil jawaban ===
82
  if not df_jawaban.empty and intent in df_jawaban["Intent"].values:
 
83
  reply = df_jawaban.loc[df_jawaban["Intent"] == intent, "Jawaban_ID"].iloc[0]
84
  else:
85
  reply = responses.get(intent, responses["fallback"])
86
-
87
- return {"reply": reply, "intent": intent}
88
-
89
  except Exception as e:
 
90
  print(f"❌ Runtime error: {e}")
91
- return {"reply": "⚠️ Internal server error.", "intent": "error"}
 
 
3
  from pydantic import BaseModel
4
  from transformers import AutoTokenizer
5
  import onnxruntime as ort
6
+ import numpy as np # Menggantikan torch untuk operasi array
7
  import pandas as pd
8
  from pathlib import Path
9
+ import traceback # Untuk debugging di log
10
 
11
  app = FastAPI()
12
 
13
  # === CORS untuk frontend di Vercel ===
14
  app.add_middleware(
15
  CORSMiddleware,
16
+ allow_origins=["*"],
17
  allow_credentials=True,
18
  allow_methods=["*"],
19
  allow_headers=["*"],
 
21
 
22
  # === Path setup ===
23
  BASE_DIR = Path(__file__).resolve().parent
24
+
25
+ # GANTI DENGAN NAMA FILE ONNX YANG SESUAI DI FOLDER 'models'
26
+ # Jika file Anda bernama bert_chatbot.onnx dan berada di models/
27
  MODEL_PATH = BASE_DIR / "models" / "bert_chatbot.onnx"
28
+ TOKENIZER_PATH = BASE_DIR / "models" / "bert-base-multilingual-cased"
29
  DATASET_PATH = BASE_DIR / "dataset_chatbot_template.xlsx"
30
 
31
+ # === Global Variables ===
32
+ tokenizer = None
33
+ session = None
34
+ df_jawaban = None
 
35
 
36
+ # === Load tokenizer dan model ===
37
  try:
38
+ print("🚀 Loading ONNX model...")
39
+
40
+ # 1. Muat Tokenizer
41
+ tokenizer = AutoTokenizer.from_pretrained(str(TOKENIZER_PATH))
42
+
43
+ # 2. Muat ONNX Runtime Session (Provider CPU adalah yang paling stabil di HF Free Tier)
44
+ session = ort.InferenceSession(str(MODEL_PATH), providers=["CPUExecutionProvider"])
45
+
46
+ # 3. Muat Dataset
47
  df_jawaban = pd.read_excel(DATASET_PATH)
48
+
49
+ print(" ONNX model loaded!")
50
+
51
+ except Exception as e:
52
+ # Ini akan mencetak error jika path salah atau file tidak ditemukan
53
+ print("--------------------------------------------------")
54
+ print(f"❌ FATAL ERROR SAAT MEMUAT ONNX/SUMBER DAYA: {e}")
55
+ traceback.print_exc()
56
+ print("--------------------------------------------------")
57
+ pass
58
+
59
 
60
  # === Default responses ===
61
  responses = {
62
  "about_me": "I am a passionate developer specializing in AI and web development.",
63
  "skills": "My main skills are HTML5, CSS3, JavaScript, Laravel, Node.js, TensorFlow, and PyTorch.",
64
+ "projects": "Some of my projects include Mobile Apps Bald Detection and Portfolio Website.",
65
  "experience": "I have worked as IT Support, AI Engineer, and Freelancer.",
66
  "career_goal": "My career goal is to become a Full Stack Developer and ML Engineer.",
67
  "greeting": "Hello! How can I help you regarding this portfolio?",
 
78
 
79
  @app.post("/chatbot")
80
  async def chatbot(req: ChatRequest):
81
+ # Cek jika session ONNX gagal dimuat saat startup
82
+ if session is None:
83
+ return {"reply": responses["fallback"], "intent": "error_loading"}
84
+
85
  try:
86
+ # 1. Tokenisasi (return_tensors="np" karena kita menggunakan NumPy/ONNX)
87
+ # return_tensors="np" menghemat konversi PyTorch
88
+ inputs = tokenizer(req.text, return_tensors="np", padding=True, truncation=True, max_length=128)
89
+
90
+ # 2. Dapatkan nama input dari ONNX Session
91
+ input_names = [i.name for i in session.get_inputs()]
92
+
93
+ # Mapping input NumPy ke nama input ONNX
94
+ ort_inputs = {name: inputs[name] for name in input_names}
95
+
96
+ # 3. Inferensi ONNX
97
  ort_outputs = session.run(None, ort_inputs)
98
+
99
+ # 4. Ambil Logit dan Prediksi (numpy)
100
+ logits = ort_outputs[0]
101
+ pred_id = np.argmax(logits, axis=1)[0]
102
+
103
+ # === Mapping ID ke label (ANDA HARUS TAHU MAPPING INI) ===
104
  id2label = {
105
+ 0: "about_me", 1: "skills", 2: "projects", 3: "experience",
106
+ 4: "career_goal", 5: "greeting",
 
 
 
 
107
  }
108
  intent = id2label.get(pred_id, "fallback")
109
+
110
  # === Ambil jawaban ===
111
  if not df_jawaban.empty and intent in df_jawaban["Intent"].values:
112
+ # Menggunakan .astype(str) untuk mencegah error Pandas tipe data
113
  reply = df_jawaban.loc[df_jawaban["Intent"] == intent, "Jawaban_ID"].iloc[0]
114
  else:
115
  reply = responses.get(intent, responses["fallback"])
116
+
117
+ return {"reply": str(reply), "intent": intent}
118
+
119
  except Exception as e:
120
+ import traceback
121
  print(f"❌ Runtime error: {e}")
122
+ traceback.print_exc()
123
+ return {"reply": "⚠️ Internal server error.", "intent": "error"}