diff --git a/.env.example b/.env.example index 683573e..8918e6a 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,8 @@ TELEGRAM_BOT_TOKEN=isi_token_dari_botfather -OPENAI_API_KEY=isi_api_key_openai -OPENAI_MODEL=gpt-4o -OPENAI_EMBEDDING_MODEL=text-embedding-3-small +DEEPSEEK_API_KEY=isi_api_key_deepseek +DEEPSEEK_MODEL=deepseek-chat ADMIN_TELEGRAM_ID=isi_telegram_id_anda DB_PATH=/app/data/second_brain.db CHROMA_PATH=/app/data/chromadb UPLOAD_PATH=/app/data/uploads +EMBEDDING_MODEL=all-MiniLM-L6-v2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc80642 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.env +data/* +!data/.gitkeep +__pycache__/ +*.pyc +*.pyo +.DS_Store diff --git a/Dockerfile b/Dockerfile index f8afa71..536746c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt +# Pre-download embedding model agar tidak diunduh ulang saat runtime +RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')" + COPY src/ ./src/ CMD ["python", "src/main.py"] diff --git a/requirements.txt b/requirements.txt index 31a6872..60a533a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ python-dotenv==1.0.1 chromadb==0.5.3 pdfplumber==0.11.0 python-docx==1.1.2 +sentence-transformers==3.0.1 diff --git a/src/config.py b/src/config.py index 510d2a8..685641a 100644 --- a/src/config.py +++ b/src/config.py @@ -4,10 +4,11 @@ from dotenv import load_dotenv load_dotenv() TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"] -OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] -OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o") -OPENAI_EMBEDDING_MODEL = os.getenv("OPENAI_EMBEDDING_MODEL", "text-embedding-3-small") +DEEPSEEK_API_KEY = os.environ["DEEPSEEK_API_KEY"] +DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat") +DEEPSEEK_BASE_URL = "https://api.deepseek.com" ADMIN_TELEGRAM_ID = int(os.environ["ADMIN_TELEGRAM_ID"]) DB_PATH = os.getenv("DB_PATH", "/app/data/second_brain.db") CHROMA_PATH = os.getenv("CHROMA_PATH", "/app/data/chromadb") UPLOAD_PATH = os.getenv("UPLOAD_PATH", "/app/data/uploads") +EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "all-MiniLM-L6-v2") diff --git a/src/handlers/user.py b/src/handlers/user.py index 378fc44..1346775 100644 --- a/src/handlers/user.py +++ b/src/handlers/user.py @@ -2,13 +2,13 @@ from pathlib import Path from telegram import Update from telegram.ext import ContextTypes from openai import AsyncOpenAI -from config import OPENAI_API_KEY, OPENAI_MODEL, ADMIN_TELEGRAM_ID +from config import DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL, ADMIN_TELEGRAM_ID import database as db import rag import document as doc_processor from guard import is_academic_topic, REJECT_MESSAGE -_client = AsyncOpenAI(api_key=OPENAI_API_KEY) +_client = AsyncOpenAI(api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL) ACADEMIC_SYSTEM_PROMPT = ( "Anda adalah asisten akademik personal untuk dosen di perguruan tinggi Indonesia. " @@ -128,7 +128,7 @@ async def ask(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.chat.send_action("typing") # Guard: hanya izinkan topik akademik - if not await is_academic_topic(question, _client, OPENAI_MODEL): + if not await is_academic_topic(question, _client, DEEPSEEK_MODEL): await update.message.reply_text(REJECT_MESSAGE) return @@ -154,7 +154,7 @@ async def ask(update: Update, context: ContextTypes.DEFAULT_TYPE): try: response = await _client.chat.completions.create( - model=OPENAI_MODEL, + model=DEEPSEEK_MODEL, messages=messages, max_tokens=1500, ) diff --git a/src/rag.py b/src/rag.py index e929954..91c185b 100644 --- a/src/rag.py +++ b/src/rag.py @@ -1,10 +1,10 @@ import asyncio import chromadb -from openai import AsyncOpenAI -from config import CHROMA_PATH, OPENAI_API_KEY, OPENAI_EMBEDDING_MODEL +from sentence_transformers import SentenceTransformer +from config import CHROMA_PATH, EMBEDDING_MODEL -_client = AsyncOpenAI(api_key=OPENAI_API_KEY) _chroma = chromadb.PersistentClient(path=CHROMA_PATH) +_embedding_model = SentenceTransformer(EMBEDDING_MODEL) def _get_collection(name: str): @@ -12,8 +12,8 @@ def _get_collection(name: str): async def _embed(text: str) -> list: - resp = await _client.embeddings.create(model=OPENAI_EMBEDDING_MODEL, input=text) - return resp.data[0].embedding + embedding = await asyncio.to_thread(_embedding_model.encode, text) + return embedding.tolist() async def add_memory(user_id: int, memory_id: int, content: str, scope: str = "private"):