|
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 |
|
|
|
import numpy as np |
|
from hazm import * |
|
import re |
|
import nltk |
|
nltk.download('punkt') |
|
st.set_page_config( |
|
page_title="رزم یار", |
|
page_icon="⚔️", |
|
layout="wide" |
|
) |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.stAppHeader.st-emotion-cache-12fmjuu.e4hpqof0 { |
|
background-color: rgba(46,59,46, 0.8) !important; |
|
color: #2e3b2e !important; |
|
font-family: 'Vazirmatn', Tahoma, sans-serif !important; |
|
padding: 20px !important; |
|
border-radius: 10px !important; |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
st.markdown(""" |
|
<style> |
|
@font-face { |
|
font-family: 'Roboto'; |
|
src: url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap') format('woff2'); |
|
font-weight: 400; |
|
font-style: normal; |
|
} |
|
html, body, [class*="css"] { |
|
font-family: 'Roboto', Tahoma, sans-serif !important; |
|
font-weight: 400 !important; |
|
direction: rtl; |
|
text-align: right; |
|
ظ color: #ffffff; |
|
} |
|
.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; |
|
font-weight: 600; |
|
color: #d4d4d4; |
|
cursor: pointer; |
|
transition: background-color 0.3s ease; |
|
} |
|
.menu-item:hover { |
|
background-color: #2e3b2e; |
|
color: #b8860b; |
|
} |
|
.menu-item img { |
|
width: 25px; |
|
height: 25px; |
|
} |
|
.stButton>button { |
|
background-color: #b8860b !important; |
|
color: #1a2b1e !important; |
|
font-family: 'Roboto', Tahoma, sans-serif; |
|
font-weight: 700 !important; |
|
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); |
|
font-family: 'Roboto', Tahoma, sans-serif; /* اضافه شد */ |
|
} |
|
.subtitle { |
|
font-size: 18px; |
|
color: #d4d4d4; |
|
font-weight: 600; |
|
margin-top: 10px; |
|
} |
|
.chat-message { |
|
flex-wrap: wrap; |
|
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; |
|
font-weight: 600; |
|
display: flex; |
|
flex-wrap: wrap; |
|
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: 'Roboto', Tahoma; |
|
font-weight: 500; |
|
font-size: 16px; |
|
color: #d4d4d4 !important; |
|
} |
|
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('<style>.stTextInput > div[data-baseweb="input"] + div, .stTextInput div:has(div[role="alert"]) { display: none !important; }</style>', unsafe_allow_html=True) |
|
st.markdown(""" |
|
<style> |
|
input { |
|
background-color: #2e3b2e; |
|
color: gold; |
|
border: 1px solid gold; |
|
border-radius: 10px; |
|
padding: 10px; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
st.markdown(""" |
|
<style> |
|
html, body, [class*="css"] { |
|
font-family: 'Vazir', sans-serif; |
|
} |
|
label { |
|
font-size: 20px !important; |
|
color: #ffffff !important; |
|
font-weight: 800 !important; |
|
margin-bottom: 10px !important; |
|
display: block; |
|
} |
|
input[type="text"], |
|
input[type="password"], |
|
input[type="text"]:focus, |
|
input[type="password"]:focus, |
|
input[type="text"]:hover, |
|
input[type="password"]:hover { |
|
background-color: #ffffff !important; |
|
color: #000000 !important; |
|
font-size: 18px !important; |
|
font-family: 'Vazir', sans-serif !important; |
|
} |
|
/* Placeholder style */ |
|
::placeholder { |
|
color: #bbbbbb !important; |
|
opacity: 0.8 !important; |
|
font-size: 16px; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
username = st.text_input("نام کاربری:", placeholder="شناسه خود را وارد کنید", |
|
label_visibility="visible") |
|
|
|
password = st.text_input("رمز عبور:", placeholder="رمز عبور ", type="password", |
|
label_visibility="visible") |
|
st.markdown(""" |
|
<style> |
|
div.stButton > button { |
|
background-image: url("https://upload.wikimedia.org/wikipedia/commons/5/59/US_Army_Universal_Camouflage_Pattern.jpg"); |
|
background-size: cover; |
|
background-repeat: no-repeat; |
|
background-position: center; |
|
color: #f5deb3; |
|
font-family: 'Vazir', sans-serif; |
|
font-size: 20px; |
|
font-weight: bold; |
|
padding: 14px 35px; |
|
border: 2px solid #d4af37; |
|
border-radius: 14px; |
|
box-shadow: 0 0 18px rgba(0,0,0,0.6); |
|
transition: all 0.3s ease-in-out; |
|
} |
|
div.stButton > button:hover { |
|
filter: brightness(1.2); |
|
box-shadow: 0 0 22px #b8860b; |
|
transform: scale(1.03); |
|
} |
|
div.stButton > button:active { |
|
transform: scale(0.97); |
|
box-shadow: 0 0 12px #000; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
if st.button("ورود"): |
|
if username == "admin" and password == "123": |
|
st.session_state.authenticated = True |
|
st.rerun() |
|
else: |
|
st.markdown(""" |
|
<div style="background-color: rgba(241, 196, 15, 0.6); color: #2e3b2e; padding: 10px; border-radius: 10px; border: 2px solid #2e3b2e; margin-top: 20px; text-align: center; backdrop-filter: blur(5px);"> |
|
نام کاربری یا رمز عبور اشتباه است. |
|
</div> |
|
""", 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), |
|
] |
|
|
|
st.markdown(""" |
|
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazir-font@v30.1.0/dist/font-face.css" rel="stylesheet" type="text/css" /> |
|
""", unsafe_allow_html=True) |
|
|
|
for idx, (text, icon, link) in enumerate(menu_items): |
|
content = f""" |
|
<div class="menu-item" style="display: flex; align-items: center; margin-bottom: 10px;"> |
|
<img src="{icon}" width="20" height="20" style="margin-left: 10px;" /> |
|
<span style="color: white; font-family: 'Vazir', sans-serif; font-weight: bold;">{text}</span> |
|
</div> |
|
""" |
|
|
|
if link: |
|
content = f'<a href="{link}" target="_blank" style="text-decoration: none;">{content}</a>' |
|
|
|
st.markdown(content, unsafe_allow_html=True) |
|
|
|
if idx in [1, 3, 5]: |
|
st.markdown("<hr style='border-top: 1px solid #555;'/>", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<style> |
|
.header-text { |
|
text-align: center; |
|
margin: 50px 0; |
|
background: #2e3b2e; |
|
padding: 60px 30px; |
|
border-radius: 25px; |
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.8); |
|
animation: slideIn 2s ease-in-out, fadeIn 3s ease-in-out; |
|
background-size: cover; |
|
background-position: center; |
|
position: relative; |
|
} |
|
@keyframes fadeIn { |
|
0% { opacity: 0; transform: translateY(30px); } |
|
100% { opacity: 1; transform: translateY(0); } |
|
} |
|
@keyframes slideIn { |
|
0% { transform: translateX(-50%); opacity: 0; } |
|
100% { transform: translateX(0); opacity: 1; } |
|
} |
|
.header-text h1 { |
|
font-family: 'Vazir', sans-serif; |
|
font-size: 62px; |
|
color: #d89b00; |
|
margin: 0; |
|
font-weight: 900; |
|
letter-spacing: 4px; |
|
text-shadow: 4px 4px 15px rgba(0, 0, 0, 0.9); |
|
transform: scale(1.08); |
|
animation: glow 2s ease-in-out infinite alternate; |
|
} |
|
.subtitle { |
|
font-family: 'Vazir', sans-serif; |
|
font-size: 24px; |
|
color: #f8f8f8; |
|
font-weight: 700; |
|
margin-top: 15px; |
|
letter-spacing: 2px; |
|
text-shadow: 3px 3px 10px rgba(0,0,0,0.8); |
|
animation: fadeInSubtitle 2s ease-in-out; |
|
} |
|
@keyframes fadeInSubtitle { |
|
0% { opacity: 0; transform: translateY(20px); } |
|
100% { opacity: 1; transform: translateY(0); } |
|
} |
|
.stButton>button { |
|
background-color: #e67e22 !important; |
|
color: #4b5320 !important; |
|
font-family: 'Vazir', sans-serif; |
|
font-weight: 700 !important; |
|
border-radius: 20px; |
|
padding: 15px 30px; |
|
border: none; |
|
transition: all 0.3s ease; |
|
font-size: 18px; |
|
width: 100%; |
|
margin: 20px 0; |
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); |
|
} |
|
.stButton>button:hover { |
|
background-color: #f39c12 !important; |
|
transform: translateY(-4px); |
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6); |
|
} |
|
.stApp { |
|
background: #2e3b2e; |
|
color: white; |
|
font-family: 'Vazir', sans-serif; |
|
} |
|
</style> |
|
<div class="header-text"> |
|
<h1>رزمیار ارتش</h1> |
|
<div class="subtitle">دستیارهوشمند ارتش جمهوری اسلامی ایران</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.st-emotion-cache-128upt6.eht7o1d3 { |
|
background-color: rgba(46,59,46, 0.8) !important; |
|
border-radius: 10px !important; |
|
color: #d4d4d4 !important; |
|
font-family: 'Vazirmatn', Tahoma, sans-serif !important; |
|
padding: 15px !important; |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.st-af.st-ah.st-bb.st-ar.st-as.st-ax.st-ay.st-az.st-b0.st-b1.st-b2.st-bc.st-b7 { |
|
background-color: #3a5338 !important; |
|
color: #d4d4d4 !important; |
|
border: 1px solid #c8a200 !important; |
|
border-radius: 10px; |
|
padding: 15px; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<style> |
|
.st-emotion-cache-yd4u6l.e1togvvn1 { |
|
background-color: rgba(106, 127, 83, 0.8) !important; |
|
border-radius: 10px !important; |
|
color: #d4d4d4 !important; |
|
font-family: 'Vazirmatn', Tahoma, sans-serif !important; |
|
padding: 15px !important; |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
st.markdown(""" |
|
<style> |
|
.stAppHeader.st-emotion-cache-12fmjuu.e4hpqof0 { |
|
background-color: rgba(42, 55, 39, 0.9) !important; |
|
color: #d4d4d4 !important; |
|
font-family: 'Vazirmatn', Tahoma, sans-serif !important; |
|
padding: 20px !important; |
|
border-radius: 10px !important; |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<style> |
|
textarea::placeholder { |
|
color: #ffffff !important; |
|
opacity: 1 !important; |
|
} |
|
textarea { |
|
color: #ffffff !important; |
|
border-radius: 10px !important; |
|
padding: 10px !important; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.thinking-message { |
|
display: flex; |
|
align-items: center; |
|
font-size: 18px; |
|
color: #ffffff; |
|
} |
|
.thinking-message p { |
|
margin-right: 10px; |
|
} |
|
.spinner { |
|
border: 4px solid #f3f3f3; |
|
border-top: 4px solid #4b6d3d; |
|
border-radius: 50%; |
|
width: 20px; |
|
height: 20px; |
|
animation: spin 2s linear infinite; |
|
} |
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
import json |
|
import requests |
|
import streamlit as st |
|
import numpy as np |
|
from langchain.schema import SystemMessage, HumanMessage |
|
from langchain.chat_models import ChatOpenAI |
|
from sklearn.metrics.pairwise import cosine_similarity |
|
llm = ChatOpenAI( |
|
base_url="https://api.together.xyz/v1", |
|
api_key='333ac33f5be91819cb7ade101134d73f5e63d299a964ae290850eeac5d82a8d5', |
|
model="meta-llama/Llama-3.3-70B-Instruct-Turbo", |
|
|
|
) |
|
|
|
EMBEDDING_FILE = "embeddings.json" |
|
EMBEDDING_MODEL = "intfloat/multilingual-e5-large-instruct" |
|
TOGETHER_API_KEY = "333ac33f5be91819cb7ade101134d73f5e63d299a964ae290850eeac5d82a8d5" |
|
|
|
@st.cache_data |
|
def load_embeddings(file_path): |
|
with open(file_path, "r", encoding="utf-8") as f: |
|
return json.load(f) |
|
|
|
def get_query_embedding_together(query): |
|
url = "https://api.together.xyz/v1/embeddings" |
|
headers = { |
|
"Authorization": f"Bearer {TOGETHER_API_KEY}", |
|
"accept": "application/json", |
|
"content-type": "application/json" |
|
} |
|
payload = { |
|
"model": EMBEDDING_MODEL, |
|
"input": query |
|
} |
|
response = requests.post(url, headers=headers, json=payload) |
|
response.raise_for_status() |
|
return response.json()["data"][0]["embedding"] |
|
|
|
def find_most_similar_chunks(query_embedding, data, top_n=20): |
|
query_vec = np.array(query_embedding).reshape(1, -1) |
|
similarities = [] |
|
for item in data: |
|
chunk_vec = np.array(item["embedding"]).reshape(1, -1) |
|
sim = cosine_similarity(query_vec, chunk_vec)[0][0] |
|
similarities.append((item["chunk"], sim)) |
|
similarities.sort(key=lambda x: x[1], reverse=True) |
|
return [chunk for chunk, _ in similarities[:top_n]] |
|
|
|
def clean_text(text): |
|
import re |
|
return re.sub(r'[^آ-یa-zA-Z0-9۰-۹,.،؟!؛\s]+', '', text) |
|
|
|
query = st.chat_input("چطور میتونم کمک کنم؟") |
|
|
|
if "chat_history" not in st.session_state: |
|
st.session_state.chat_history = [] |
|
|
|
if query: |
|
thinking = st.empty() |
|
thinking.markdown("⏳ در حال پردازش...") |
|
|
|
try: |
|
query_embedding = get_query_embedding_together(query) |
|
data = load_embeddings(EMBEDDING_FILE) |
|
top_chunks = find_most_similar_chunks(query_embedding, data, top_n=20) |
|
|
|
context = "\n".join(top_chunks) |
|
prompt = f""" |
|
به سؤال زیر فقط بر اساس اطلاعات موجود در خطوط مرتبط پاسخ بده |
|
از تحلیل، مقدمهچینی، توضیح مراحل تفکر، یا حدس شخصی خودداری کن |
|
اگر اطلاعات کافی برای پاسخ دقیق در خطوط مرتبط وجود نداشت، فقط در آن صورت |
|
میتوانی از دانش عمومی خود استفاده کنی تا یک پاسخ حرفهای و دقیق ارائه دهی |
|
پاسخ باید نهایی، روان، و در حدود 256 تا 1024 کاراکتر باشد اگر در دیتا موجود نبود نزدیک ترین پاسخ به متن |
|
|
|
سوال: |
|
{query} |
|
|
|
خطوط مرتبط: |
|
{top_chunks} |
|
|
|
پاسخ نهایی: |
|
""" |
|
|
|
|
|
response = llm([ |
|
SystemMessage( |
|
content=" تو یک دستیار دقیق هستی که فقط با اطلاعات موجود در متن پاسخ میدهی و اگر در متن موجود نبود از شبیه ترین پاسخ به دیتای متن " |
|
), |
|
HumanMessage(content=prompt) |
|
]) |
|
final_answer = clean_text(response.content.strip()) |
|
|
|
except Exception as e: |
|
final_answer = f"❗ خطا: {str(e)}" |
|
|
|
thinking.empty() |
|
|
|
st.session_state.chat_history.append(("🧑", query)) |
|
st.session_state.chat_history.append(("🤖", final_answer)) |
|
|
|
st.markdown(""" |
|
<style> |
|
@import url('https://cdn.fontcdn.ir/Font/Persian/Vazir/Vazir.css'); |
|
div.chat-message { |
|
font-family: 'Vazir', sans-serif; |
|
font-size: 16px; |
|
color: white; |
|
background-color: #0d4d31; |
|
padding: 10px; |
|
border-radius: 10px; |
|
margin-bottom: 5px; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown("---") |
|
for sender, message in st.session_state.chat_history: |
|
st.markdown(f'<div class="chat-message"><strong>{sender}</strong>: {message}</div>', unsafe_allow_html=True) |