import streamlit as st from hazm import Normalizer, SentenceTokenizer import os import docx from langchain.chat_models import ChatOpenAI from langchain.schema import SystemMessage, HumanMessage from rapidfuzz import fuzz import concurrent.futures import time # from sentence_transformers import SentenceTransformer import numpy as np from hazm import * import re import nltk nltk.download('punkt') st.markdown(""" """, unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) # ---------- احراز هویت ---------- if "authenticated" not in st.session_state: st.session_state.authenticated = False if not st.session_state.authenticated: st.markdown('', unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) username = st.text_input("نام کاربری:", placeholder="شناسه خود را وارد کنید", label_visibility="visible") password = st.text_input("رمز عبور:", placeholder="رمز عبور ", type="password", label_visibility="visible") st.markdown(""" """, unsafe_allow_html=True) if st.button("ورود"): if username == "admin" and password == "123": st.session_state.authenticated = True st.rerun() else: st.markdown("""
نام کاربری یا رمز عبور اشتباه است.
""", unsafe_allow_html=True) st.stop() # ---------- سایدبار ---------- with st.sidebar: st.image("log.png", use_container_width=True) menu_items = [ ("گزارش عملیاتی", "https://cdn-icons-png.flaticon.com/512/3596/3596165.png", "https://m17idd-reporting.hf.space"), ("تاریخچه ماموریت‌ها", "https://cdn-icons-png.flaticon.com/512/709/709496.png", None), ("تحلیل داده‌های نظامی", "https://cdn-icons-png.flaticon.com/512/1828/1828932.png", "https://m17idd-test.hf.space"), ("مدیریت منابع", "https://cdn-icons-png.flaticon.com/512/681/681494.png", None), ("دستیار فرماندهی", "https://cdn-icons-png.flaticon.com/512/3601/3601646.png", None), ("تنظیمات امنیتی", "https://cdn-icons-png.flaticon.com/512/2099/2099058.png", None), ("پشتیبانی فنی", "https://cdn-icons-png.flaticon.com/512/597/597177.png", None), ] # لود فونت Vazir از CDN st.markdown(""" """, unsafe_allow_html=True) for idx, (text, icon, link) in enumerate(menu_items): content = f""" """ if link: content = f'{content}' st.markdown(content, unsafe_allow_html=True) if idx in [1, 3, 5]: st.markdown("
", unsafe_allow_html=True) st.markdown("""

رزم‌‌یار‌ارتش

دستیارهوشمندارتش جمهوری اسلامی ایران
""", unsafe_allow_html=True) # ---------- مدل زبانی ---------- llm = ChatOpenAI( base_url="https://api.together.xyz/v1", api_key='0291f33aee03412a47fa5d8e562e515182dcc5d9aac5a7fb5eefdd1759005979', model="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free", max_tokens=1024 ) # ---------- پردازش فایل‌ها با کش و موازی ---------- folder_path = '46' normalizer = Normalizer() sentence_tokenizer = SentenceTokenizer() @st.cache_data(show_spinner="در حال پردازش اسناد... لطفاً صبور باشید.") def load_and_process_documents(path): def process_docx(filename): try: full_path = os.path.join(path, filename) doc = docx.Document(full_path) text = "\n".join([para.text for para in doc.paragraphs]) # استخراج متن normalized = normalizer.normalize(text) sentences = normalized return sentences except Exception as e: print(f"Error processing {filename}: {e}") return [] with concurrent.futures.ThreadPoolExecutor() as executor: results = executor.map(process_docx, [f for f in os.listdir(path) if f.endswith(".docx")]) return list(results) all_sentences = load_and_process_documents(folder_path) def clean_text(text): cleaned_text = re.sub(r'[^آ-ی۰-۹0-9،.؟!؛+\-* ]+', '', text) return cleaned_text # all_sentences = load_and_process_documents(folder_path) # st.markdown(all_sentences[2]) # ---------- ورودی جستجو ---------- st.markdown(""" """, unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) query = st.chat_input("چطور می‌تونم کمک کنم؟") if query: st.markdown(f'
{query}
', unsafe_allow_html=True) think = st.markdown("""

در حال فکر کردن...

