Spaces:
Sleeping
Sleeping
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 | |
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() | |