NimrodDev commited on
Commit
f56ceca
·
1 Parent(s): e95e29f
Files changed (3) hide show
  1. Dockerfile +9 -7
  2. app.py +23 -4
  3. rag.py +14 -10
Dockerfile CHANGED
@@ -1,6 +1,14 @@
1
  # ---------- Base ----------
2
  FROM python:3.11-slim
3
 
 
 
 
 
 
 
 
 
4
  # ---------- System Dependencies ----------
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
6
  build-essential \
@@ -11,13 +19,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
11
  # ---------- Working Directory ----------
12
  WORKDIR /code
13
 
14
- # ---------- Environment Variables ----------
15
- ENV PYTHONUNBUFFERED=1
16
- ENV PYTHONDONTWRITEBYTECODE=1
17
- ENV HF_HOME=/code/.cache/huggingface
18
- ENV TRANSFORMERS_CACHE=/code/.cache/huggingface
19
- ENV TORCH_HOME=/code/.cache/torch
20
-
21
  # ---------- Install Python Dependencies ----------
22
  COPY requirements.txt .
23
  RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
@@ -38,3 +39,4 @@ EXPOSE 7860
38
  # ---------- Start the App ----------
39
  CMD ["gunicorn", "app:app", "-b", "0.0.0.0:7860", "--timeout", "300"]
40
 
 
 
1
  # ---------- Base ----------
2
  FROM python:3.11-slim
3
 
4
+ # ---------- Environment Setup ----------
5
+ ENV DEBIAN_FRONTEND=noninteractive
6
+ ENV PYTHONUNBUFFERED=1
7
+ ENV PYTHONDONTWRITEBYTECODE=1
8
+ ENV HF_HOME=/code/.cache/huggingface
9
+ ENV TRANSFORMERS_CACHE=/code/.cache/huggingface
10
+ ENV TORCH_HOME=/code/.cache/torch
11
+
12
  # ---------- System Dependencies ----------
13
  RUN apt-get update && apt-get install -y --no-install-recommends \
14
  build-essential \
 
19
  # ---------- Working Directory ----------
20
  WORKDIR /code
21
 
 
 
 
 
 
 
 
22
  # ---------- Install Python Dependencies ----------
23
  COPY requirements.txt .
24
  RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
 
39
  # ---------- Start the App ----------
40
  CMD ["gunicorn", "app:app", "-b", "0.0.0.0:7860", "--timeout", "300"]
41
 
42
+
app.py CHANGED
@@ -1,23 +1,42 @@
1
  from flask import Flask, request, jsonify
 
2
 
3
  app = Flask(__name__)
4
 
 
 
 
 
5
  @app.route("/webhook", methods=["POST"])
6
  def webhook():
7
  data = request.get_json()
8
  question = data.get("question")
9
  phone = data.get("phone")
10
 
11
- # retrieve docs from FAISS or Supabase (pseudo)
 
 
 
12
  retrieved_docs = retriever.get_relevant_documents(question)
13
 
14
  if not retrieved_docs:
15
- return jsonify({"answer": "I couldn’t find relevant info on that yet.", "docs": 0})
 
 
 
 
 
 
 
 
 
16
 
17
- # Generate answer
18
- answer = qa_chain.invoke({"question": question, "context": retrieved_docs})
19
  return jsonify({"answer": answer, "docs": len(retrieved_docs)})
20
 
 
 
 
 
21
  if __name__ == "__main__":
22
  app.run(host="0.0.0.0", port=7860)
23
 
 
1
  from flask import Flask, request, jsonify
2
+ from rag import get_retriever, get_qa_chain # ✅ import helper functions from rag.py
3
 
4
  app = Flask(__name__)
5
 
6
+ # Initialize global retriever and QA chain once at startup
7
+ retriever = get_retriever()
8
+ qa_chain = get_qa_chain()
9
+
10
  @app.route("/webhook", methods=["POST"])
11
  def webhook():
12
  data = request.get_json()
13
  question = data.get("question")
14
  phone = data.get("phone")
15
 
16
+ if not question:
17
+ return jsonify({"error": "Missing question"}), 400
18
+
19
+ # Retrieve documents from FAISS/Supabase
20
  retrieved_docs = retriever.get_relevant_documents(question)
21
 
22
  if not retrieved_docs:
23
+ return jsonify({
24
+ "answer": "I couldn’t find relevant info on that yet.",
25
+ "docs": 0
26
+ })
27
+
28
+ # Generate an answer using the QA chain
29
+ answer = qa_chain.invoke({
30
+ "question": question,
31
+ "context": retrieved_docs
32
+ })
33
 
 
 
34
  return jsonify({"answer": answer, "docs": len(retrieved_docs)})
35
 
36
+ @app.route("/", methods=["GET"])
37
+ def index():
38
+ return jsonify({"status": "running", "message": "Lamaki RAG backend active!"})
39
+
40
  if __name__ == "__main__":
41
  app.run(host="0.0.0.0", port=7860)
42
 
rag.py CHANGED
@@ -26,6 +26,7 @@ supabase = None
26
  if SUPABASE_URL and SUPABASE_KEY:
27
  try:
28
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
 
29
  except Exception as e:
30
  print(f"⚠️ Supabase init failed: {e}")
31
 