""", unsafe_allow_html=True) else: st.markdown("") # استایل‌ها برای چرخش و پیام در حال فکر کردن st.markdown(""" """, unsafe_allow_html=True) import os import json from sentence_transformers import SentenceTransformer, util # بارگذاری مدل model = SentenceTransformer("nomic-ai/nomic-embed-text-v2-moe", trust_remote_code=True) # مسیر فایل کش برای ذخیره امبدینگ‌های کتاب embeddings_cache_path = "book_embeddings_cache.json" def load_embeddings_from_cache(): """بارگذاری امبدینگ‌ها از کش (فایل JSON)""" if os.path.exists(embeddings_cache_path): with open(embeddings_cache_path, "r") as file: return json.load(file) return {} def save_embeddings_to_cache(embeddings): """ذخیره امبدینگ‌ها به کش (فایل JSON)""" with open(embeddings_cache_path, "w") as file: json.dump(embeddings, file) # بارگذاری امبدینگ‌های قبلی از کش (اگر موجود باشد) book_embeddings = load_embeddings_from_cache() if query: found = False threshold = 0.66 # بررسی اینکه آیا امبدینگ‌های سوال قبلاً محاسبه شده یا خیر query_embedding = model.encode(query, convert_to_tensor=True) # اگر امبدینگ‌های کتاب در کش نباشند، آن‌ها را محاسبه و ذخیره می‌کنیم if not book_embeddings: print("Emeddings for the book are not cached. Embedding the book now...") # فرض کنید 'all_sentences' لیستی از جملات کتاب است all_sentences_embeddings = model.encode(all_sentences, convert_to_tensor=True) # ذخیره امبدینگ‌های کتاب در کش book_embeddings = {idx: embedding.tolist() for idx, embedding in enumerate(all_sentences_embeddings)} save_embeddings_to_cache(book_embeddings) # محاسبه مشابهت‌ها با استفاده از امبدینگ‌های کتاب sentence_embeddings = [embedding for embedding in book_embeddings.values()] similarities = util.pytorch_cos_sim(query_embedding, sentence_embeddings)[0] for idx, similarity in enumerate(similarities): if similarity >= threshold: sentence = all_sentences[idx] prompt = f""" تعدادی پاسخ برای سوال زیر تولید شده است. لطفاً ابتدا این پاسخ‌ها را بررسی کن، سپس با در نظر گرفتن محتوای سوال و لحن آن، یک پاسخ نهایی حرفه‌ای، دقیق و روان ارائه کن که هم به سوال پاسخ دهد و هم از نظر نگارشی و ساختاری در سطح بالایی باشد. پاسخ نهایی باید حداکثر 1024 کاراکتر و حداقل 512 باشد، خلاصه و واضح نوشته شود و فقط به زبان فارسی باشد. از تکرار اضافی پرهیز کن و محتوای چند پاسخ را در صورت نیاز با هم ترکیب کن. سوال: {query} پاسخ‌ها: {sentence} پاسخ نهایی حرفه‌ای بازنویسی‌شده: """ response = llm([SystemMessage(content="You are a helpful assistant."), HumanMessage(content=prompt)]) rewritten = response.content.strip() # بررسی مرتبط بودن پاسخ با سؤال review_prompt = f""" لطفاً بررسی کن که آیا پاسخ زیر، حدودا به سوال زیر پاسخ داده است یا خیر یعنی کلمات کلیدی اصلی در سوال در پاسخ هست یا ن . اگر پاسخ به سوال مرتبط، درست و قابل قبول است، فقط بنویس: 'تأیید شد'. اگر پاسخ اشتباه است یا هیچ ربطی به سوال ندارد، فقط بنویس: 'نیاز به اصلاح دارد'. سوال: {query} پاسخ: {rewritten} """ review_response = llm([SystemMessage(content="You are a helpful assistant."), HumanMessage(content=review_prompt)]) review_result = review_response.content.strip() if "تأیید شد" in review_result: rewritten = clean_text(rewritten) st.markdown(f'
{rewritten}
', unsafe_allow_html=True) else: final_prompt = f""" لطفاً برای سوال زیر پاسخی حرفه‌ای، دقیق و روان تولید کن که مرتبط و معتبر باشد. و از متن ساختگی پرهیز کن و از دیتای متن مرتبط نیز استفاده کن از زبانی جز فارسی استفاده نکن : سوال: {query} متن مرتبط: {sentence} """ new_response = llm([SystemMessage(content="You are a helpful assistant."), HumanMessage(content=final_prompt)]) final_answer = new_response.content.strip() final_answer = clean_text(final_answer) st.markdown(f'
{final_answer}
', unsafe_allow_html=True) think.empty() found = True break if not found: prompt = f"لطفاً بر اساس سوال زیر یک متن مرتبط و معنادار تولید کن و جملات ساختگی استفاده نکن و از جملات موجود در اسناد و جملات معتبر استفاده کن و از زبانی جز فارسی استفاده نکن:\n\nسوال: {query}" response = llm([SystemMessage(content="You are a helpful assistant."), HumanMessage(content=prompt)]) rewritten = response.content.strip() rewritten = clean_text(rewritten) st.markdown(f'
{rewritten}
', unsafe_allow_html=True) think.empty()