Sanjay kumar K commited on
Commit
08a2a8d
Β·
1 Parent(s): 6aaba54

Add multi-key Groq rotation, session cleanup, smarter FAISS chunking

Browse files
Files changed (2) hide show
  1. app.py +45 -22
  2. ingest.py +5 -1
app.py CHANGED
@@ -27,16 +27,21 @@ from langchain_core.prompts import ChatPromptTemplate
27
  load_dotenv()
28
 
29
  # ── Configuration ─────────────────────────────────────────────────────────────
30
- GROQ_API_KEY = os.getenv("GROQ_API_KEY", "").strip()
31
- GROQ_KEYS = [GROQ_API_KEY] if GROQ_API_KEY else []
 
 
 
32
  BUSINESS_NAME = os.getenv("BUSINESS_NAME", "Vibhu Solutions")
33
  LLM_MODEL = os.getenv("LLM_MODEL", "llama-3.1-8b-instant")
34
  FAISS_DIR = "./faiss_db"
35
  EMBED_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
36
- MAX_HISTORY = 8 # last 8 messages (~4 exchanges) per session
 
37
 
38
  # In-memory session store {session_id: [{"role": "user"|"assistant", "content": "..."}]}
39
  session_memory: Dict[str, List[Dict[str, str]]] = {}
 
40
 
41
  # ── Agent System Prompt (system message only β€” question goes in human message)
42
  SYSTEM_PROMPT = """\
@@ -107,7 +112,15 @@ async def lifespan(app: FastAPI):
107
  yield
108
 
109
 
110
- def _build_components(key_index: int = 0):
 
 
 
 
 
 
 
 
111
  if not GROQ_KEYS:
112
  raise ValueError("GROQ_API_KEY not set. Add it to your .env file.")
113
  if not Path(FAISS_DIR).exists():
@@ -124,12 +137,8 @@ def _build_components(key_index: int = 0):
124
  FAISS_DIR, embeddings, allow_dangerous_deserialization=True
125
  )
126
  retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
127
- llm = ChatGroq(
128
- groq_api_key=GROQ_KEYS[key_index % len(GROQ_KEYS)],
129
- model_name=LLM_MODEL,
130
- temperature=0.2,
131
- )
132
- prompt = ChatPromptTemplate.from_messages([
133
  ("system", SYSTEM_PROMPT),
134
  ("human", "{question}"),
135
  ])
@@ -207,7 +216,7 @@ def health():
207
 
208
  @app.post("/chat", response_model=ChatResponse)
209
  def chat(request: ChatRequest):
210
- global _retriever, _llm, _prompt
211
  if not request.message.strip():
212
  raise HTTPException(status_code=400, detail="Message cannot be empty.")
213
 
@@ -216,7 +225,7 @@ def chat(request: ChatRequest):
216
  try:
217
  _retriever, _llm, _prompt = _build_components()
218
  print(f"βœ… {BUSINESS_NAME} AI Agent is ready (loaded on first request).")
219
- except (ValueError, FileNotFoundError) as e:
220
  return ChatResponse(
221
  reply=(
222
  "The AI Agent is not configured yet. "
@@ -235,15 +244,12 @@ def chat(request: ChatRequest):
235
 
236
  # Build user-context block (used by RULE 4 to skip re-asking known details)
237
  ctx_parts = []
238
- if request.user_name:
239
- ctx_parts.append(f"Name : {request.user_name}")
240
- if request.user_email:
241
- ctx_parts.append(f"Email : {request.user_email}")
242
- if request.user_phone:
243
- ctx_parts.append(f"Phone : {request.user_phone}")
244
  user_ctx = "\n".join(ctx_parts) if ctx_parts else "Not provided"
245
 
246
- # Format prompt and invoke LLM
247
  formatted = _prompt.format_messages(
248
  business_name=BUSINESS_NAME,
249
  context=context,
@@ -251,13 +257,30 @@ def chat(request: ChatRequest):
251
  history=_format_history(history),
252
  user_context=user_ctx,
253
  )
254
- reply = _llm.invoke(formatted).content
 
 
 
 
 
 
 
 
 
 
 
 
255
 
256
  # Persist history (capped at MAX_HISTORY messages)
257
  history.append({"role": "user", "content": request.message.strip()})
258
  history.append({"role": "assistant", "content": reply})
259
  session_memory[request.session_id] = history[-MAX_HISTORY:]
260
 
 
 
 
 
 
261
  return ChatResponse(reply=reply, session_id=request.session_id)
262
 
263
  except Exception as e:
@@ -267,8 +290,8 @@ def chat(request: ChatRequest):
267
  if "rate_limit_exceeded" in err or "429" in err:
268
  return ChatResponse(
269
  reply=(
270
- "Our AI assistant is temporarily busy due to high demand. "
271
- "Please try again in a few minutes.\n\n"
272
  "Or contact us directly:\n"
273
  "πŸ“§ contact@vibhusolutions.com\n"
274
  "πŸ“ž +91 9380345108"
 
27
  load_dotenv()
28
 
29
  # ── Configuration ─────────────────────────────────────────────────────────────
30
+ GROQ_KEYS = [k.strip() for k in [
31
+ os.getenv("GROQ_API_KEY", ""),
32
+ os.getenv("GROQ_API_KEY_2", ""),
33
+ os.getenv("GROQ_API_KEY_3", ""),
34
+ ] if k.strip()]
35
  BUSINESS_NAME = os.getenv("BUSINESS_NAME", "Vibhu Solutions")
36
  LLM_MODEL = os.getenv("LLM_MODEL", "llama-3.1-8b-instant")
37
  FAISS_DIR = "./faiss_db"
38
  EMBED_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
39
+ MAX_HISTORY = 8 # last 8 messages (~4 exchanges) per session
40
+ MAX_SESSIONS = 500 # evict oldest sessions beyond this limit
41
 
42
  # In-memory session store {session_id: [{"role": "user"|"assistant", "content": "..."}]}
43
  session_memory: Dict[str, List[Dict[str, str]]] = {}
44
+ _active_key_i: int = 0
45
 
46
  # ── Agent System Prompt (system message only β€” question goes in human message)
47
  SYSTEM_PROMPT = """\
 
