|
import time |
|
import streamlit as st |
|
from langchain.document_loaders import PyPDFLoader |
|
from langchain.text_splitter import RecursiveCharacterTextSplitter |
|
from langchain.embeddings.base import Embeddings |
|
from langchain.vectorstores import FAISS |
|
from langchain.indexes import VectorstoreIndexCreator |
|
from langchain.chains import RetrievalQA |
|
from langchain.chat_models import ChatOpenAI |
|
from typing import List |
|
from together import Together |
|
import pandas as pd |
|
import streamlit as st |
|
from langchain.docstore.document import Document |
|
import docx |
|
import os |
|
|
|
from hazm import * |
|
|
|
|
|
|
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap'); |
|
|
|
html, body, [class*="css"] { |
|
font-family: 'Vazirmatn', Tahoma, sans-serif; |
|
direction: rtl; |
|
text-align: right; |
|
} |
|
|
|
.stApp { |
|
background: linear-gradient(to left, #4b5e40, #2e3b2e); |
|
color: #ffffff; |
|
} |
|
|
|
/* استایل سایدبار */ |
|
[data-testid="stSidebar"] { |
|
width: 260px !important; |
|
background-color: #1a2b1e; |
|
border: none !important; /* حذف حاشیه زرد */ |
|
padding-top: 20px; |
|
} |
|
|
|
.menu-item { |
|
display: flex; |
|
align-items: center; |
|
gap: 12px; |
|
padding: 12px 20px; |
|
font-size: 16px; |
|
color: #d4d4d4; |
|
cursor: pointer; |
|
transition: background-color 0.3s ease; |
|
} |
|
|
|
.menu-item:hover { |
|
background-color: #2e3b2e; |
|
color: #b8860b; |
|
} |
|
|
|
.menu-item img { |
|
width: 24px; |
|
height: 24px; |
|
} |
|
|
|
/* استایل دکمهها */ |
|
.stButton>button { |
|
background-color: #b8860b !important; |
|
color: #1a2b1e !important; |
|
font-family: 'Vazirmatn', Tahoma; |
|
font-weight: 700; |
|
border-radius: 10px; |
|
padding: 12px 24px; |
|
border: none; |
|
transition: all 0.3s ease; |
|
font-size: 16px; |
|
width: 100%; |
|
margin: 10px 0; |
|
} |
|
|
|
.stButton>button:hover { |
|
background-color: #8b6508 !important; |
|
transform: translateY(-2px); |
|
box-shadow: 0 4px 8px rgba(0,0,0,0.3); |
|
} |
|
|
|
/* استایل هدر */ |
|
.header-text { |
|
text-align: center; |
|
margin: 20px 0; |
|
background-color: rgba(26, 43, 30, 0.9); |
|
padding: 25px; |
|
border-radius: 15px; |
|
box-shadow: 0 6px 12px rgba(0,0,0,0.4); |
|
} |
|
|
|
.header-text h1 { |
|
font-size: 42px; |
|
color: #b8860b; |
|
margin: 0; |
|
font-weight: 700; |
|
} |
|
|
|
.subtitle { |
|
font-size: 18px; |
|
color: #d4d4d4; |
|
margin-top: 10px; |
|
} |
|
|
|
/* استایل پیام چت */ |
|
.chat-message { |
|
background-color: rgba(26, 43, 30, 0.95); |
|
border: 2px solid #b8860b; |
|
border-radius: 15px; |
|
padding: 20px; |
|
margin: 15px 0; |
|
box-shadow: 0 6px 12px rgba(0,0,0,0.3); |
|
animation: fadeIn 0.6s ease; |
|
font-size: 18px; |
|
color: #d4d4d4; |
|
display: flex; |
|
align-items: center; |
|
gap: 15px; |
|
} |
|
|
|
@keyframes fadeIn { |
|
from { opacity: 0; transform: translateY(10px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
/* استایل ورودیها */ |
|
.stTextInput>div>input, .stTextArea textarea { |
|
background-color: rgba(26, 43, 30, 0.95) !important; |
|
border-radius: 10px !important; |
|
border: 1px solid #b8860b !important; |
|
padding: 12px !important; |
|
font-family: 'Vazirmatn', Tahoma; |
|
font-size: 16px; |
|
color: #d4d4d4 !important; |
|
} |
|
|
|
img.small-logo { |
|
width: 120px; |
|
margin: 0 auto 20px; |
|
display: block; |
|
} |
|
|
|
hr { |
|
border: 1px solid #b8860b; |
|
margin: 15px 0; |
|
} |
|
|
|
/* رفع مشکل نوار زرد */ |
|
[data-testid="stSidebar"] > div { |
|
border: none !important; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if "authenticated" not in st.session_state: |
|
st.session_state.authenticated = False |
|
|
|
if not st.session_state.authenticated: |
|
st.markdown("<h3 style='text-align: center; color: #b8860b;'>ورود به رزمیار ارتش</h3>", unsafe_allow_html=True) |
|
username = st.text_input("نام کاربری:", placeholder="شناسه نظامی خود را وارد کنید") |
|
password = st.text_input("رمز عبور:", type="password", placeholder="رمز عبور نظامی") |
|
if st.button("ورود"): |
|
if username == "admin" and password == "123": |
|
st.session_state.authenticated = True |
|
st.rerun() |
|
else: |
|
st.error("نام کاربری یا رمز عبور اشتباه است.") |
|
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://cdn-icons-png.flaticon.com/512/709/709496.png"), |
|
("تحلیل دادههای نظامی", "https://cdn-icons-png.flaticon.com/512/1828/1828932.png"), |
|
("مدیریت منابع", "https://cdn-icons-png.flaticon.com/512/681/681494.png"), |
|
("دستیار فرماندهی", "https://cdn-icons-png.flaticon.com/512/3601/3601646.png"), |
|
("تنظیمات امنیتی", "https://cdn-icons-png.flaticon.com/512/2099/2099058.png"), |
|
("پشتیبانی فنی", "https://cdn-icons-png.flaticon.com/512/597/597177.png"), |
|
] |
|
|
|
for idx, (text, icon) in enumerate(menu_items): |
|
st.markdown(f""" |
|
<div class="menu-item"> |
|
<img src="{icon}" /> |
|
{text} |
|
</div> |
|
""", unsafe_allow_html=True) |
|
if idx in [1, 3, 5]: |
|
st.markdown("<hr/>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(""" |
|
<div class="header-text"> |
|
<h1>رزمیار ارتش</h1> |
|
<div class="subtitle">دستیار هوشمندارتش</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(f""" |
|
<div class="chat-message"> |
|
<span style="font-size: 24px;">🪖</span> |
|
<span>به رزم یار ارتش خوش آمدید. </span> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
normalizer = Normalizer() |
|
|
|
|
|
tokenizer = word_tokenize |
|
|
|
|
|
word_embedding = WordEmbedding(model_type='fasttext') |
|
WordEmbedding = wordEmbedding.load_model('word2vec.bin') |
|
|
|
class CustomEmbeddings(Embeddings): |
|
def __init__(self, word_embedding: WordEmbedding): |
|
self.word_embedding = word_embedding |
|
|
|
def embed_documents(self, texts: List[str]) -> List[List[float]]: |
|
embeddings = [] |
|
for text in texts: |
|
|
|
embeddings.append([self.word_embedding.embed(word) for word in tokenizer(text)]) |
|
return embeddings |
|
|
|
def embed_query(self, text: str) -> List[float]: |
|
return self.embed_documents([text])[0] |
|
|
|
@st.cache_resource |
|
def get_docx_index(folder_path): |
|
with st.spinner('📄 در حال پردازش فایلهای Word...'): |
|
texts = [] |
|
|
|
|
|
for filename in os.listdir(folder_path): |
|
if filename.endswith(".docx"): |
|
full_path = os.path.join(folder_path, filename) |
|
doc = docx.Document(full_path) |
|
|
|
|
|
file_text = "\n".join([para.text for para in doc.paragraphs]) |
|
if file_text.strip(): |
|
texts.append(file_text) |
|
|
|
|
|
normalized_texts = [normalizer.normalize(text) for text in texts] |
|
tokenized_texts = [tokenizer(text) for text in normalized_texts] |
|
|
|
|
|
text_splitter = RecursiveCharacterTextSplitter( |
|
chunk_size=300, |
|
chunk_overlap=50, |
|
length_function=len, |
|
separators=["\n\n", "\n", " ", ""] |
|
) |
|
split_texts = [] |
|
for text in normalized_texts: |
|
split_texts.extend(text_splitter.split_text(text)) |
|
|
|
|
|
embeddings = CustomEmbeddings(word_embedding=word_embedding) |
|
|
|
|
|
index_creator = VectorstoreIndexCreator( |
|
embedding=embeddings, |
|
text_splitter=text_splitter |
|
) |
|
|
|
documents = [Document(page_content=text) for text in split_texts] |
|
|
|
return index_creator.from_documents(documents) |
|
|
|
|
|
folder_path = '46/' |
|
|
|
try: |
|
docx_index = get_docx_index(folder_path) |
|
except Exception as e: |
|
st.error(f"❌ خطا در ساخت ایندکس: {e}") |
|
|
|
|
|
llm = ChatOpenAI( |
|
base_url="https://api.together.xyz/v1", |
|
api_key='0291f33aee03412a47fa5d8e562e515182dcc5d9aac5a7fb5eefdd1759005979', |
|
model="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free" |
|
) |
|
|
|
chain = RetrievalQA.from_chain_type( |
|
llm=llm, |
|
chain_type='stuff', |
|
retriever=docx_index.vectorstore.as_retriever(), |
|
input_key='question' |
|
) |
|
|
|
if 'messages' not in st.session_state: |
|
st.session_state.messages = [] |
|
|
|
if 'pending_prompt' not in st.session_state: |
|
st.session_state.pending_prompt = None |
|
|
|
for msg in st.session_state.messages: |
|
with st.chat_message(msg['role']): |
|
st.markdown(f"🗨️ {msg['content']}", unsafe_allow_html=True) |
|
|
|
prompt = st.chat_input("چطور میتونم کمک کنم؟") |
|
|
|
if prompt: |
|
st.session_state.messages.append({'role': 'user', 'content': prompt}) |
|
st.session_state.pending_prompt = prompt |
|
st.rerun() |
|
|
|
if st.session_state.pending_prompt: |
|
with st.chat_message('ai'): |
|
thinking = st.empty() |
|
thinking.markdown("🤖 در حال فکر کردن...") |
|
|
|
response = chain.run(f'پاسخ را فقط به زبان فارسی جواب بده به هیچ عنوان از زبان چینی در پاسخ استفاده نکن. سوال: {st.session_state.pending_prompt}') |
|
answer = response.split("Helpful Answer:")[-1].strip() if "Helpful Answer:" in response else response.strip() |
|
if not answer: |
|
answer = "متأسفم، اطلاعات دقیقی در این مورد ندارم." |
|
|
|
thinking.empty() |
|
full_response = "" |
|
placeholder = st.empty() |
|
for word in answer.split(): |
|
full_response += word + " " |
|
placeholder.markdown(full_response + "▌") |
|
time.sleep(0.03) |
|
|
|
placeholder.markdown(full_response) |
|
st.session_state.messages.append({'role': 'ai', 'content': full_response}) |
|
st.session_state.pending_prompt = None |