File size: 6,031 Bytes
0f8f1e7
 
74cc79a
0f8f1e7
 
 
 
 
86841b5
493753d
86841b5
493753d
 
 
86841b5
493753d
 
 
86841b5
0f8f1e7
 
 
493753d
0f8f1e7
493753d
0f8f1e7
 
 
 
493753d
0f8f1e7
 
493753d
 
 
 
 
 
0f8f1e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e14a8c
 
 
 
 
0f8f1e7
493753d
 
 
 
 
0f8f1e7
493753d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74cc79a
493753d
0f8f1e7
493753d
 
 
 
 
 
 
 
 
1e14a8c
 
 
 
493753d
1e14a8c
493753d
54629f4
493753d
 
1e14a8c
 
493753d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0f8f1e7
493753d
0f8f1e7
493753d
 
 
 
 
 
 
 
 
 
0f8f1e7
 
493753d
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
import os
import gradio as gr
import spaces
from datasets import load_dataset, concatenate_datasets
from langchain.docstore.document import Document
from langchain.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from tqdm import tqdm
from dotenv import load_dotenv
import pickle

# Импорты для перевода
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import ChatPromptTemplate

# Загружаем переменные окружения
load_dotenv()
hf_key = os.getenv("HF_KEY")

# Путь для сохранения FAISS-индекса
INDEX_PATH = "./faiss_index"

# Инициализируем эмбеддинг-модель
embeddings = HuggingFaceEmbeddings(model_name="fitlemon/bge-m3-uz-legal-matryoshka")
translations = pickle.load(open("translations.pkl", "rb"))


def update_faiss_index():
    """
    Загружает датасеты, преобразует данные в документы с метаданными,
    создаёт FAISS-индекс и сохраняет его локально.
    """
    train_dataset = load_dataset("fitlemon/rag-labor-codex-dataset", token=hf_key)[
        "train"
    ]
    test_dataset = load_dataset("fitlemon/rag-labor-codex-dataset", token=hf_key)[
        "test"
    ]
    dataset = concatenate_datasets([train_dataset, test_dataset])

    docs = []
    unique_chunks = set()
    for row in tqdm(dataset, desc="Загрузка документов..."):
        chunk = row["chunk"]
        if chunk in unique_chunks:
            continue
        unique_chunks.add(chunk)
        doc = Document(
            page_content=chunk,
            metadata={
                "section": row["section"],
                "section_name": row["section_name"],
                "chapter_name": row["chapter"],
            },
        )
        docs.append(doc)

    print(f"Документы успешно загружены и преобразованы. Длина документов: {len(docs)}")
    db = FAISS.from_documents(docs, embeddings)
    os.makedirs(INDEX_PATH, exist_ok=True)
    db.save_local(INDEX_PATH)
    print("FAISS индекс обновлён и сохранён в:", INDEX_PATH)
    return db


if not os.path.exists(INDEX_PATH):
    db = update_faiss_index()
else:
    db = FAISS.load_local(INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
    print("Загружен существующий FAISS индекс из:", INDEX_PATH)


def translate_ru_uz(message: str) -> str:
    """
    Переводит текст с русского на узбекский с использованием ChatOpenAI.
    Пример: input: "отпуск" → output: "tatil".
    """
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "You are a helpful assistant that translates {input_language} to {output_language}. The subject of Text is Human Resources. Example input: отпуск. Output: tatil.",
            ),
            ("human", "{input}"),
        ]
    )
    llm = ChatOpenAI(
        api_key=os.environ["OPENAI_API_KEY"], model="gpt-4o-mini-2024-07-18"
    )
    chain = prompt | llm
    response = chain.invoke(
        {
            "input_language": "Russian",
            "output_language": "Uzbek",
            "input": message,
        }
    )
    return response.content

@spaces.GPU
def retrieve_articles(query, language):
    """
    Если выбран язык "Russian", переводит запрос с русского на узбекский.
    Затем ищет в FAISS-индексе топ-3 наиболее релевантных документа и возвращает результат в Markdown.
    """
    if language == "Russian":
        translated_query = translate_ru_uz(query)
    else:
        translated_query = query

    results = db.similarity_search(translated_query, k=3)

    result_text = ""
    for doc in results:
        result_text += (
            f"### {doc.metadata['section']}: {doc.metadata['section_name']}\n"
        )
        if language == "Russian":
            result_text += f"**Текст статьи на русском:** {translations.get(doc.page_content, "Не найден")}\n\n"
        result_text += f"**Bo'lim:** {doc.metadata['chapter_name']}\n\n"
        result_text += f"**Modda teksti:**\n{doc.page_content}\n\n"
        result_text += "---\n\n"
    return result_text
    # return "Привет, мир!" if language == "Russian" else "Salom Dunyo!"


def toggle_language(current_language: str) -> gr.update:
    """
    Переключает язык между "Russian" и "Uzbek".
    """
    new_language = "Uzbek" if current_language == "Russian" else "Russian"
    return gr.update(value=new_language)


# Создаём Gradio-интерфейс на основе Blocks
with gr.Blocks() as demo:
    gr.Markdown("# Поиск по Кодексу через Эмбеддинг Модель")
    gr.Markdown(
        "Введите ваш вопрос и выберите язык запроса. Если выбран русский, запрос будет переведен на узбекский перед поиском."
    )

    with gr.Row():
        language_radio = gr.Radio(
            choices=["Russian", "Uzbek"], label="Язык запроса", value="Russian"
        )

    with gr.Row():
        query_input = gr.Textbox(
            lines=3, placeholder="Введите ваш вопрос о кодексе...", label="Запрос"
        )
        search_button = gr.Button("Поиск")

    output_markdown = gr.Markdown()

    search_button.click(
        fn=retrieve_articles,
        inputs=[query_input, language_radio],
        outputs=output_markdown,
    )
    query_input.submit(
        fn=retrieve_articles,
        inputs=[query_input, language_radio],
        outputs=output_markdown,
    )

if __name__ == "__main__":
    demo.launch()