amosnbn commited on
Commit
2aab552
Β·
1 Parent(s): 57cffd2
Files changed (1) hide show
  1. app.py +17 -20
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # app.py
2
- # PapuaTranslate β€” Flask + SQLAlchemy + Supabase + mT5-LoRA (lazy load)
3
  import os, re, logging, threading
4
  from datetime import datetime, timezone
5
  from functools import wraps
@@ -9,9 +9,10 @@ from flask import Flask, render_template, request, redirect, url_for, session, j
9
  logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
10
  log = logging.getLogger("papua-app")
11
 
12
- # ===== Flask (templates di folder 'templates') =====
13
- app = Flask(__name__, template_folder="templates", static_folder="static")
14
- SESSION_SECURE = os.getenv("SESSION_COOKIE_SECURE", "true").lower() in ("1", "true", "yes")
 
15
  app.config.update(
16
  SECRET_KEY=os.getenv("SECRET_KEY", "dev-secret-change-me"),
17
  SESSION_COOKIE_SAMESITE="Lax",
@@ -27,7 +28,7 @@ if not DATABASE_URL:
27
  DATABASE_URL = "sqlite:////tmp/app.db"
28
  log.warning("[DB] DATABASE_URL tidak diset; pakai SQLite /tmp/app.db")
29
  else:
30
- # normalisasi skema lama β†’ psycopg2 driver
31
  if DATABASE_URL.startswith("postgres://"):
32
  DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql+psycopg2://", 1)
33
  elif DATABASE_URL.startswith("postgresql://"):
@@ -85,7 +86,7 @@ PAPUA_MAP = {
85
  }
86
  def prenorm(text: str) -> str:
87
  t = re.sub(r"\s+", " ", text.strip())
88
- t = t.replace("…", "...").replace("–", "-").replace("β€”", "-")
89
  for pat, repl in PAPUA_MAP.items(): t = re.sub(pat, repl, t, flags=re.IGNORECASE)
90
  return t
91
 
@@ -93,8 +94,9 @@ def prenorm(text: str) -> str:
93
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
94
  from peft import PeftModel
95
 
96
- BASE_MODEL_ID = os.getenv("BASE_MODEL_ID", "amosnbn/cendol-mt5-base-inst")
97
- ADAPTER_ID = os.getenv("ADAPTER_ID", "amosnbn/papua-lora-ckpt-168")
 
98
  DEVICE = "cuda" if os.getenv("DEVICE", "cpu") == "cuda" else "cpu"
99
 
100
  TOK = None
@@ -103,10 +105,10 @@ _MODEL_LOCK = threading.Lock()
103
 
104
  def _load_model():
105
  global TOK, MODEL
106
- log.info("[MODEL] loading base=%s adapter=%s", BASE_MODEL_ID, ADAPTER_ID)
107
  TOK = AutoTokenizer.from_pretrained(BASE_MODEL_ID)
108
  base = AutoModelForSeq2SeqLM.from_pretrained(BASE_MODEL_ID)
109
- MODEL = PeftModel.from_pretrained(base, ADAPTER_ID)
110
  MODEL.eval().to(DEVICE)
111
  log.info("[MODEL] ready on %s", DEVICE)
112
 
@@ -131,7 +133,7 @@ def translate_with_model(text: str, max_new_tokens: int = 48) -> str:
131
  )
132
  return tok.decode(outputs[0], skip_special_tokens=True)
133
 
134
- # ===== Little utils / logging =====
135
  @app.before_request
136
  def _log_req():
137
  if request.path not in ("/health", "/ping", "/favicon.ico"):
@@ -157,13 +159,11 @@ def login_post():
157
  email = (request.form.get("email") or "").strip().lower()
158
  pwd = request.form.get("password") or ""
159
  if not email or not pwd:
160
- flash("Isi email dan password", "error")
161
- return redirect(url_for("login_get"))
162
  with SessionLocal() as s:
163
  u = s.query(User).filter_by(email=email).first()
164
  if not u or not verify_password(u, pwd):
165
- flash("Email atau password salah", "error")
166
- return redirect(url_for("login_get"))
167
  session["uid"], session["email"] = u.id, u.email
168
  return redirect(url_for("index"))
169
 
@@ -176,12 +176,10 @@ def register_post():
176
  email = (request.form.get("email") or "").strip().lower()
177
  pwd = request.form.get("password") or ""
178
  if not email or not pwd:
179
- flash("Isi email dan password", "error")
180
- return redirect(url_for("register_get"))
181
  with SessionLocal() as s:
182
  if s.query(User).filter_by(email=email).first():
183
- flash("Email sudah terdaftar", "error")
184
- return redirect(url_for("register_get"))
185
  u = User(email=email); set_password(u, pwd)
186
  s.add(u); s.commit()
187
  session["uid"], session["email"] = u.id, u.email
@@ -202,7 +200,6 @@ def index():
202
  .order_by(Translation.id.desc())
