bagaseptian commited on
Commit
23d4b72
·
1 Parent(s): 1aefa24

upload files to huggingface

Browse files
Files changed (7) hide show
  1. Dockerfile +22 -0
  2. README.md +26 -8
  3. app/__init__.py +0 -0
  4. app/ai_processor.py +90 -0
  5. app/main.py +125 -0
  6. app/models.py +5 -0
  7. requirements.txt +10 -0
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ #variabel
4
+ ENV PYTHONUNBUFFERED 1
5
+ #local char
6
+ ENV LANG C.UTF-8
7
+ #directory
8
+ WORKDIR /app
9
+
10
+ #copyfile requirements
11
+ COPY ./requirements.txt /app/requirements.txt
12
+
13
+ #install depedensi
14
+ RUN pip install --no-cache-dir --upgrade pip \
15
+ && pip install --no-cache-dir --prefer-binary -r /app/requirements.txt
16
+ #copy aplikasi ke folder
17
+ COPY ./app /app/app
18
+ #port
19
+ EXPOSE 8000
20
+
21
+ #running
22
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]
README.md CHANGED
@@ -1,12 +1,30 @@
1
  ---
2
- title: Smartcv Backend
3
- emoji: 🌍
4
- colorFrom: red
5
- colorTo: yellow
6
  sdk: docker
 
7
  pinned: false
8
- license: mit
9
- short_description: AI summarize
10
- ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: SmartCV Backend API
3
+ emoji: बुद्धि
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: docker
7
+ app_port: 8000
8
  pinned: false
 
 
 
9
 
