NimrodDev commited on
Commit
12748e3
·
1 Parent(s): 29645cb

clean final: HF Inference API embeddings (no disk)

Browse files
Files changed (1) hide show
  1. rag.py +21 -51
rag.py CHANGED
@@ -6,20 +6,17 @@ from typing import List, Tuple
6
 
7
  from langchain.text_splitter import RecursiveCharacterTextSplitter
8
  from langchain_community.vectorstores import FAISS
9
- from langchain_huggingface import HuggingFaceEmbeddings, HuggingFaceEndpoint
10
  from langchain_core.prompts import PromptTemplate
11
  from langchain.chains import RetrievalQA
12
  from supabase import create_client
13
 
14
- # ------------------------------------------------------------------
15
- # CONFIG
16
- # ------------------------------------------------------------------
17
  DATASET_API = "https://datasets-server.huggingface.co/rows"
18
  DATASET = "NimrodDev/LD_Events2"
19
  CONFIG = "default"
20
  SPLIT = "train"
21
  LIMIT = 500
22
- EMBED_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
23
  LLM_MODEL = "microsoft/DialoGPT-medium"
24
  SUPABASE_URL = os.getenv("SUPABASE_URL")
25
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
@@ -27,24 +24,20 @@ HF_TOKEN = os.getenv("HF_TOKEN")
27
 
28
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
29
 
30
- # ------------------------------------------------------------------
31
- # INTENT REGEX
32
- # ------------------------------------------------------------------
33
  GREETING_RE = re.compile(r"\b(hi|hello|hey|good morning|good afternoon|good evening)\b", re.I)
34
  THANKS_RE = re.compile(r"\b(thank|thanks|appreciate)\b", re.I)
35
  BYE_RE = re.compile(r"\b(bye|goodbye|see you|later)\b", re.I)
36
  MONEY_RE = re.compile(r"\b(price|cost|budget|cheap|expensive|money|usd|ksh|payment|deposit)\b", re.I)
37
  COMPLAIN_RE = re.compile(r"\b(complain|bad|terrible|awful|disappointed|angry|slow|rude)\b", re.I)
38
 
