Spaces:
Running
Running
Update app.py
Browse filesAdd memory context
app.py
CHANGED
|
@@ -108,7 +108,7 @@ def retrieve_context_per_file(question, state, k=10):
|
|
| 108 |
# Get top-k indices for this file
|
| 109 |
top_k_idx = distances.argsort()[: min(k, len(chunks))]
|
| 110 |
|
| 111 |
-
#
|
| 112 |
selected = [f"[{filename}]\n{chunks[i]}" for i in top_k_idx]
|
| 113 |
all_selected.extend(selected)
|
| 114 |
|
|
@@ -158,7 +158,6 @@ def load_pdf(files, state):
|
|
| 158 |
"""
|
| 159 |
files: current list from the UI (after add/remove).
|
| 160 |
We rebuild state from scratch to stay in sync.
|
| 161 |
-
State structure:
|
| 162 |
|
| 163 |
state = {
|
| 164 |
"files": {
|
|
@@ -169,7 +168,9 @@ def load_pdf(files, state):
|
|
| 169 |
...
|
| 170 |
},
|
| 171 |
"pdf_count": int,
|
| 172 |
-
"total_chunks": int
|
|
|
|
|
|
|
| 173 |
}
|
| 174 |
"""
|
| 175 |
# If user cleared all files
|
|
@@ -270,14 +271,58 @@ def answer_question(question, state):
|
|
| 270 |
else:
|
| 271 |
print("HF_TOKEN is None or empty")
|
| 272 |
|
| 273 |
-
|
|
|
|
| 274 |
return "Please enter a question."
|
| 275 |
|
| 276 |
if not state["files"]:
|
| 277 |
return "Please upload at least one PDF first."
|
| 278 |
|
| 279 |
-
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
return answer
|
| 282 |
|
| 283 |
|
|
@@ -293,6 +338,8 @@ with gr.Blocks(title="PDF Q&A Chatbot") as demo:
|
|
| 293 |
"files": {}, # filename -> {chunks, embeddings}
|
| 294 |
"pdf_count": 0,
|
| 295 |
"total_chunks": 0,
|
|
|
|
|
|
|
| 296 |
}
|
| 297 |
)
|
| 298 |
|
|
|
|
| 108 |
# Get top-k indices for this file
|
| 109 |
top_k_idx = distances.argsort()[: min(k, len(chunks))]
|
| 110 |
|
| 111 |
+
# Tag chunks with filename for transparency
|
| 112 |
selected = [f"[{filename}]\n{chunks[i]}" for i in top_k_idx]
|
| 113 |
all_selected.extend(selected)
|
| 114 |
|
|
|
|
| 158 |
"""
|
| 159 |
files: current list from the UI (after add/remove).
|
| 160 |
We rebuild state from scratch to stay in sync.
|
|
|
|
| 161 |
|
| 162 |
state = {
|
| 163 |
"files": {
|
|
|
|
| 168 |
...
|
| 169 |
},
|
| 170 |
"pdf_count": int,
|
| 171 |
+
"total_chunks": int,
|
| 172 |
+
"history": [...],
|
| 173 |
+
"facts": [...]
|
| 174 |
}
|
| 175 |
"""
|
| 176 |
# If user cleared all files
|
|
|
|
| 271 |
else:
|
| 272 |
print("HF_TOKEN is None or empty")
|
| 273 |
|
| 274 |
+
question = question.strip()
|
| 275 |
+
if not question:
|
| 276 |
return "Please enter a question."
|
| 277 |
|
| 278 |
if not state["files"]:
|
| 279 |
return "Please upload at least one PDF first."
|
| 280 |
|
| 281 |
+
# Simple persistent memory hook: "Remember that ..."
|
| 282 |
+
lower_q = question.lower()
|
| 283 |
+
if lower_q.startswith("remember that"):
|
| 284 |
+
fact = question[len("remember that"):].strip(" .")
|
| 285 |
+
if fact:
|
| 286 |
+
state["facts"].append(fact)
|
| 287 |
+
return f"Okay, I will remember that {fact}."
|
| 288 |
+
else:
|
| 289 |
+
return "What should I remember?"
|
| 290 |
+
|
| 291 |
+
# RAG context (per-file balanced retrieval)
|
| 292 |
+
rag_context = retrieve_context_per_file(question, state, k=10)
|
| 293 |
+
|
| 294 |
+
# Conversation memory (last 5 turns)
|
| 295 |
+
memory_context = ""
|
| 296 |
+
if state["history"]:
|
| 297 |
+
memory_context = "\n\n".join(
|
| 298 |
+
[
|
| 299 |
+
f"User: {h['user']}\nAssistant: {h['assistant']}"
|
| 300 |
+
for h in state["history"][-5:]
|
| 301 |
+
]
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
# Persistent facts
|
| 305 |
+
facts_context = ""
|
| 306 |
+
if state["facts"]:
|
| 307 |
+
facts_context = "User Facts:\n" + "\n".join(state["facts"]) + "\n\n"
|
| 308 |
+
|
| 309 |
+
# Merge all contexts
|
| 310 |
+
full_context_parts = []
|
| 311 |
+
if facts_context:
|
| 312 |
+
full_context_parts.append(facts_context)
|
| 313 |
+
if memory_context:
|
| 314 |
+
full_context_parts.append("Conversation Memory:\n" + memory_context)
|
| 315 |
+
if rag_context:
|
| 316 |
+
full_context_parts.append("Document Context:\n" + rag_context)
|
| 317 |
+
|
| 318 |
+
full_context = "\n\n".join(full_context_parts)
|
| 319 |
+
|
| 320 |
+
# Generate answer
|
| 321 |
+
answer = generate_answer(question, full_context)
|
| 322 |
+
|
| 323 |
+
# Save conversational memory
|
| 324 |
+
state["history"].append({"user": question, "assistant": answer})
|
| 325 |
+
|
| 326 |
return answer
|
| 327 |
|
| 328 |
|
|
|
|
| 338 |
"files": {}, # filename -> {chunks, embeddings}
|
| 339 |
"pdf_count": 0,
|
| 340 |
"total_chunks": 0,
|
| 341 |
+
"history": [], # conversational memory
|
| 342 |
+
"facts": [], # persistent user facts
|
| 343 |
}
|
| 344 |
)
|
| 345 |
|