10
+ # SmartCV Backend API
11
+
12
+ Backend API untuk aplikasi SmartCV.
13
+ Dibangun dengan Python, FastAPI, dan menggunakan model AI dari Hugging Face Transformers untuk meringkas teks pengalaman kerja menjadi format CV yang profesional.
14
+
15
+ **Endpoint Utama:**
16
+ - `POST /summarize`: Menerima JSON dengan field `text` dan mengembalikan JSON dengan field `summary`.
17
+
18
+ **Teknologi:**
19
+ - Python 3.10
20
+ - FastAPI
21
+ - Uvicorn
22
+ - Pydantic
23
+ - Hugging Face Transformers (Model: [NAMA_MODEL_ANDA, misal google/flan-t5-small])
24
+
25
+ ## Cara Menjalankan Lokal (Untuk Pengembangan)
26
+ 1. `pip install -r requirements.txt`
27
+ 2. `uvicorn app.main:app --reload --port 8000`
28
+
29
+ ## Deployment
30
+ Dideploy ke Hugging Face Spaces menggunakan Docker.
app/__init__.py ADDED
File without changes
app/ai_processor.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend/app/ai_processor.py
2
+ import torch
3
+ from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
4
+ import os
5
+ import traceback # Untuk logging error
6
+
7
+ # --- MODEL_NAME tetap sama seperti yang sudah Anda pilih dan uji ---
8
+ MODEL_NAME = "google/flan-t5-small"
9
+ # MODEL_NAME = "facebook/bart-large-cnn"
10
+ # MODEL_NAME = "sshleifer/distilbart-cnn-6-6"
11
+
12
+ model = None
13
+ tokenizer = None
14
+ device = None
15
+
16
+ def initialize_model():
17
+ global model, tokenizer, device
18
+ if model is not None and tokenizer is not None:
19
+ # print("INFO: AI Processor - Model dan tokenizer sudah dimuat.") # Bisa di-uncomment jika perlu
20
+ return True
21
+ try:
22
+ print(f"INFO: AI Processor - Memulai proses pemuatan model: {MODEL_NAME}...")
23
+ if torch.cuda.is_available():
24
+ device = torch.device("cuda")
25
+ print("INFO: AI Processor - GPU (CUDA) terdeteksi.")
26
+ else:
27
+ device = torch.device("cpu")
28
+ print("INFO: AI Processor - GPU tidak terdeteksi, menggunakan CPU.")
29
+
30
+ print(f"INFO: AI Processor - Memuat tokenizer untuk {MODEL_NAME}...")
31
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
32
+ print("INFO: AI Processor - Tokenizer berhasil dimuat.")
33
+
34
+ print(f"INFO: AI Processor - Memuat model {MODEL_NAME} ke device {device}...")
35
+ model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)
36
+ model.to(device)
37
+ model.eval()
38
+ print(f"INFO: AI Processor - Model {MODEL_NAME} berhasil dimuat ke {device}.")
39
+ return True
40
+ except Exception as e:
41
+ print(f"ERROR: AI Processor - Gagal memuat model {MODEL_NAME}: {str(e)}")
42
+ traceback.print_exc()
43
+ model = None
44
+ tokenizer = None
45
+ return False
46
+
47
+ def generate_cv_summary(input_text: str) -> str:
48
+ global model, tokenizer, device
49
+ if model is None or tokenizer is None:
50
+ error_msg = "ERROR: AI Processor - Model atau tokenizer belum berhasil diinisialisasi. Coba panggil initialize_model() lagi."
51
+ print(error_msg)
52
+ if not initialize_model(): # Coba inisialisasi ulang
53
+ return error_msg + " Inisialisasi ulang juga gagal."
54
+ if model is None or tokenizer is None: # Cek lagi setelah coba inisialisasi ulang
55
+ return "ERROR: AI Processor - Model tetap tidak tersedia setelah mencoba inisialisasi ulang."
56
+
57
+ # Log ini bisa dipertahankan untuk melihat apa yang diproses
58
+ # print(f"INFO: AI Processor - Menerima teks untuk diringkas (panjang: {len(input_text)} char).")
59
+ try:
60
+ # --- PROMPT ANDA YANG SUDAH DISESUAIKAN ---
61
+ prompt_prefix = "Summarize the following work experience for a professional, ATS-friendly CV. Focus on quantifiable achievements, key responsibilities, and relevant skills. Use concise bullet points if appropriate: "
62
+ # Atau prompt lain yang sudah Anda temukan bekerja dengan baik.
63
+ # -----------------------------------------
64
+ text_to_summarize = prompt_prefix + input_text
65
+ # print(f"DEBUG: AI Processor - Teks input ke tokenizer: '{text_to_summarize[:100]}...'") # Hapus jika terlalu verbose
66
+
67
+ inputs = tokenizer(text_to_summarize, return_tensors="pt", max_length=1024, truncation=True, padding="longest")
68
+ input_ids = inputs.input_ids.to(device)
69
+ attention_mask = inputs.attention_mask.to(device)
70
+
71
+ # print(f"DEBUG: AI Processor - Melakukan inferensi pada device {device}...") # Hapus jika terlalu verbose
72
+ with torch.no_grad():
73
+ summary_ids = model.generate(
74
+ input_ids,
75
+ attention_mask=attention_mask,
76
+ max_length=250,
77
+ min_length=50,
78
+ num_beams=4,
79
+ early_stopping=True,
80
+ no_repeat_ngram_size=3,
81
+ )
82
+ summary_text = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
83
+ # print(f"INFO: AI Processor - Ringkasan digenerate: '{summary_text[:100]}...'") # Bisa dipertahankan
84
+ return summary_text.strip()
85
+ except Exception as e:
86
+ print(f"ERROR: AI Processor - Error saat proses generasi ringkasan: {str(e)}")
87
+ traceback.print_exc()
88
+ return "Error: Terjadi masalah internal pada AI saat mencoba membuat ringkasan. Silakan coba lagi."
89
+
90
+ # initialize_model() akan dipanggil dari startup event di main.py
app/main.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend/app/main.py
2
+ from fastapi import FastAPI, HTTPException
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ import os
5
+ import traceback # Untuk logging traceback error yang lebih detail
6
+
7
+ # Pastikan import dari modul lokal Anda benar
8
+ from .models import TextInput, SummaryOutput
9
+ from .ai_processor import generate_cv_summary, initialize_model as initialize_ai_model
10
+
11
+ # python-dotenv hanya berguna untuk pengembangan lokal jika ada .env file.
12
+ # Di produksi (HF Spaces), variabel lingkungan diset melalui secrets.
13
+ # from dotenv import load_dotenv
14
+ # load_dotenv()
15
+
16
+ app = FastAPI(
17
+ title="SmartCV API - Production", # Ganti title jika mau
18
+ description="API untuk menghasilkan ringkasan CV profesional menggunakan AI.",
19
+ version="1.0.0" # Ganti versi jika mau
20
+ )
21
+
22
+ # --- Konfigurasi CORS untuk Produksi dan Pengembangan ---
23
+ # Daftar origins default yang selalu diizinkan (untuk dev lokal)
24
+ allowed_origins_core = [
25
+ "http://localhost:3000",
26
+ "http://localhost:3001", # Jika Anda kadang menggunakan port lain untuk frontend dev
27
+ ]
28
+
29
+ # Ambil URL frontend dari environment variables (diset di HF Spaces atau Vercel)
30
+ # Untuk HF Spaces, kita akan set VERCEL_FRONTEND_URL sebagai secret
31
+ vercel_url_from_env = os.getenv("VERCEL_FRONTEND_URL")
32
+ if vercel_url_from_env:
33
+ # Pastikan tidak ada spasi dan hapus trailing slash jika ada
34
+ cleaned_vercel_url = vercel_url_from_env.strip().rstrip('/')
35
+ if cleaned_vercel_url: # Pastikan tidak kosong setelah strip
36
+ allowed_origins_core.append(cleaned_vercel_url)
37
+ print(f"INFO: Backend - Vercel frontend URL '{cleaned_vercel_url}' ditambahkan ke CORS.")
38
+ else:
39
+ print("WARN: Backend - VERCEL_FRONTEND_URL diset tapi kosong setelah dibersihkan.")
40
+
41
+
42
+ # Ambil URL Gitpod untuk pengembangan (jika berjalan di Gitpod)
43
+ gitpod_workspace_url_from_env = os.getenv("GITPOD_WORKSPACE_URL")
44
+ if gitpod_workspace_url_from_env:
45
+ frontend_port_gitpod = 3000 # Asumsi port frontend React di Gitpod
46
+ # Hapus "https://" sebelum membangun URL port
47
+ gitpod_domain_part = gitpod_workspace_url_from_env.replace('https://', '', 1)
48
+ gitpod_frontend_origin = f"https://{frontend_port_gitpod}-{gitpod_domain_part}"
49
+ allowed_origins_core.append(gitpod_frontend_origin)
50
+ print(f"INFO: Backend - Gitpod frontend URL '{gitpod_frontend_origin}' ditambahkan ke CORS.")
51
+
52
+ # Jika tidak ada URL produksi (Vercel) atau Gitpod yang valid terdeteksi selain localhost,
53
+ # mungkin lebih aman untuk TIDAK mengizinkan "*" di produksi.
54
+ # Namun, untuk portofolio ini, jika VERCEL_FRONTEND_URL tidak diset,
55
+ # mungkin kita perlu fallback ke "*" agar tidak error saat pertama kali deploy sebelum set secret.
56
+ # Ini adalah trade-off keamanan vs kemudahan setup awal.
57
+
58
+ # Hapus duplikat jika ada
59
+ final_allowed_origins = sorted(list(set(allowed_origins_core)))
60
+
61
+ if not any(origin.startswith("https://") for origin in final_allowed_origins if origin not in ["http://localhost:3000", "http://localhost:3001"]):
62
+ # Jika tidak ada origin HTTPS (Vercel/Gitpod) yang valid, cetak peringatan.
63
+ # Untuk produksi, idealnya ini tidak terjadi.
64
+ print("WARN: Backend - Tidak ada origin HTTPS (Vercel/Gitpod) yang dikonfigurasi untuk CORS selain localhost. Ini mungkin tidak aman untuk produksi.")
65
+ # Jika Anda ingin lebih ketat di produksi dan VERCEL_FRONTEND_URL WAJIB ada:
66
+ # if not vercel_url_from_env:
67
+ # print("CRITICAL: VERCEL_FRONTEND_URL tidak diset! CORS mungkin tidak berfungsi untuk frontend produksi.")
68
+ # # Anda bisa memilih untuk raise error di sini atau biarkan (akan fallback ke localhost saja)
69
+
70
+ print(f"INFO: Backend - CORS akan mengizinkan origins: {final_allowed_origins}")
71
+
72
+ app.add_middleware(
73
+ CORSMiddleware,
74
+ allow_origins=final_allowed_origins if final_allowed_origins else ["http://localhost:3000"], # Fallback minimal jika daftar kosong
75
+ allow_credentials=True,
76
+ allow_methods=["GET", "POST", "OPTIONS"],
77
+ allow_headers=["*"], # Untuk kesederhanaan, izinkan semua header. Bisa diperketat.
78
+ )
79
+ # --- Akhir Konfigurasi CORS ---
80
+
81
+ @app.on_event("startup")
82
+ async def startup_event():
83
+ print("INFO: Backend - Aplikasi FastAPI memulai proses startup...")
84
+ if initialize_ai_model():
85
+ print("INFO: Backend - Model AI berhasil diinisialisasi atau sudah siap.")
86
+ else:
87
+ # Ini adalah log error penting untuk produksi
88
+ print("ERROR: Backend - Model AI GAGAL diinisialisasi saat startup! Endpoint AI tidak akan berfungsi.")
89
+
90
+ @app.get("/", include_in_schema=False) # Sembunyikan dari docs API jika mau
91
+ async def read_root():
92
+ # Log ini bisa berguna untuk health check sederhana
93
+ # print("INFO: Backend - Root endpoint '/' diakses.")
94
+ return {"message": "Selamat datang di SmartCV API! API aktif."}
95
+
96
+ @app.post("/summarize", response_model=SummaryOutput)
97
+ async def summarize_text_endpoint(input_data: TextInput):
98
+ # Log ini penting untuk melihat traffic
99
+ print(f"INFO: Backend - Menerima permintaan ke /summarize. Panjang input: {len(input_data.text)} char.")
100
+
101
+ if not input_data.text or not input_data.text.strip() or len(input_data.text) < 10 : # Tambahkan min_length di sini juga
102
+ print(f"WARN: Backend - Input teks tidak valid atau terlalu pendek. Input: '{input_data.text[:30]}...'")
103
+ raise HTTPException(status_code=400, detail="Input teks tidak boleh kosong dan minimal 10 karakter.")
104
+
105
+ try:
106
+ # print("DEBUG: Backend - Memanggil generate_cv_summary...") # Bisa dihapus
107
+ summary = generate_cv_summary(input_data.text)
108
+ # print(f"DEBUG: Backend - Hasil dari generate_cv_summary: '{summary[:50]}...'") # Bisa dihapus
109
+
110
+ if summary.startswith("Error:"): # Cek jika fungsi AI mengembalikan pesan error internal
111
+ print(f"ERROR: Backend - Error dari ai_processor: {summary}")
112
+ # Untuk error dari AI processor, mungkin 500 lebih cocok daripada 503 jika itu error pemrosesan
113
+ raise HTTPException(status_code=500, detail=summary)
114
+
115
+ # print("INFO: Backend - Ringkasan berhasil dibuat, mengirim respons.") # Bisa dihapus jika terlalu verbose
116
+ return SummaryOutput(summary=summary)
117
+ except HTTPException:
118
+ # Re-raise HTTPException agar FastAPI menanganinya dengan benar (misal, 400 atau 503 dari atas)
119
+ raise
120
+ except Exception as e:
121
+ # Ini menangkap error tak terduga lainnya dari dalam endpoint
122
+ print(f"CRITICAL: Backend - Error tidak terduga di endpoint /summarize: {str(e)}")
123
+ # Cetak traceback lengkap ke log server untuk debugging mendalam
124
+ traceback.print_exc()
125
+ raise HTTPException(status_code=500, detail=f"Terjadi kesalahan internal server saat memproses permintaan Anda.")
app/models.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ class TextInput(BaseModel):
3
+ text: str = Field(..., min_length=10, description="Teks input dari pengguna yang akan diringkas.")
4
+ class SummaryOutput(BaseModel):
5
+ summary: str = Field(..., description="Hasil ringkasan teks yang dihasilkan oleh AI.")
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard] #dukungan websockets dan lain-lain
3
+ pydantic
4
+ python-dotenv #.env lokal
5
+
6
+ #depedensi AI
7
+ transformers[torch]
8
+ # torch
9
+ sentencepiece
10
+ accelerate