39
- # ------------------------------------------------------------------
40
- # FALLBACKS
41
- # ------------------------------------------------------------------
42
  FALLBACKS = {
43
  "LD Events": {
44
  "greeting": "Hello! 👋 I’m Amina, your assistant for *LD Events* (weddings, graduations, corporate events) "
45
  "and *Lamaki Designs* (construction & architectural plans).\n\n"
46
- "Which service would you like to know about?\n\n"
47
- ,
48
  "money": "Our pricing depends on venue / project size. Please share a few details so we can give you a tailored quote.",
49
  "complain": "We’re sorry to hear this. A senior agent will contact you within 30 minutes to resolve the issue.",
50
  "thanks": "You’re welcome! If you need anything else, just text back.",
@@ -54,8 +47,7 @@ FALLBACKS = {
54
  "Lamaki Designs": {
55
  "greeting": "Karibu! 🏗️ I’m Amina, your assistant for *Lamaki Designs* (construction, architectural plans, project management) "
56
  "and *LD Events* (weddings, graduations, corporate events).\n\n"
57
- "Which service would you like to know about?\n\n"
58
- ,
59
  "money": "Cost varies by project size and materials. Kindly share your plot size / plan so we can estimate for you.",
60
  "complain": "We apologise for the inconvenience. Our site manager will call you within 30 minutes to sort it out.",
61
  "thanks": "Asante! Feel free to text any time.",
@@ -64,9 +56,7 @@ FALLBACKS = {
64
  }
65
  }
66
 
67
- # ------------------------------------------------------------------
68
- # HELPERS
69
- # ------------------------------------------------------------------
70
  def _company_from_text(text: str) -> str:
71
  t = text.lower()
72
  if any(k in t for k in ("ld events", "event", "wedding", "venue", "graduation")):
@@ -86,9 +76,7 @@ def _detect_intent(text: str) -> str:
86
  def _fallback_answer(company: str, intent: str) -> str:
87
  return FALLBACKS[company].get(intent, FALLBACKS[company]["default"])
88
 
89
- # ------------------------------------------------------------------
90
- # BULLET-PROOF ONLINE FETCH – RETURNS EMPTY LIST ON ANY ERROR
91
- # ------------------------------------------------------------------
92
  @lru_cache(maxsize=1)
93
  def get_texts() -> List[str]:
94
  try:
@@ -103,38 +91,30 @@ def get_texts() -> List[str]:
103
  print(f"⚠ Dataset fetch failed: {e} – using empty corpus")
104
  return []
105
 
106
- # ------------------------------------------------------------------
107
- # ------------------------------------------------------------------
108
- # ------------------------------------------------------------------
109
  @lru_cache(maxsize=1)
110
  def get_vectorstore() -> FAISS:
111
  texts = get_texts()
112
 
113
- # --- FINAL: use HF Inference Providers router (no disk) ---------------
114
- # --- FINAL: HF Inference Providers (no disk, no cache) ----------------
115
- # --- FINAL: HF Inference Providers (no disk, no cache) ------------------
116
  from langchain_huggingface import HuggingFaceInferenceAPIEmbeddings
117
  embeddings = HuggingFaceInferenceAPIEmbeddings(
118
- api_key=HF_TOKEN,
119
- model_name="sentence-transformers/all-MiniLM-L6-v2"
120
- )
121
- # ------------------------------------------------------------------------
122
- # ------------------------------------------------------------------------
123
- # ------------------------------------------------------------------------
124
-
125
- if not texts: # no data → empty FAISS
126
  return FAISS.from_texts([""], embeddings) # dummy
127
 
128
  splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=50)
129
  docs = splitter.create_documents(texts, metadatas=[{"source": DATASET}] * len(texts))
130
  return FAISS.from_documents(docs, embeddings)
131
 
132
- # ------------------------------------------------------------------# LLM
133
- # ------------------------------------------------------------------
134
  @lru_cache(maxsize=1)
135
  def get_llm():
136
  return HuggingFaceEndpoint(
137
- repo_id=LLM_MODEL,
138
  temperature=0.1,
139
  max_new_tokens=150,
140
  huggingfacehub_api_token=HF_TOKEN
@@ -146,29 +126,19 @@ Context: {context}
146
  Question: {question}
147
  Answer:""")
148
 
149
- # ------------------------------------------------------------------
150
- # MAIN ENTRY – NEVER CRASHES
151
- # ------------------------------------------------------------------
152
  def ask_question(phone: str, question: str) -> Tuple[str, List]:
153
  intent = _detect_intent(question)
154
  company = _company_from_text(question)
155
 
156
- # unbiased interactive greeting
157
- if intent == "greeting":
158
- answer = _fallback_answer(company, "greeting")
159
- _save_chat(phone, question, answer)
160
- return answer, []
161
-
162
- # other small-talk
163
- if intent in ("thanks", "bye"):
164
  answer = _fallback_answer(company, intent)
165
  _save_chat(phone, question, answer)
166
  return answer, []
167
 
168
- # RAG path – same index every call (empty index → no docs → fallback)
169
  vs = get_vectorstore()
170
  docs = vs.similarity_search(question, k=3)
171
- if not docs or docs[0].page_content.strip() == "": # empty dummy
172
  answer = _fallback_answer(company, intent if intent in ("money", "complain") else "default")
173
  _save_chat(phone, question, answer)
174
  return answer, []
 
6
 
7
  from langchain.text_splitter import RecursiveCharacterTextSplitter
8
  from langchain_community.vectorstores import FAISS
9
+ from langchain_huggingface import HuggingFaceEndpoint
10
  from langchain_core.prompts import PromptTemplate
11
  from langchain.chains import RetrievalQA
12
  from supabase import create_client
13
 
14
+ # ------------------------------------------------------------------ CONFIG
 
 
15
  DATASET_API = "https://datasets-server.huggingface.co/rows"
16
  DATASET = "NimrodDev/LD_Events2"
17
  CONFIG = "default"
18
  SPLIT = "train"
19
  LIMIT = 500
 
20
  LLM_MODEL = "microsoft/DialoGPT-medium"
21
  SUPABASE_URL = os.getenv("SUPABASE_URL")
22
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
 
24
 
25
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
26
 
27
+ # ------------------------------------------------------------------ INTENT
28
+ import re
 
29
  GREETING_RE = re.compile(r"\b(hi|hello|hey|good morning|good afternoon|good evening)\b", re.I)
30
  THANKS_RE = re.compile(r"\b(thank|thanks|appreciate)\b", re.I)
31
  BYE_RE = re.compile(r"\b(bye|goodbye|see you|later)\b", re.I)
32
  MONEY_RE = re.compile(r"\b(price|cost|budget|cheap|expensive|money|usd|ksh|payment|deposit)\b", re.I)
33
  COMPLAIN_RE = re.compile(r"\b(complain|bad|terrible|awful|disappointed|angry|slow|rude)\b", re.I)
34
 
35
+ # ------------------------------------------------------------------ FALLBACKS
 
 
36
  FALLBACKS = {
37
  "LD Events": {
38
  "greeting": "Hello! 👋 I’m Amina, your assistant for *LD Events* (weddings, graduations, corporate events) "
39
  "and *Lamaki Designs* (construction & architectural plans).\n\n"
40
+ "Which service would you like to know about?\n\n",
 
41
  "money": "Our pricing depends on venue / project size. Please share a few details so we can give you a tailored quote.",
42
  "complain": "We’re sorry to hear this. A senior agent will contact you within 30 minutes to resolve the issue.",
43
  "thanks": "You’re welcome! If you need anything else, just text back.",
 
47
  "Lamaki Designs": {
48
  "greeting": "Karibu! 🏗️ I’m Amina, your assistant for *Lamaki Designs* (construction, architectural plans, project management) "
49
  "and *LD Events* (weddings, graduations, corporate events).\n\n"
50
+ "Which service would you like to know about?\n\n",
 
51
  "money": "Cost varies by project size and materials. Kindly share your plot size / plan so we can estimate for you.",
52
  "complain": "We apologise for the inconvenience. Our site manager will call you within 30 minutes to sort it out.",
53
  "thanks": "Asante! Feel free to text any time.",
 
56
  }
57
  }
58
 
59
+ # ------------------------------------------------------------------ HELPERS
 
 
60
  def _company_from_text(text: str) -> str:
61
  t = text.lower()
62
  if any(k in t for k in ("ld events", "event", "wedding", "venue", "graduation")):
 
76
  def _fallback_answer(company: str, intent: str) -> str:
77
  return FALLBACKS[company].get(intent, FALLBACKS[company]["default"])
78
 
79
+ # ------------------------------------------------------------------ DATA FETCH
 
 
80
  @lru_cache(maxsize=1)
81
  def get_texts() -> List[str]:
82
  try:
 
91
  print(f"⚠ Dataset fetch failed: {e} – using empty corpus")
92
  return []
93
 
94
+ # ------------------------------------------------------------------ EMBEDDINGS
 
 
95
  @lru_cache(maxsize=1)
96
  def get_vectorstore() -> FAISS:
97
  texts = get_texts()
98
 
99
+ # HF Inference Providers zero disk, zero cache
 
 
100
  from langchain_huggingface import HuggingFaceInferenceAPIEmbeddings
101
  embeddings = HuggingFaceInferenceAPIEmbeddings(
102
+ api_key=HF_TOKEN,
103
+ model_name="sentence-transformers/all-MiniLM-L6-v2"
104
+ )
105
+
106
+ if not texts:
 
 
 
107
  return FAISS.from_texts([""], embeddings) # dummy
108
 
109
  splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=50)
110
  docs = splitter.create_documents(texts, metadatas=[{"source": DATASET}] * len(texts))
111
  return FAISS.from_documents(docs, embeddings)
112
 
113
+ # ------------------------------------------------------------------ LLM
 
114
  @lru_cache(maxsize=1)
115
  def get_llm():
116
  return HuggingFaceEndpoint(
117
+ repo_id="microsoft/DialoGPT-medium",
118
  temperature=0.1,
119
  max_new_tokens=150,
120
  huggingfacehub_api_token=HF_TOKEN
 
126
  Question: {question}
127
  Answer:""")
128
 
129
+ # ------------------------------------------------------------------ MAIN
 
 
130
  def ask_question(phone: str, question: str) -> Tuple[str, List]:
131
  intent = _detect_intent(question)
132
  company = _company_from_text(question)
133
 
134
+ if intent in ("greeting", "thanks", "bye"):
 
 
 
 
 
 
 
135
  answer = _fallback_answer(company, intent)
136
  _save_chat(phone, question, answer)
137
  return answer, []
138
 
 
139
  vs = get_vectorstore()
140
  docs = vs.similarity_search(question, k=3)
141
+ if not docs or docs[0].page_content.strip() == "":
142
  answer = _fallback_answer(company, intent if intent in ("money", "complain") else "default")
143
  _save_chat(phone, question, answer)
144
  return answer, []