Sentence Similarity
Transformers
Safetensors
Hindi
hindi
embeddings
sentence-embeddings
semantic-search
text-similarity
Instructions to use convaiinnovations/hindi-embedding-model-final with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use convaiinnovations/hindi-embedding-model-final with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("convaiinnovations/hindi-embedding-model-final", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload Hindi embeddings model and all associated files
Browse files- hindi-rag-system.py +181 -10
- hindi-rag-system.py.amltmp +181 -10
hindi-rag-system.py
CHANGED
|
@@ -16,6 +16,12 @@ from langchain.vectorstores import FAISS as LangchainFAISS
|
|
| 16 |
from langchain.docstore.document import Document
|
| 17 |
from langchain.embeddings.base import Embeddings
|
| 18 |
from typing import List, Dict, Any, Optional, Callable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
# Tokenizer wrapper class - same as in original code
|
| 21 |
class SentencePieceTokenizerWrapper:
|
|
@@ -702,6 +708,107 @@ def perform_similarity_search(vector_store, query, k=6):
|
|
| 702 |
print(f"Searching for: {query}")
|
| 703 |
return vector_store.similarity_search_with_score(query, k=k)
|
| 704 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 705 |
# Main RAG functions
|
| 706 |
def index_text_files(model, tokenizer, data_dir, output_dir, device="cuda", chunk_size=500):
|
| 707 |
"""
|
|
@@ -795,7 +902,7 @@ def query_text_corpus(model, tokenizer, vector_store_path, query, k=6, device="c
|
|
| 795 |
|
| 796 |
processed_results.append((combined_doc, score))
|
| 797 |
|
| 798 |
-
return processed_results
|
| 799 |
|
| 800 |
def main():
|
| 801 |
parser = argparse.ArgumentParser(description="Hindi RAG System with LangChain and FAISS")
|
|
@@ -821,27 +928,54 @@ def main():
|
|
| 821 |
help="Run in interactive mode for querying")
|
| 822 |
parser.add_argument("--reindex", action="store_true",
|
| 823 |
help="Force reindexing even if index exists")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 824 |
args = parser.parse_args()
|
| 825 |
|
| 826 |
-
# Load model and tokenizer
|
| 827 |
-
|
| 828 |
|
| 829 |
-
# Move model to device
|
| 830 |
-
|
| 831 |
|
| 832 |
# Create vector store path
|
| 833 |
vector_store_path = os.path.join(args.output_dir, "faiss_index")
|
| 834 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
if args.index or args.reindex:
|
| 836 |
# Index text files
|
| 837 |
-
|
|
|
|
|
|
|
| 838 |
print(f"Indexing complete. Vector store saved to {vector_store_path}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
|
| 840 |
if args.query:
|
| 841 |
# Query the corpus
|
| 842 |
-
results = query_text_corpus(
|
|
|
|
|
|
|
| 843 |
|
| 844 |
-
# Print results
|
| 845 |
print("\nSearch Results:")
|
| 846 |
for i, (doc, score) in enumerate(results):
|
| 847 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
@@ -850,10 +984,27 @@ def main():
|
|
| 850 |
# Extract and print only relevant sentences
|
| 851 |
relevant_text = extract_relevant_sentences(doc.page_content, args.query)
|
| 852 |
print(f"Content: {relevant_text}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 853 |
|
| 854 |
if args.interactive:
|
| 855 |
print("\nInteractive mode. Enter queries (or type 'quit' to exit).")
|
| 856 |
|
|
|
|
|
|
|
|
|
|
| 857 |
while True:
|
| 858 |
print("\nEnter query:")
|
| 859 |
query = input()
|
|
@@ -865,9 +1016,11 @@ def main():
|
|
| 865 |
break
|
| 866 |
|
| 867 |
# Query the corpus
|
| 868 |
-
results = query_text_corpus(
|
|
|
|
|
|
|
| 869 |
|
| 870 |
-
# Print results
|
| 871 |
print("\nSearch Results:")
|
| 872 |
for i, (doc, score) in enumerate(results):
|
| 873 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
@@ -876,6 +1029,24 @@ def main():
|
|
| 876 |
# Extract and print only relevant sentences
|
| 877 |
relevant_text = extract_relevant_sentences(doc.page_content, query)
|
| 878 |
print(f"Content: {relevant_text}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 879 |
|
| 880 |
if __name__ == "__main__":
|
| 881 |
main()
|
|
|
|
| 16 |
from langchain.docstore.document import Document
|
| 17 |
from langchain.embeddings.base import Embeddings
|
| 18 |
from typing import List, Dict, Any, Optional, Callable
|
| 19 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
| 20 |
+
import gc
|
| 21 |
+
import warnings
|
| 22 |
+
|
| 23 |
+
# Ignore specific HuggingFace warnings
|
| 24 |
+
warnings.filterwarnings("ignore", category=UserWarning, message=".*The model doesn't have tied token embeddings.*")
|
| 25 |
|
| 26 |
# Tokenizer wrapper class - same as in original code
|
| 27 |
class SentencePieceTokenizerWrapper:
|
|
|
|
| 708 |
print(f"Searching for: {query}")
|
| 709 |
return vector_store.similarity_search_with_score(query, k=k)
|
| 710 |
|
| 711 |
+
# Llama model loading function
|
| 712 |
+
def load_llama_model(model_name="unsloth/Llama-3.2-1B-Instruct", device="cuda"):
|
| 713 |
+
"""
|
| 714 |
+
Load and prepare Llama model for text generation
|
| 715 |
+
"""
|
| 716 |
+
print(f"Loading LLM: {model_name}")
|
| 717 |
+
|
| 718 |
+
# Check if CUDA is available
|
| 719 |
+
if device == "cuda" and not torch.cuda.is_available():
|
| 720 |
+
print("CUDA not available, falling back to CPU")
|
| 721 |
+
device = "cpu"
|
| 722 |
+
|
| 723 |
+
# Quantization config for 4-bit precision to save memory
|
| 724 |
+
quantization = BitsAndBytesConfig(
|
| 725 |
+
load_in_4bit=True,
|
| 726 |
+
bnb_4bit_compute_dtype=torch.float16,
|
| 727 |
+
bnb_4bit_quant_type="nf4",
|
| 728 |
+
bnb_4bit_use_double_quant=True,
|
| 729 |
+
) if device == "cuda" else None
|
| 730 |
+
|
| 731 |
+
# Standard HuggingFace loading
|
| 732 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 733 |
+
if device == "cuda":
|
| 734 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 735 |
+
model_name,
|
| 736 |
+
device_map="auto",
|
| 737 |
+
quantization_config=quantization
|
| 738 |
+
)
|
| 739 |
+
else:
|
| 740 |
+
model = AutoModelForCausalLM.from_pretrained(model_name)
|
| 741 |
+
model = model.to(device)
|
| 742 |
+
|
| 743 |
+
print("Successfully loaded model")
|
| 744 |
+
|
| 745 |
+
return model, tokenizer
|
| 746 |
+
|
| 747 |
+
def setup_qa_system(model, tokenizer, vector_store):
|
| 748 |
+
"""
|
| 749 |
+
Set up a direct QA system using the model and retriever
|
| 750 |
+
"""
|
| 751 |
+
# Create retriever
|
| 752 |
+
retriever = vector_store.as_retriever(
|
| 753 |
+
search_type="similarity",
|
| 754 |
+
search_kwargs={"k": 3}
|
| 755 |
+
)
|
| 756 |
+
|
| 757 |
+
# Create a function to generate answers
|
| 758 |
+
def generate_answer(query):
|
| 759 |
+
# Retrieve documents
|
| 760 |
+
try:
|
| 761 |
+
docs = retriever.invoke(query)
|
| 762 |
+
except:
|
| 763 |
+
# Fallback to older method if invoke isn't available
|
| 764 |
+
docs = retriever.get_relevant_documents(query)
|
| 765 |
+
|
| 766 |
+
# Extract the content
|
| 767 |
+
context = "\n\n".join([doc.page_content for doc in docs])
|
| 768 |
+
|
| 769 |
+
# Create prompt
|
| 770 |
+
prompt = f"""
|
| 771 |
+
आपको निम्नलिखित संदर्भ से जानकारी के आधार पर एक प्रश्न का उत्तर देना है।
|
| 772 |
+
यदि आप उत्तर नहीं जानते हैं, तो बस "मुझे नहीं पता" कहें।
|
| 773 |
+
|
| 774 |
+
संदर्भ:
|
| 775 |
+
{context}
|
| 776 |
+
|
| 777 |
+
प्रश्न: {query}
|
| 778 |
+
|
| 779 |
+
उत्तर:
|
| 780 |
+
"""
|
| 781 |
+
|
| 782 |
+
# Generate text
|
| 783 |
+
inputs = tokenizer(prompt, return_tensors="pt")
|
| 784 |
+
|
| 785 |
+
# Move to the same device as the model
|
| 786 |
+
for k, v in inputs.items():
|
| 787 |
+
if hasattr(v, "to") and callable(v.to):
|
| 788 |
+
inputs[k] = v.to(model.device)
|
| 789 |
+
|
| 790 |
+
with torch.no_grad():
|
| 791 |
+
try:
|
| 792 |
+
outputs = model.generate(
|
| 793 |
+
inputs.input_ids,
|
| 794 |
+
max_new_tokens=512,
|
| 795 |
+
temperature=0.7,
|
| 796 |
+
top_p=0.9,
|
| 797 |
+
do_sample=True
|
| 798 |
+
)
|
| 799 |
+
except Exception as e:
|
| 800 |
+
return f"Error generating response: {str(e)}"
|
| 801 |
+
|
| 802 |
+
# Decode the generated text
|
| 803 |
+
full_response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 804 |
+
|
| 805 |
+
# Extract just the answer part (after the prompt)
|
| 806 |
+
answer = full_response.split("उत्तर:")[-1].strip()
|
| 807 |
+
|
| 808 |
+
return answer
|
| 809 |
+
|
| 810 |
+
return generate_answer
|
| 811 |
+
|
| 812 |
# Main RAG functions
|
| 813 |
def index_text_files(model, tokenizer, data_dir, output_dir, device="cuda", chunk_size=500):
|
| 814 |
"""
|
|
|
|
| 902 |
|
| 903 |
processed_results.append((combined_doc, score))
|
| 904 |
|
| 905 |
+
return processed_results, vector_store
|
| 906 |
|
| 907 |
def main():
|
| 908 |
parser = argparse.ArgumentParser(description="Hindi RAG System with LangChain and FAISS")
|
|
|
|
| 928 |
help="Run in interactive mode for querying")
|
| 929 |
parser.add_argument("--reindex", action="store_true",
|
| 930 |
help="Force reindexing even if index exists")
|
| 931 |
+
parser.add_argument("--qa", action="store_true",
|
| 932 |
+
help="Use LLM for question answering instead of just retrieval")
|
| 933 |
+
parser.add_argument("--llm_name", type=str, default="unsloth/Llama-3.2-1B-Instruct",
|
| 934 |
+
help="HuggingFace model name for the LLM")
|
| 935 |
args = parser.parse_args()
|
| 936 |
|
| 937 |
+
# Load embedding model and tokenizer
|
| 938 |
+
embed_model, embed_tokenizer, config = load_model_and_tokenizer(args.model_dir, args.tokenizer_dir)
|
| 939 |
|
| 940 |
+
# Move embedding model to device
|
| 941 |
+
embed_model = embed_model.to(args.device)
|
| 942 |
|
| 943 |
# Create vector store path
|
| 944 |
vector_store_path = os.path.join(args.output_dir, "faiss_index")
|
| 945 |
|
| 946 |
+
# Load LLM if QA is enabled
|
| 947 |
+
llm_model = None
|
| 948 |
+
llm_tokenizer = None
|
| 949 |
+
qa_generator = None
|
| 950 |
+
|
| 951 |
+
if args.qa:
|
| 952 |
+
try:
|
| 953 |
+
# Load LLM
|
| 954 |
+
llm_model, llm_tokenizer = load_llama_model(args.llm_name, args.device)
|
| 955 |
+
print("LLM loaded successfully for QA")
|
| 956 |
+
except Exception as e:
|
| 957 |
+
print(f"Error loading LLM: {e}")
|
| 958 |
+
print("Falling back to retrieval-only mode")
|
| 959 |
+
args.qa = False
|
| 960 |
+
|
| 961 |
if args.index or args.reindex:
|
| 962 |
# Index text files
|
| 963 |
+
vector_store, _ = index_text_files(
|
| 964 |
+
embed_model, embed_tokenizer, args.data_dir, args.output_dir, args.device, args.chunk_size
|
| 965 |
+
)
|
| 966 |
print(f"Indexing complete. Vector store saved to {vector_store_path}")
|
| 967 |
+
|
| 968 |
+
# Set up QA chain if enabled
|
| 969 |
+
if args.qa and llm_model is not None and llm_tokenizer is not None:
|
| 970 |
+
qa_generator = setup_qa_system(llm_model, llm_tokenizer, vector_store)
|
| 971 |
|
| 972 |
if args.query:
|
| 973 |
# Query the corpus
|
| 974 |
+
results, vector_store = query_text_corpus(
|
| 975 |
+
embed_model, embed_tokenizer, vector_store_path, args.query, args.top_k, args.device
|
| 976 |
+
)
|
| 977 |
|
| 978 |
+
# Print retrieval results
|
| 979 |
print("\nSearch Results:")
|
| 980 |
for i, (doc, score) in enumerate(results):
|
| 981 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
|
|
| 984 |
# Extract and print only relevant sentences
|
| 985 |
relevant_text = extract_relevant_sentences(doc.page_content, args.query)
|
| 986 |
print(f"Content: {relevant_text}")
|
| 987 |
+
|
| 988 |
+
# If QA is enabled, also answer the question using the LLM
|
| 989 |
+
if args.qa and llm_model is not None and llm_tokenizer is not None:
|
| 990 |
+
if qa_generator is None:
|
| 991 |
+
qa_generator = setup_qa_system(llm_model, llm_tokenizer, vector_store)
|
| 992 |
+
|
| 993 |
+
# Get answer from QA chain
|
| 994 |
+
print("\nGenerating answer using LLM...")
|
| 995 |
+
try:
|
| 996 |
+
answer = qa_generator(args.query)
|
| 997 |
+
print("\nLLM Answer:")
|
| 998 |
+
print(answer)
|
| 999 |
+
except Exception as e:
|
| 1000 |
+
print(f"Error generating answer: {e}")
|
| 1001 |
|
| 1002 |
if args.interactive:
|
| 1003 |
print("\nInteractive mode. Enter queries (or type 'quit' to exit).")
|
| 1004 |
|
| 1005 |
+
# For the first query, load vector store
|
| 1006 |
+
vector_store = None
|
| 1007 |
+
|
| 1008 |
while True:
|
| 1009 |
print("\nEnter query:")
|
| 1010 |
query = input()
|
|
|
|
| 1016 |
break
|
| 1017 |
|
| 1018 |
# Query the corpus
|
| 1019 |
+
results, vector_store = query_text_corpus(
|
| 1020 |
+
embed_model, embed_tokenizer, vector_store_path, query, args.top_k, args.device
|
| 1021 |
+
)
|
| 1022 |
|
| 1023 |
+
# Print retrieval results
|
| 1024 |
print("\nSearch Results:")
|
| 1025 |
for i, (doc, score) in enumerate(results):
|
| 1026 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
|
|
| 1029 |
# Extract and print only relevant sentences
|
| 1030 |
relevant_text = extract_relevant_sentences(doc.page_content, query)
|
| 1031 |
print(f"Content: {relevant_text}")
|
| 1032 |
+
|
| 1033 |
+
# If QA is enabled, also answer the question using the LLM
|
| 1034 |
+
if args.qa and llm_model is not None and llm_tokenizer is not None:
|
| 1035 |
+
if qa_generator is None:
|
| 1036 |
+
qa_generator = setup_qa_system(llm_model, llm_tokenizer, vector_store)
|
| 1037 |
+
|
| 1038 |
+
print("\nGenerating answer using LLM...")
|
| 1039 |
+
try:
|
| 1040 |
+
answer = qa_generator(query)
|
| 1041 |
+
print("\nLLM Answer:")
|
| 1042 |
+
print(answer)
|
| 1043 |
+
except Exception as e:
|
| 1044 |
+
print(f"Error generating answer: {e}")
|
| 1045 |
+
|
| 1046 |
+
# Clean up GPU memory
|
| 1047 |
+
if args.device == "cuda":
|
| 1048 |
+
gc.collect()
|
| 1049 |
+
torch.cuda.empty_cache()
|
| 1050 |
|
| 1051 |
if __name__ == "__main__":
|
| 1052 |
main()
|
hindi-rag-system.py.amltmp
CHANGED
|
@@ -16,6 +16,12 @@ from langchain.vectorstores import FAISS as LangchainFAISS
|
|
| 16 |
from langchain.docstore.document import Document
|
| 17 |
from langchain.embeddings.base import Embeddings
|
| 18 |
from typing import List, Dict, Any, Optional, Callable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
# Tokenizer wrapper class - same as in original code
|
| 21 |
class SentencePieceTokenizerWrapper:
|
|
@@ -702,6 +708,107 @@ def perform_similarity_search(vector_store, query, k=6):
|
|
| 702 |
print(f"Searching for: {query}")
|
| 703 |
return vector_store.similarity_search_with_score(query, k=k)
|
| 704 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 705 |
# Main RAG functions
|
| 706 |
def index_text_files(model, tokenizer, data_dir, output_dir, device="cuda", chunk_size=500):
|
| 707 |
"""
|
|
@@ -795,7 +902,7 @@ def query_text_corpus(model, tokenizer, vector_store_path, query, k=6, device="c
|
|
| 795 |
|
| 796 |
processed_results.append((combined_doc, score))
|
| 797 |
|
| 798 |
-
return processed_results
|
| 799 |
|
| 800 |
def main():
|
| 801 |
parser = argparse.ArgumentParser(description="Hindi RAG System with LangChain and FAISS")
|
|
@@ -821,27 +928,54 @@ def main():
|
|
| 821 |
help="Run in interactive mode for querying")
|
| 822 |
parser.add_argument("--reindex", action="store_true",
|
| 823 |
help="Force reindexing even if index exists")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 824 |
args = parser.parse_args()
|
| 825 |
|
| 826 |
-
# Load model and tokenizer
|
| 827 |
-
|
| 828 |
|
| 829 |
-
# Move model to device
|
| 830 |
-
|
| 831 |
|
| 832 |
# Create vector store path
|
| 833 |
vector_store_path = os.path.join(args.output_dir, "faiss_index")
|
| 834 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
if args.index or args.reindex:
|
| 836 |
# Index text files
|
| 837 |
-
|
|
|
|
|
|
|
| 838 |
print(f"Indexing complete. Vector store saved to {vector_store_path}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
|
| 840 |
if args.query:
|
| 841 |
# Query the corpus
|
| 842 |
-
results = query_text_corpus(
|
|
|
|
|
|
|
| 843 |
|
| 844 |
-
# Print results
|
| 845 |
print("\nSearch Results:")
|
| 846 |
for i, (doc, score) in enumerate(results):
|
| 847 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
@@ -850,10 +984,27 @@ def main():
|
|
| 850 |
# Extract and print only relevant sentences
|
| 851 |
relevant_text = extract_relevant_sentences(doc.page_content, args.query)
|
| 852 |
print(f"Content: {relevant_text}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 853 |
|
| 854 |
if args.interactive:
|
| 855 |
print("\nInteractive mode. Enter queries (or type 'quit' to exit).")
|
| 856 |
|
|
|
|
|
|
|
|
|
|
| 857 |
while True:
|
| 858 |
print("\nEnter query:")
|
| 859 |
query = input()
|
|
@@ -865,9 +1016,11 @@ def main():
|
|
| 865 |
break
|
| 866 |
|
| 867 |
# Query the corpus
|
| 868 |
-
results = query_text_corpus(
|
|
|
|
|
|
|
| 869 |
|
| 870 |
-
# Print results
|
| 871 |
print("\nSearch Results:")
|
| 872 |
for i, (doc, score) in enumerate(results):
|
| 873 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
@@ -876,6 +1029,24 @@ def main():
|
|
| 876 |
# Extract and print only relevant sentences
|
| 877 |
relevant_text = extract_relevant_sentences(doc.page_content, query)
|
| 878 |
print(f"Content: {relevant_text}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 879 |
|
| 880 |
if __name__ == "__main__":
|
| 881 |
main()
|
|
|
|
| 16 |
from langchain.docstore.document import Document
|
| 17 |
from langchain.embeddings.base import Embeddings
|
| 18 |
from typing import List, Dict, Any, Optional, Callable
|
| 19 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
| 20 |
+
import gc
|
| 21 |
+
import warnings
|
| 22 |
+
|
| 23 |
+
# Ignore specific HuggingFace warnings
|
| 24 |
+
warnings.filterwarnings("ignore", category=UserWarning, message=".*The model doesn't have tied token embeddings.*")
|
| 25 |
|
| 26 |
# Tokenizer wrapper class - same as in original code
|
| 27 |
class SentencePieceTokenizerWrapper:
|
|
|
|
| 708 |
print(f"Searching for: {query}")
|
| 709 |
return vector_store.similarity_search_with_score(query, k=k)
|
| 710 |
|
| 711 |
+
# Llama model loading function
|
| 712 |
+
def load_llama_model(model_name="unsloth/Llama-3.2-1B-Instruct", device="cuda"):
|
| 713 |
+
"""
|
| 714 |
+
Load and prepare Llama model for text generation
|
| 715 |
+
"""
|
| 716 |
+
print(f"Loading LLM: {model_name}")
|
| 717 |
+
|
| 718 |
+
# Check if CUDA is available
|
| 719 |
+
if device == "cuda" and not torch.cuda.is_available():
|
| 720 |
+
print("CUDA not available, falling back to CPU")
|
| 721 |
+
device = "cpu"
|
| 722 |
+
|
| 723 |
+
# Quantization config for 4-bit precision to save memory
|
| 724 |
+
quantization = BitsAndBytesConfig(
|
| 725 |
+
load_in_4bit=True,
|
| 726 |
+
bnb_4bit_compute_dtype=torch.float16,
|
| 727 |
+
bnb_4bit_quant_type="nf4",
|
| 728 |
+
bnb_4bit_use_double_quant=True,
|
| 729 |
+
) if device == "cuda" else None
|
| 730 |
+
|
| 731 |
+
# Standard HuggingFace loading
|
| 732 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 733 |
+
if device == "cuda":
|
| 734 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 735 |
+
model_name,
|
| 736 |
+
device_map="auto",
|
| 737 |
+
quantization_config=quantization
|
| 738 |
+
)
|
| 739 |
+
else:
|
| 740 |
+
model = AutoModelForCausalLM.from_pretrained(model_name)
|
| 741 |
+
model = model.to(device)
|
| 742 |
+
|
| 743 |
+
print("Successfully loaded model")
|
| 744 |
+
|
| 745 |
+
return model, tokenizer
|
| 746 |
+
|
| 747 |
+
def setup_qa_system(model, tokenizer, vector_store):
|
| 748 |
+
"""
|
| 749 |
+
Set up a direct QA system using the model and retriever
|
| 750 |
+
"""
|
| 751 |
+
# Create retriever
|
| 752 |
+
retriever = vector_store.as_retriever(
|
| 753 |
+
search_type="similarity",
|
| 754 |
+
search_kwargs={"k": 3}
|
| 755 |
+
)
|
| 756 |
+
|
| 757 |
+
# Create a function to generate answers
|
| 758 |
+
def generate_answer(query):
|
| 759 |
+
# Retrieve documents
|
| 760 |
+
try:
|
| 761 |
+
docs = retriever.invoke(query)
|
| 762 |
+
except:
|
| 763 |
+
# Fallback to older method if invoke isn't available
|
| 764 |
+
docs = retriever.get_relevant_documents(query)
|
| 765 |
+
|
| 766 |
+
# Extract the content
|
| 767 |
+
context = "\n\n".join([doc.page_content for doc in docs])
|
| 768 |
+
|
| 769 |
+
# Create prompt
|
| 770 |
+
prompt = f"""
|
| 771 |
+
आपको निम्नलिखित संदर्भ से जानकारी के आधार पर एक प्रश्न का उत्तर देना है।
|
| 772 |
+
यदि आप उत्तर नहीं जानते हैं, तो बस "मुझे नहीं पता" कहें।
|
| 773 |
+
|
| 774 |
+
संदर्भ:
|
| 775 |
+
{context}
|
| 776 |
+
|
| 777 |
+
प्रश्न: {query}
|
| 778 |
+
|
| 779 |
+
उत्तर:
|
| 780 |
+
"""
|
| 781 |
+
|
| 782 |
+
# Generate text
|
| 783 |
+
inputs = tokenizer(prompt, return_tensors="pt")
|
| 784 |
+
|
| 785 |
+
# Move to the same device as the model
|
| 786 |
+
for k, v in inputs.items():
|
| 787 |
+
if hasattr(v, "to") and callable(v.to):
|
| 788 |
+
inputs[k] = v.to(model.device)
|
| 789 |
+
|
| 790 |
+
with torch.no_grad():
|
| 791 |
+
try:
|
| 792 |
+
outputs = model.generate(
|
| 793 |
+
inputs.input_ids,
|
| 794 |
+
max_new_tokens=512,
|
| 795 |
+
temperature=0.7,
|
| 796 |
+
top_p=0.9,
|
| 797 |
+
do_sample=True
|
| 798 |
+
)
|
| 799 |
+
except Exception as e:
|
| 800 |
+
return f"Error generating response: {str(e)}"
|
| 801 |
+
|
| 802 |
+
# Decode the generated text
|
| 803 |
+
full_response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 804 |
+
|
| 805 |
+
# Extract just the answer part (after the prompt)
|
| 806 |
+
answer = full_response.split("उत्तर:")[-1].strip()
|
| 807 |
+
|
| 808 |
+
return answer
|
| 809 |
+
|
| 810 |
+
return generate_answer
|
| 811 |
+
|
| 812 |
# Main RAG functions
|
| 813 |
def index_text_files(model, tokenizer, data_dir, output_dir, device="cuda", chunk_size=500):
|
| 814 |
"""
|
|
|
|
| 902 |
|
| 903 |
processed_results.append((combined_doc, score))
|
| 904 |
|
| 905 |
+
return processed_results, vector_store
|
| 906 |
|
| 907 |
def main():
|
| 908 |
parser = argparse.ArgumentParser(description="Hindi RAG System with LangChain and FAISS")
|
|
|
|
| 928 |
help="Run in interactive mode for querying")
|
| 929 |
parser.add_argument("--reindex", action="store_true",
|
| 930 |
help="Force reindexing even if index exists")
|
| 931 |
+
parser.add_argument("--qa", action="store_true",
|
| 932 |
+
help="Use LLM for question answering instead of just retrieval")
|
| 933 |
+
parser.add_argument("--llm_name", type=str, default="unsloth/Llama-3.2-1B-Instruct",
|
| 934 |
+
help="HuggingFace model name for the LLM")
|
| 935 |
args = parser.parse_args()
|
| 936 |
|
| 937 |
+
# Load embedding model and tokenizer
|
| 938 |
+
embed_model, embed_tokenizer, config = load_model_and_tokenizer(args.model_dir, args.tokenizer_dir)
|
| 939 |
|
| 940 |
+
# Move embedding model to device
|
| 941 |
+
embed_model = embed_model.to(args.device)
|
| 942 |
|
| 943 |
# Create vector store path
|
| 944 |
vector_store_path = os.path.join(args.output_dir, "faiss_index")
|
| 945 |
|
| 946 |
+
# Load LLM if QA is enabled
|
| 947 |
+
llm_model = None
|
| 948 |
+
llm_tokenizer = None
|
| 949 |
+
qa_generator = None
|
| 950 |
+
|
| 951 |
+
if args.qa:
|
| 952 |
+
try:
|
| 953 |
+
# Load LLM
|
| 954 |
+
llm_model, llm_tokenizer = load_llama_model(args.llm_name, args.device)
|
| 955 |
+
print("LLM loaded successfully for QA")
|
| 956 |
+
except Exception as e:
|
| 957 |
+
print(f"Error loading LLM: {e}")
|
| 958 |
+
print("Falling back to retrieval-only mode")
|
| 959 |
+
args.qa = False
|
| 960 |
+
|
| 961 |
if args.index or args.reindex:
|
| 962 |
# Index text files
|
| 963 |
+
vector_store, _ = index_text_files(
|
| 964 |
+
embed_model, embed_tokenizer, args.data_dir, args.output_dir, args.device, args.chunk_size
|
| 965 |
+
)
|
| 966 |
print(f"Indexing complete. Vector store saved to {vector_store_path}")
|
| 967 |
+
|
| 968 |
+
# Set up QA chain if enabled
|
| 969 |
+
if args.qa and llm_model is not None and llm_tokenizer is not None:
|
| 970 |
+
qa_generator = setup_qa_system(llm_model, llm_tokenizer, vector_store)
|
| 971 |
|
| 972 |
if args.query:
|
| 973 |
# Query the corpus
|
| 974 |
+
results, vector_store = query_text_corpus(
|
| 975 |
+
embed_model, embed_tokenizer, vector_store_path, args.query, args.top_k, args.device
|
| 976 |
+
)
|
| 977 |
|
| 978 |
+
# Print retrieval results
|
| 979 |
print("\nSearch Results:")
|
| 980 |
for i, (doc, score) in enumerate(results):
|
| 981 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
|
|
| 984 |
# Extract and print only relevant sentences
|
| 985 |
relevant_text = extract_relevant_sentences(doc.page_content, args.query)
|
| 986 |
print(f"Content: {relevant_text}")
|
| 987 |
+
|
| 988 |
+
# If QA is enabled, also answer the question using the LLM
|
| 989 |
+
if args.qa and llm_model is not None and llm_tokenizer is not None:
|
| 990 |
+
if qa_generator is None:
|
| 991 |
+
qa_generator = setup_qa_system(llm_model, llm_tokenizer, vector_store)
|
| 992 |
+
|
| 993 |
+
# Get answer from QA chain
|
| 994 |
+
print("\nGenerating answer using LLM...")
|
| 995 |
+
try:
|
| 996 |
+
answer = qa_generator(args.query)
|
| 997 |
+
print("\nLLM Answer:")
|
| 998 |
+
print(answer)
|
| 999 |
+
except Exception as e:
|
| 1000 |
+
print(f"Error generating answer: {e}")
|
| 1001 |
|
| 1002 |
if args.interactive:
|
| 1003 |
print("\nInteractive mode. Enter queries (or type 'quit' to exit).")
|
| 1004 |
|
| 1005 |
+
# For the first query, load vector store
|
| 1006 |
+
vector_store = None
|
| 1007 |
+
|
| 1008 |
while True:
|
| 1009 |
print("\nEnter query:")
|
| 1010 |
query = input()
|
|
|
|
| 1016 |
break
|
| 1017 |
|
| 1018 |
# Query the corpus
|
| 1019 |
+
results, vector_store = query_text_corpus(
|
| 1020 |
+
embed_model, embed_tokenizer, vector_store_path, query, args.top_k, args.device
|
| 1021 |
+
)
|
| 1022 |
|
| 1023 |
+
# Print retrieval results
|
| 1024 |
print("\nSearch Results:")
|
| 1025 |
for i, (doc, score) in enumerate(results):
|
| 1026 |
print(f"\nResult {i+1} (Score: {score:.4f}):")
|
|
|
|
| 1029 |
# Extract and print only relevant sentences
|
| 1030 |
relevant_text = extract_relevant_sentences(doc.page_content, query)
|
| 1031 |
print(f"Content: {relevant_text}")
|
| 1032 |
+
|
| 1033 |
+
# If QA is enabled, also answer the question using the LLM
|
| 1034 |
+
if args.qa and llm_model is not None and llm_tokenizer is not None:
|
| 1035 |
+
if qa_generator is None:
|
| 1036 |
+
qa_generator = setup_qa_system(llm_model, llm_tokenizer, vector_store)
|
| 1037 |
+
|
| 1038 |
+
print("\nGenerating answer using LLM...")
|
| 1039 |
+
try:
|
| 1040 |
+
answer = qa_generator(query)
|
| 1041 |
+
print("\nLLM Answer:")
|
| 1042 |
+
print(answer)
|
| 1043 |
+
except Exception as e:
|
| 1044 |
+
print(f"Error generating answer: {e}")
|
| 1045 |
+
|
| 1046 |
+
# Clean up GPU memory
|
| 1047 |
+
if args.device == "cuda":
|
| 1048 |
+
gc.collect()
|
| 1049 |
+
torch.cuda.empty_cache()
|
| 1050 |
|
| 1051 |
if __name__ == "__main__":
|
| 1052 |
main()
|