File size: 6,258 Bytes
0f8f1e7
 
74cc79a
0f8f1e7
 
 
 
 
86841b5
493753d
86841b5
493753d
 
 
86841b5
184c970
 
493753d
 
 
86841b5
0f8f1e7
 
 
493753d
0f8f1e7
493753d
0f8f1e7
 
 
 
493753d
0f8f1e7
 
493753d
 
285ac79
493753d
 
285ac79
0ca2eec
0f8f1e7
184c970
0f8f1e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e14a8c
 
 
 
 
0f8f1e7
493753d
 
 
 
 
0f8f1e7
493753d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74cc79a
493753d
0f8f1e7
493753d
 
 
 
 
 
f67e311
493753d
 
1e14a8c
 
 
 
493753d
1e14a8c
493753d
05dc960
493753d
 
1e14a8c
 
493753d
 
 
 
 
 
 
 
 
 
 
 
 
485cccf
493753d
 
 
 
 
 
 
 
 
627080f
485cccf
627080f
 
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
169
170
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

from uz_translit import to_latin

# Загружаем переменные окружения
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])
    # dataset = dataset.select(range(5))  # Для тестирования на небольшом количестве данных
    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 = to_latin(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"
        )

    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()