-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
134 lines (103 loc) · 4.57 KB
/
Copy pathmain.py
File metadata and controls
134 lines (103 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import Response
from pydantic import BaseModel
from openai import OpenAI
import json, os, uuid
from pathlib import Path
from typing import Optional
app = FastAPI(title="Chatbot API")
# CORS: izinkan semua origin supaya widget bisa dipasang di website mana saja
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["POST", "GET"],
allow_headers=["*"],
)
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# Simpan riwayat percakapan di memory (cukup untuk MVP)
sessions: dict[str, list] = {}
# ── Model ──────────────────────────────────────────────────────────────
class ChatRequest(BaseModel):
message: str
session_id: Optional[str] = None
# ── Helper Functions ───────────────────────────────────────────────────
def load_client(client_id: str) -> dict:
"""Load konfigurasi dan data produk per klien"""
path = Path(f"clients/{client_id}.json")
if not path.exists():
raise HTTPException(status_code=404, detail=f"Client '{client_id}' tidak ditemukan")
return json.loads(path.read_text(encoding="utf-8"))
def build_system_prompt(cfg: dict) -> str:
"""Build system prompt dari data klien — ini 'otak' chatbot"""
products = "\n".join([
f"[{i+1}] {p['name']} | Harga: Rp{p['price']:,} | Stok: {p['stock']} | "
f"{p['description']} | Link: {p['checkout_url']}"
for i, p in enumerate(cfg.get("products", []))
])
faq = "\n".join([
f"Q: {f['q']}\nA: {f['a']}"
for f in cfg.get("faq", [])
])
wa = cfg.get("whatsapp", "")
return f"""Kamu adalah AI Sales Assistant untuk {cfg['store_name']}.
=== PRODUK TERSEDIA ===
{products}
=== FAQ ===
{faq}
=== ATURAN WAJIB ===
- HANYA rekomendasikan produk dari daftar di atas, jangan mengarang produk baru
- Jangan menebak harga, stok, atau detail yang tidak ada di data produk
- Selalu sertakan link checkout saat merekomendasikan produk
- Kalau tidak tahu jawabannya, arahkan ke admin: wa.me/{wa}
- Maksimal rekomendasikan 2-3 produk sekaligus, jangan dump semua
- Bahasa: santai, ramah, boleh pakai "kak"
- Jangan bahas topik di luar produk toko
{cfg.get('system_prompt_extra', '')}"""
# ── Endpoints ──────────────────────────────────────────────────────────
@app.post("/chat/{client_id}")
async def chat(client_id: str, req: ChatRequest):
"""Endpoint utama: terima pesan user, return respons AI"""
cfg = load_client(client_id)
# Buat session baru kalau belum ada
session_id = req.session_id or str(uuid.uuid4())
history = sessions.get(session_id, [])
# Tambah pesan user ke history
history.append({"role": "user", "content": req.message})
# Kirim ke OpenAI (ambil max 10 pesan terakhir biar hemat token)
messages = [
{"role": "system", "content": build_system_prompt(cfg)}
] + history[-10:]
model = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
response = openai_client.chat.completions.create(
model=model,
messages=messages,
max_tokens=500,
temperature=0.75
)
reply = response.choices[0].message.content
# Simpan respons ke history
history.append({"role": "assistant", "content": reply})
sessions[session_id] = history
return {"reply": reply, "session_id": session_id}
@app.get("/widget/{client_id}.js", response_class=Response)
async def serve_widget(client_id: str):
"""Serve widget JS yang sudah di-inject dengan config klien"""
cfg = load_client(client_id)
api_url = os.getenv("API_URL", "http://localhost:8000")
template = Path("widget_template.js").read_text(encoding="utf-8")
widget_js = (template
.replace("{{CLIENT_ID}}", client_id)
.replace("{{STORE_NAME}}", cfg["store_name"])
.replace("{{API_URL}}", api_url)
.replace("{{ACCENT_COLOR}}", cfg.get("accent_color", "#16a34a"))
.replace("{{GREETING}}", cfg.get("greeting", "Halo kak! Ada yang bisa dibantu?"))
)
return Response(content=widget_js, media_type="application/javascript")
@app.get("/health")
def health():
"""Health check untuk Render"""
return {"status": "ok"}
@app.get("/")
def root():
return {"message": "Chatbot API is running"}