File size: 7,623 Bytes
c3ce070
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
from langchain.schema import Document
import json
import os
import pickle
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from langchain_huggingface import HuggingFaceEndpoint
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from typing import Optional

_qa_chain: Optional[RetrievalQA] = None
_initialized = False


def init_rag():
    global _qa_chain, _initialized

    if _initialized:
        return

    HF_TOKEN = os.getenv("HF_TOKEN")
    if not HF_TOKEN:
        print(
            "⚠️  HF_TOKEN not found in environment variables. Set it in Spaces secrets or .env file"
        )

    documents = []

    try:
        with open("tnb_genelgeler_rag.json", "r", encoding="utf-8") as f:
            genelge_data = json.load(f)
        print(f"✅ Loaded {len(genelge_data)} chunks from tnb_genelgeler_rag.json")

        for item in genelge_data:
            if "source_type" not in item.get("metadata", {}):
                item.setdefault("metadata", {})["source_type"] = "genelge"
            documents.append(
                Document(
                    page_content=item.get("content", ""),
                    metadata=item.get("metadata", {}),
                )
            )

    except FileNotFoundError:
        print("⚠️  tnb_genelgeler_rag.json not found. Please upload data files.")

    try:
        with open("noterlik_kanunu_rag.json", "r", encoding="utf-8") as f:
            kanun_data = json.load(f)
        print(f"✅ Loaded {len(kanun_data)} chunks from noterlik_kanunu_rag.json")

        for item in kanun_data:
            documents.append(
                Document(
                    page_content=item.get("content", ""),
                    metadata=item.get("metadata", {}),
                )
            )

    except FileNotFoundError:
        print("⚠️  noterlik_kanunu_rag.json not found. Please upload data files.")

    if not documents:
        print("❌ No documents loaded. Please prepare data files first.")
        _initialized = False
        return

    print(f"📚 Total documents loaded: {len(documents)}")

    faiss_index_path = "faiss_index"

    print("🔄 Initializing embedding model (multilingual-e5-base)...")
    embedding_model = HuggingFaceEmbeddings(
        model_name="intfloat/multilingual-e5-base", encode_kwargs={"batch_size": 32}
    )
    print("✅ Embedding model initialized")

    # Load or create FAISS index
    if os.path.exists(faiss_index_path):
        print(f"✅ Found existing FAISS index at {faiss_index_path} — loading...")
        try:
            vector_db = FAISS.load_local(
                faiss_index_path, embedding_model, allow_dangerous_deserialization=True
            )
            print("✅ FAISS index loaded successfully!")
        except Exception as e:
            print(f"❌ Failed to load FAISS index: {e}")
            print(f"🔄 Creating new FAISS index...")
            vector_db = FAISS.from_documents(documents, embedding_model)
            vector_db.save_local(faiss_index_path)
            print(f"✅ FAISS index created and saved to {faiss_index_path}")
    else:
        print(f"🔄 Creating new FAISS index (this may take a few minutes)...")
        vector_db = FAISS.from_documents(documents, embedding_model)
        vector_db.save_local(faiss_index_path)
        print(f"✅ FAISS index created and saved to {faiss_index_path}")

    bm25_path = "bm25_retriever.pkl"
    if os.path.exists(bm25_path):
        print(f"✅ Loading existing BM25 index from {bm25_path}...")
        with open(bm25_path, "rb") as f:
            bm25_retriever = pickle.load(f)
        print(f"✅ BM25 index loaded successfully!")
    else:
        print(f"🔄 Creating new BM25 index...")
        bm25_retriever = BM25Retriever.from_documents(documents)
        bm25_retriever.k = 5
        with open(bm25_path, "wb") as f:
            pickle.dump(bm25_retriever, f)
        print(f"✅ BM25 index created and saved to {bm25_path}")

    vector_retriever = vector_db.as_retriever(search_kwargs={"k": 5})

    ensemble_retriever = EnsembleRetriever(
        retrievers=[bm25_retriever, vector_retriever], weights=[0.5, 0.5]
    )

    print("🔄 Initializing HuggingFace LLM (Mistral-7B-Instruct)...")
    try:
        llm = HuggingFaceEndpoint(
            repo_id="mistralai/Mistral-7B-Instruct-v0.2",
            huggingfacehub_api_token=HF_TOKEN,
            temperature=0.3,
            max_new_tokens=1024,
            top_p=0.95,
            repetition_penalty=1.1,
        )
        print("✅ HuggingFace LLM initialized (Mistral-7B-Instruct-v0.2)")
    except Exception as e:
        print(f"❌ Failed to initialize LLM: {e}")
        print(f"   HF_TOKEN is {'set' if HF_TOKEN else 'NOT set'}")
        _initialized = False
        return

    turkish_legal_prompt = """Sen Türk Noter Hukuku konusunda uzman bir yapay zeka asistanısın. Görevin, Noterlik Kanunu ve Türkiye Noterler Birliği genelgelerinden yararlanarak kullanıcının sorusunu doğru ve eksiksiz yanıtlamaktır.

    BAĞLAM BİLGİLERİ (Kanun ve Genelgelerden):
    {context}

    KULLANICI SORUSU: {question}

    YANITLAMA STRATEJİSİ:
    1. **KAYNAK ÖNCELİĞİ**: 
       - Noterlik Kanunu → Temel yasal çerçeve ve genel kurallar
       - TNB Genelgeleri → Kanunun uygulanmasına ilişkin özel düzenlemeler ve açıklamalar
       - Her iki kaynağı da kontrol et ve ilgili olanları kullan

    2. **HİBRİT YANITLAMA**: 
       - Kanun maddeleri varsa bunları temel al
       - Genelgelerdeki uygulama detayları varsa ekle
       - Kaynak belirtmeyi unutma!

    3. **KAYNAK BELİRTME**:
       - Kanundan alınan bilgi → "Noterlik Kanunu Madde X'e göre..."
       - Genelgelerden alınan bilgi → "Genelge X, Madde Y'ye göre..."
       - Genel bilgi → "Genel olarak..." veya "Türk Hukuku'nda..."

    4. **KALİTE KURALLARI**:
       - Yanıtını net, anlaşılır ve yapılandırılmış şekilde sun
       - Hukuki terminolojiyi doğru kullan
       - Kesin olmadığın konularda varsayımda bulunma
       - Hem kanunu hem genelgeleri kaynak olarak kullanabilirsin

    YANITINIZ:"""

    prompt_template = PromptTemplate(
        template=turkish_legal_prompt, input_variables=["context", "question"]
    )

    _qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        retriever=ensemble_retriever,
        chain_type="stuff",
        chain_type_kwargs={"prompt": prompt_template, "document_separator": "\n---\n"},
        return_source_documents=True,
        verbose=False,
    )

    if _qa_chain is None:
        print("❌ QA Chain creation failed silently")
        return

    print("✅ RAG system initialized successfully!\n")
    _initialized = True


def query_rag(question: str):
    global _qa_chain, _initialized

    if not _initialized:
        init_rag()

    if not _initialized or _qa_chain is None:
        print("❌ RAG system is not properly initialized. Chain or data missing.")
        return None

    try:
        print(f"DEBUG: _qa_chain type: {type(_qa_chain)}")
        print(f"DEBUG: _qa_chain.invoke type: {type(_qa_chain.invoke)}")
        print(f"DEBUG: Calling invoke with question: {question[:50]}...")
        result = _qa_chain.invoke({"query": question})
        return result
    except Exception as e:
        print(f"❌ Error querying RAG: {e}")
        import traceback
        traceback.print_exc()
        return None