Download app.py from M17idd/army: direct link, hf CLI and curl.
- Browser
- Download file 16.4 kB
-
https://huggingface.co/spaces/M17idd/army/resolve/main/app.py
- Command line
-
hf download hf://spaces/M17idd/army/app.py
-
curl -L -o app.py https://huggingface.co/spaces/M17idd/army/resolve/main/app.py
16.4 kB
| import streamlit as st | |
| from hazm import Normalizer, SentenceTokenizer | |
| import os | |
| import docx | |
| from langchain.chat_models import init_chat_model | |
| from langchain.messages import SystemMessage, HumanMessage, AIMessage | |
| from sentence_transformers import SentenceTransformer | |
| 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 | |
| import json | |
| import requests | |
| from sklearn.metrics.pairwise import cosine_similarity | |
| model11 = SentenceTransformer("all-MiniLM-L6-v2") | |
| EMBEDDING_MODEL = "text-embedding-3-large" | |
| 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 { | |
| 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) | |
| # Header | |
| 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) | |
| # Initialize LangChain LLM with Apifree | |
| llm = init_chat_model( | |
| "gpt-5-mini", | |
| base_url="https://api.apifree.ai/gpt-5-mini", | |
| api_key="sk-p6nSSQGZ3174esjkw7YcFpcXj6XNp", | |
| max_tokens=4096, | |
| temperature=0.7, | |
| ) | |
| EMBEDDING_FILE = "embeddings.json" | |
| def load_embeddings(file_path): | |
| with open(file_path, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| def get_query_embedding_apifree(query: str): | |
| url = "https://api.apifree.ai/" | |
| headers = { | |
| "Authorization": "Bearer sk-p6nSSQGZ3174esjkw7YcFpcXj6XNp", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": "gpt-5-mini", | |
| "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_apifree(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.invoke([ | |
| 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) | |