Retrieve_Document_with_llama / retrieval_vectorless.py
laxuu's picture
retrieve data vectorless
0fb4d01
Raw
History Blame Contribute Delete
6.55 kB
import os
import datetime
import time
import gradio as gr
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Replacing vector components with a statistical BM25 (vectorless) retriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.llms import LlamaCpp
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
# -------------------------------------------------------------------------
# 1. Global Setup (LLM Loaded Once - Embeddings Removed)
# -------------------------------------------------------------------------
print("Initializing Local LLM Runtime (Vectorless Mode)...")
MODEL_PATH = "model/gemma-4-E2B-it-Q8_0.gguf" # Ensure this path is correct
llm = LlamaCpp(
model_path=MODEL_PATH,
n_ctx=4096,
temperature=0.1,
max_tokens=512,
verbose=False
)
# Build the rigid system prompt structure
system_prompt = (
"You are a strict Document Retrieval analysis assistant.\n"
"Answer the user's question using ONLY the following pieces of retrieved financial context. "
"If you do not know the answer or if it is not explicitly stated in the context, "
"state clearly that the book does not provide this information. Do not make up facts.\n\n"
"Context:\n{context}"
)
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}"),
])
question_answer_chain = create_stuff_documents_chain(llm, prompt)
# -------------------------------------------------------------------------
# 2. Core Processing Functions
# -------------------------------------------------------------------------
def process_pdf(pdf_file):
"""Loads, splits, and indexes the PDF using BM25 string-matching instead of vectors."""
if pdf_file is None:
return "No file uploaded.", None, ""
start_time = time.time()
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_output = f"[{current_time}] ๐Ÿ“ Loading PDF: {os.path.basename(pdf_file.name)}...\n"
try:
# Load and Split
loader = PyPDFLoader(pdf_file.name)
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
final_chunks = text_splitter.split_documents(docs)
log_output += f" -> Successfully generated {len(final_chunks)} semantic chunks.\n"
# Build Vectorless BM25 Text Index
log_output += f" -> Indexing text chunks into local BM25 Search Engine...\n"
bm25_retriever = BM25Retriever.from_documents(final_chunks)
# Configure to return top 3 keyword-matched document chunks
bm25_retriever.k = 3
elapsed = time.time() - start_time
log_output += f"โœ… Success! Vectorless processing finished in {elapsed:.2f} seconds."
return log_output, bm25_retriever, "PDF text indexed! You can now ask questions."
except Exception as e:
return f"โŒ Error processing PDF: {str(e)}", None, "Processing failed."
def answer_query(query, bm25_retriever, chat_history):
"""Retrieves context using BM25 and runs inference against the local GGUF model."""
current_time = datetime.datetime.now().strftime("%H:%M:%S")
if bm25_retriever is None:
chat_history.append((query, "โš ๏ธ Please upload and process a financial PDF first!"))
return "", chat_history
if not query.strip():
return "", chat_history
# Dynamically bind the pre-computed BM25 engine into the RAG chain
rag_chain = create_retrieval_chain(bm25_retriever, question_answer_chain)
# Run Inference
response = rag_chain.invoke({"input": query})
answer = response['answer']
# Format answer with timestamp marker
formatted_answer = f"[{current_time}]\n{answer}"
chat_history.append((query, formatted_answer))
return "", chat_history
# -------------------------------------------------------------------------
# 3. Gradio UI Architecture
# -------------------------------------------------------------------------
with gr.Blocks(theme=gr.themes.Soft()) as demo:
# State now holds the BM25Retriever object instead of a FAISS DB
retriever_state = gr.State(None)
gr.Markdown(
"""
# ๐Ÿฆ Secure Document Retrieve Vectorless RAG Engine
Analyze private documents completely offline using high-performance local AI and keyword-based retrieval.
"""
)
with gr.Row():
# Left Panel: Document Ingestion and Pipeline Metrics
with gr.Column(scale=1):
gr.Markdown("### 1. Document Ingestion")
file_input = gr.File(label="Upload PDF/ Book", file_types=[".pdf"])
process_btn = gr.Button("Build Text Keyword Base", variant="primary")
status_text = gr.Textbox(label="Engine Status", interactive=False, value="Awaiting document...")
log_monitor = gr.TextArea(label="Pipeline Terminal Logs", interactive=False, lines=8)
# Right Panel: Interactive Chat Interface
with gr.Column(scale=2):
gr.Markdown("### 2. Conversational Analysis")
chatbot = gr.Chatbot(label="Document Retrieval Chat", height=450)
with gr.Row():
query_input = gr.Textbox(
label="Ask a document retrieval question...",
placeholder="e.g., What is the primary difference between working capital and capital budgeting?",
scale=4
)
submit_btn = gr.Button("Submit Query", variant="secondary", scale=1)
# --- UI Event Wire-up ---
# File processing sequence
process_btn.click(
fn=process_pdf,
inputs=[file_input],
outputs=[log_monitor, retriever_state, status_text]
)
# Query execution sequences (Triggers on Click or hitting Enter)
submit_btn.click(
fn=answer_query,
inputs=[query_input, retriever_state, chatbot],
outputs=[query_input, chatbot]
)
query_input.submit(
fn=answer_query,
inputs=[query_input, retriever_state, chatbot],
outputs=[query_input, chatbot]
)
# Launch local server
if __name__ == "__main__":
demo.launch(server_name="127.0.0.1", server_port=7860, share=False)