chore: switch to DeepSeek API + sentence-transformers for free deployment
- Replace OpenAI LLM with DeepSeek (deepseek-chat) via OpenAI-compatible client - Replace OpenAI embeddings with local sentence-transformers (all-MiniLM-L6-v2) - Pre-download embedding model during Docker build - Add .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
TELEGRAM_BOT_TOKEN=isi_token_dari_botfather
|
TELEGRAM_BOT_TOKEN=isi_token_dari_botfather
|
||||||
OPENAI_API_KEY=isi_api_key_openai
|
DEEPSEEK_API_KEY=isi_api_key_deepseek
|
||||||
OPENAI_MODEL=gpt-4o
|
DEEPSEEK_MODEL=deepseek-chat
|
||||||
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
|
||||||
ADMIN_TELEGRAM_ID=isi_telegram_id_anda
|
ADMIN_TELEGRAM_ID=isi_telegram_id_anda
|
||||||
DB_PATH=/app/data/second_brain.db
|
DB_PATH=/app/data/second_brain.db
|
||||||
CHROMA_PATH=/app/data/chromadb
|
CHROMA_PATH=/app/data/chromadb
|
||||||
UPLOAD_PATH=/app/data/uploads
|
UPLOAD_PATH=/app/data/uploads
|
||||||
|
EMBEDDING_MODEL=all-MiniLM-L6-v2
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
.env
|
||||||
|
data/*
|
||||||
|
!data/.gitkeep
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.DS_Store
|
||||||
@@ -10,6 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r 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/
|
COPY src/ ./src/
|
||||||
|
|
||||||
CMD ["python", "src/main.py"]
|
CMD ["python", "src/main.py"]
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ python-dotenv==1.0.1
|
|||||||
chromadb==0.5.3
|
chromadb==0.5.3
|
||||||
pdfplumber==0.11.0
|
pdfplumber==0.11.0
|
||||||
python-docx==1.1.2
|
python-docx==1.1.2
|
||||||
|
sentence-transformers==3.0.1
|
||||||
|
|||||||
+4
-3
@@ -4,10 +4,11 @@ from dotenv import load_dotenv
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
|
TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
|
||||||
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
|
DEEPSEEK_API_KEY = os.environ["DEEPSEEK_API_KEY"]
|
||||||
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
|
DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
|
||||||
OPENAI_EMBEDDING_MODEL = os.getenv("OPENAI_EMBEDDING_MODEL", "text-embedding-3-small")
|
DEEPSEEK_BASE_URL = "https://api.deepseek.com"
|
||||||
ADMIN_TELEGRAM_ID = int(os.environ["ADMIN_TELEGRAM_ID"])
|
ADMIN_TELEGRAM_ID = int(os.environ["ADMIN_TELEGRAM_ID"])
|
||||||
DB_PATH = os.getenv("DB_PATH", "/app/data/second_brain.db")
|
DB_PATH = os.getenv("DB_PATH", "/app/data/second_brain.db")
|
||||||
CHROMA_PATH = os.getenv("CHROMA_PATH", "/app/data/chromadb")
|
CHROMA_PATH = os.getenv("CHROMA_PATH", "/app/data/chromadb")
|
||||||
UPLOAD_PATH = os.getenv("UPLOAD_PATH", "/app/data/uploads")
|
UPLOAD_PATH = os.getenv("UPLOAD_PATH", "/app/data/uploads")
|
||||||
|
EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "all-MiniLM-L6-v2")
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ from pathlib import Path
|
|||||||
from telegram import Update
|
from telegram import Update
|
||||||
from telegram.ext import ContextTypes
|
from telegram.ext import ContextTypes
|
||||||
from openai import AsyncOpenAI
|
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 database as db
|
||||||
import rag
|
import rag
|
||||||
import document as doc_processor
|
import document as doc_processor
|
||||||
from guard import is_academic_topic, REJECT_MESSAGE
|
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 = (
|
ACADEMIC_SYSTEM_PROMPT = (
|
||||||
"Anda adalah asisten akademik personal untuk dosen di perguruan tinggi Indonesia. "
|
"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")
|
await update.message.chat.send_action("typing")
|
||||||
|
|
||||||
# Guard: hanya izinkan topik akademik
|
# 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)
|
await update.message.reply_text(REJECT_MESSAGE)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ async def ask(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
response = await _client.chat.completions.create(
|
response = await _client.chat.completions.create(
|
||||||
model=OPENAI_MODEL,
|
model=DEEPSEEK_MODEL,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
max_tokens=1500,
|
max_tokens=1500,
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import chromadb
|
import chromadb
|
||||||
from openai import AsyncOpenAI
|
from sentence_transformers import SentenceTransformer
|
||||||
from config import CHROMA_PATH, OPENAI_API_KEY, OPENAI_EMBEDDING_MODEL
|
from config import CHROMA_PATH, EMBEDDING_MODEL
|
||||||
|
|
||||||
_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
|
|
||||||
_chroma = chromadb.PersistentClient(path=CHROMA_PATH)
|
_chroma = chromadb.PersistentClient(path=CHROMA_PATH)
|
||||||
|
_embedding_model = SentenceTransformer(EMBEDDING_MODEL)
|
||||||
|
|
||||||
|
|
||||||
def _get_collection(name: str):
|
def _get_collection(name: str):
|
||||||
@@ -12,8 +12,8 @@ def _get_collection(name: str):
|
|||||||
|
|
||||||
|
|
||||||
async def _embed(text: str) -> list:
|
async def _embed(text: str) -> list:
|
||||||
resp = await _client.embeddings.create(model=OPENAI_EMBEDDING_MODEL, input=text)
|
embedding = await asyncio.to_thread(_embedding_model.encode, text)
|
||||||
return resp.data[0].embedding
|
return embedding.tolist()
|
||||||
|
|
||||||
|
|
||||||
async def add_memory(user_id: int, memory_id: int, content: str, scope: str = "private"):
|
async def add_memory(user_id: int, memory_id: int, content: str, scope: str = "private"):
|
||||||
|
|||||||
Reference in New Issue
Block a user