203
  .limit(10).all())
204
  data = [{"src": it.src, "mt": it.mt, "created_at": it.created_at} for it in items]
205
- # index.html kamu memakai 'user' & 'data'
206
  return render_template("index.html", user=session.get("email"), data=data, device=DEVICE)
207
 
208
  @app.get("/about")
 
1
  # app.py
2
+ # PapuaTranslate β€” Flask + SQLAlchemy (Supabase/SQLite) + mT5-LoRA (lazy)
3
  import os, re, logging, threading
4
  from datetime import datetime, timezone
5
  from functools import wraps
 
9
  logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
10
  log = logging.getLogger("papua-app")
11
 
12
+ # ===== Flask =====
13
+ # >> PAKAI folder 'frontend' (sesuai struktur kamu)
14
+ app = Flask(__name__, template_folder="frontend", static_folder="static")
15
+ SESSION_SECURE = os.getenv("SESSION_COOKIE_SECURE", "true").lower() in ("1","true","yes")
16
  app.config.update(
17
  SECRET_KEY=os.getenv("SECRET_KEY", "dev-secret-change-me"),
18
  SESSION_COOKIE_SAMESITE="Lax",
 
28
  DATABASE_URL = "sqlite:////tmp/app.db"
29
  log.warning("[DB] DATABASE_URL tidak diset; pakai SQLite /tmp/app.db")
30
  else:
31
+ # normalisasi skema β†’ psycopg2
32
  if DATABASE_URL.startswith("postgres://"):
33
  DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql+psycopg2://", 1)
34
  elif DATABASE_URL.startswith("postgresql://"):
 
86
  }
87
  def prenorm(text: str) -> str:
88
  t = re.sub(r"\s+", " ", text.strip())
89
+ t = t.replace("…","...").replace("–","-").replace("β€”","-")
90
  for pat, repl in PAPUA_MAP.items(): t = re.sub(pat, repl, t, flags=re.IGNORECASE)
91
  return t
92
 
 
94
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
95
  from peft import PeftModel
96
 
97
+ # Untuk uji UI cepat gunakan mt5-small & kosongkan adapter, lalu ganti ke model kamu
98
+ BASE_MODEL_ID = os.getenv("BASE_MODEL_ID", "google/mt5-small")
99
+ ADAPTER_ID = os.getenv("ADAPTER_ID", "")
100
  DEVICE = "cuda" if os.getenv("DEVICE", "cpu") == "cuda" else "cpu"
101
 
102
  TOK = None
 
105
 
106
  def _load_model():
107
  global TOK, MODEL
108
+ log.info("[MODEL] loading base=%s adapter=%s", BASE_MODEL_ID, ADAPTER_ID or "-")
109
  TOK = AutoTokenizer.from_pretrained(BASE_MODEL_ID)
110
  base = AutoModelForSeq2SeqLM.from_pretrained(BASE_MODEL_ID)
111
+ MODEL = PeftModel.from_pretrained(base, ADAPTER_ID) if ADAPTER_ID else base
112
  MODEL.eval().to(DEVICE)
113
  log.info("[MODEL] ready on %s", DEVICE)
114
 
 
133
  )
134
  return tok.decode(outputs[0], skip_special_tokens=True)
135
 
136
+ # ===== Utils / logging =====
137
  @app.before_request
138
  def _log_req():
139
  if request.path not in ("/health", "/ping", "/favicon.ico"):
 
159
  email = (request.form.get("email") or "").strip().lower()
160
  pwd = request.form.get("password") or ""
161
  if not email or not pwd:
162
+ flash("Isi email dan password", "error"); return redirect(url_for("login_get"))
 
163
  with SessionLocal() as s:
164
  u = s.query(User).filter_by(email=email).first()
165
  if not u or not verify_password(u, pwd):
166
+ flash("Email atau password salah", "error"); return redirect(url_for("login_get"))
 
167
  session["uid"], session["email"] = u.id, u.email
168
  return redirect(url_for("index"))
169
 
 
176
  email = (request.form.get("email") or "").strip().lower()
177
  pwd = request.form.get("password") or ""
178
  if not email or not pwd:
179
+ flash("Isi email dan password", "error"); return redirect(url_for("register_get"))
 
180
  with SessionLocal() as s:
181
  if s.query(User).filter_by(email=email).first():
182
+ flash("Email sudah terdaftar", "error"); return redirect(url_for("register_get"))
 
183
  u = User(email=email); set_password(u, pwd)
184
  s.add(u); s.commit()
185
  session["uid"], session["email"] = u.id, u.email
 
200
  .order_by(Translation.id.desc())
201
  .limit(10).all())
202
  data = [{"src": it.src, "mt": it.mt, "created_at": it.created_at} for it in items]
 
203
  return render_template("index.html", user=session.get("email"), data=data, device=DEVICE)
204
 
205
  @app.get("/about")