112
  yield
113
 
114
 
115
+ def _make_llm() -> "ChatGroq":
116
+ return ChatGroq(
117
+ groq_api_key=GROQ_KEYS[_active_key_i % len(GROQ_KEYS)],
118
+ model_name=LLM_MODEL,
119
+ temperature=0.2,
120
+ )
121
+
122
+
123
+ def _build_components():
124
  if not GROQ_KEYS:
125
  raise ValueError("GROQ_API_KEY not set. Add it to your .env file.")
126
  if not Path(FAISS_DIR).exists():
 
137
  FAISS_DIR, embeddings, allow_dangerous_deserialization=True
138
  )
139
  retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
140
+ llm = _make_llm()
141
+ prompt = ChatPromptTemplate.from_messages([
 
 
 
 
142
  ("system", SYSTEM_PROMPT),
143
  ("human", "{question}"),
144
  ])
 
216
 
217
  @app.post("/chat", response_model=ChatResponse)
218
  def chat(request: ChatRequest):
219
+ global _retriever, _llm, _prompt, _active_key_i
220
  if not request.message.strip():
221
  raise HTTPException(status_code=400, detail="Message cannot be empty.")
222
 
 
225
  try:
226
  _retriever, _llm, _prompt = _build_components()
227
  print(f"βœ… {BUSINESS_NAME} AI Agent is ready (loaded on first request).")
228
+ except (ValueError, FileNotFoundError):
229
  return ChatResponse(
230
  reply=(
231
  "The AI Agent is not configured yet. "
 
244
 
245
  # Build user-context block (used by RULE 4 to skip re-asking known details)
246
  ctx_parts = []
247
+ if request.user_name: ctx_parts.append(f"Name : {request.user_name}")
248
+ if request.user_email: ctx_parts.append(f"Email : {request.user_email}")
249
+ if request.user_phone: ctx_parts.append(f"Phone : {request.user_phone}")
 
 
 
250
  user_ctx = "\n".join(ctx_parts) if ctx_parts else "Not provided"
251
 
252
+ # Format prompt
253
  formatted = _prompt.format_messages(
254
  business_name=BUSINESS_NAME,
255
  context=context,
 
257
  history=_format_history(history),
258
  user_context=user_ctx,
259
  )
260
+
261
+ # Invoke LLM β€” auto-rotate key on rate limit (if multiple keys available)
262
+ try:
263
+ reply = _llm.invoke(formatted).content
264
+ except Exception as llm_err:
265
+ err_str = str(llm_err)
266
+ if ("rate_limit_exceeded" in err_str or "429" in err_str) and len(GROQ_KEYS) > 1:
267
+ _active_key_i = (_active_key_i + 1) % len(GROQ_KEYS)
268
+ _llm = _make_llm()
269
+ print(f"⚠️ Rate limit hit β€” rotated to Groq key #{_active_key_i + 1}")
270
+ reply = _llm.invoke(formatted).content
271
+ else:
272
+ raise
273
 
274
  # Persist history (capped at MAX_HISTORY messages)
275
  history.append({"role": "user", "content": request.message.strip()})
276
  history.append({"role": "assistant", "content": reply})
277
  session_memory[request.session_id] = history[-MAX_HISTORY:]
278
 
279
+ # Evict oldest sessions if memory grows too large
280
+ if len(session_memory) > MAX_SESSIONS:
281
+ oldest = next(iter(session_memory))
282
+ del session_memory[oldest]
283
+
284
  return ChatResponse(reply=reply, session_id=request.session_id)
285
 
286
  except Exception as e:
 
290
  if "rate_limit_exceeded" in err or "429" in err:
291
  return ChatResponse(
292
  reply=(
293
+ "Our AI assistant is temporarily busy. "
294
+ "Please try again in a moment.\n\n"
295
  "Or contact us directly:\n"
296
  "πŸ“§ contact@vibhusolutions.com\n"
297
  "πŸ“ž +91 9380345108"
ingest.py CHANGED
@@ -61,7 +61,11 @@ def main():
61
  print(f"Loaded {len(docs)} document(s).")
62
 
63
  print("Splitting into chunks...")
64
- splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
 
 
 
 
65
  chunks = splitter.split_documents(docs)
66
  print(f"Created {len(chunks)} chunks.")
67
 
 
61
  print(f"Loaded {len(docs)} document(s).")
62
 
63
  print("Splitting into chunks...")
64
+ splitter = RecursiveCharacterTextSplitter(
65
+ chunk_size=800,
66
+ chunk_overlap=100,
67
+ separators=["\n\nQ:", "\n\n---\n", "\n\n", "\n", " ", ""],
68
+ )
69
  chunks = splitter.split_documents(docs)
70
  print(f"Created {len(chunks)} chunks.")
71