@@ -47,8 +48,7 @@ HUMAN_RE = re.compile(r"\b(agent|human|representative|manager|someone|person)
47
  # ---------------------------------------------------------------- COMPANY FALLBACKS
48
  FALLBACKS = {
49
  "LD Events": {
50
- "greeting": "Hello! 👋 I’m *Amina*, your assistant for **LD Events** (weddings, graduations, corporate events) "
51
- "and **Lamaki Designs** (construction & architecture). How may I help you today?",
52
  "money": "Our event packages vary depending on venue and number of guests. Could you share a few details so we can estimate a quote?",
53
  "complain": "I’m sorry to hear that 😔. I’ll alert our support team — expect a call from a senior agent shortly.",
54
  "thanks": "You’re most welcome! 💐",
@@ -57,8 +57,7 @@ FALLBACKS = {
57
  "default": "Let me get back to you on that. I’ve forwarded your question to a senior planner."
58
  },
59
  "Lamaki Designs": {
60
- "greeting": "Karibu! 🏗️ I’m *Amina*, assistant for **Lamaki Designs** (construction, architectural plans, project management) "
61
- "and **LD Events** (weddings, graduations, corporate events). How may I assist?",
62
  "money": "Construction costs depend on project scope and materials. Kindly share your plot size or design type for an accurate estimate.",
63
  "complain": "We’re truly sorry for the inconvenience. Our site supervisor will reach out within 30 minutes to help.",
64
  "thanks": "Asante! We appreciate your time.",
@@ -115,7 +114,7 @@ def get_vectorstore() -> FAISS:
115
 
116
  texts = get_texts()
117
  if not texts:
118
- print("⚠️ No dataset found; building dummy FAISS index.")
119
  return FAISS.from_texts(["No context available."],
120
  HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2"))
121
 
@@ -171,12 +170,12 @@ def ask_question(phone: str, question: str) -> Tuple[str, List]:
171
  qa = RetrievalQA.from_chain_type(
172
  llm=get_llm(),
173
  retriever=retriever,
174
- chain_type_kwargs={"prompt": PROMPT},
175
  return_source_documents=True,
176
  )
177
 
178
  try:
179
- result = qa({"query": question, "company": company})
180
  answer = result.get("result", "").strip()
181
  docs = result.get("source_documents", [])
182
  except Exception as e:
@@ -192,9 +191,14 @@ def ask_question(phone: str, question: str) -> Tuple[str, List]:
192
 
193
  # ---------------------------------------------------------------- SUPABASE LOGGING
194
  def _save_chat(phone: str, q: str, a: str) -> None:
195
- if not supabase: return
 
196
  try:
197
- supabase.table("chat_memory").insert({"user_phone": phone, "role": "user", "message": q}).execute()
198
- supabase.table("chat_memory").insert({"user_phone": phone, "role": "assistant", "message": a}).execute()
 
 
 
199
  except Exception as e:
200
  print(f"⚠️ Chat log failed: {e}")
 
 
26
  if SUPABASE_URL and SUPABASE_KEY:
27
  try:
28
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
29
+ print("✅ Supabase client initialized.")
30
  except Exception as e:
31
  print(f"⚠️ Supabase init failed: {e}")
32
 
 
48
  # ---------------------------------------------------------------- COMPANY FALLBACKS
49
  FALLBACKS = {
50
  "LD Events": {
51
+ "greeting": "Hello! 👋 I’m *Amina*, your assistant for **LD Events** (weddings, graduations, corporate events) and **Lamaki Designs** (construction & architecture). How may I help you today?",
 
52
  "money": "Our event packages vary depending on venue and number of guests. Could you share a few details so we can estimate a quote?",
53
  "complain": "I’m sorry to hear that 😔. I’ll alert our support team — expect a call from a senior agent shortly.",
54
  "thanks": "You’re most welcome! 💐",
 
57
  "default": "Let me get back to you on that. I’ve forwarded your question to a senior planner."
58
  },
59
  "Lamaki Designs": {
60
+ "greeting": "Karibu! 🏗️ I’m *Amina*, assistant for **Lamaki Designs** (construction, architectural plans, project management) and **LD Events** (weddings, graduations, corporate events). How may I assist?",
 
61
  "money": "Construction costs depend on project scope and materials. Kindly share your plot size or design type for an accurate estimate.",
62
  "complain": "We’re truly sorry for the inconvenience. Our site supervisor will reach out within 30 minutes to help.",
63
  "thanks": "Asante! We appreciate your time.",
 
114
 
115
  texts = get_texts()
116
  if not texts:
117
+ print("⚠️ No dataset found; using dummy FAISS index.")
118
  return FAISS.from_texts(["No context available."],
119
  HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2"))
120
 
 
170
  qa = RetrievalQA.from_chain_type(
171
  llm=get_llm(),
172
  retriever=retriever,
173
+ chain_type_kwargs={"prompt": PROMPT.partial(company=company)},
174
  return_source_documents=True,
175
  )
176
 
177
  try:
178
+ result = qa({"query": question})
179
  answer = result.get("result", "").strip()
180
  docs = result.get("source_documents", [])
181
  except Exception as e:
 
191
 
192
  # ---------------------------------------------------------------- SUPABASE LOGGING
193
  def _save_chat(phone: str, q: str, a: str) -> None:
194
+ if not supabase:
195
+ return
196
  try:
197
+ data = [
198
+ {"user_phone": phone, "role": "user", "message": q},
199
+ {"user_phone": phone, "role": "assistant", "message": a}
200
+ ]
201
+ supabase.table("chat_memory").insert(data).execute()
202
  except Exception as e:
203
  print(f"⚠️ Chat log failed: {e}")
204
+