M17idd commited on
Commit
92fc8a0
·
verified ·
1 Parent(s): a9ee11d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -467
app.py CHANGED
@@ -2,259 +2,159 @@ import streamlit as st
2
  from hazm import Normalizer, SentenceTokenizer
3
  import os
4
  import docx
5
- from langchain.chat_models import ChatOpenAI
6
- from langchain.schema import SystemMessage, HumanMessage, AIMessage
7
 
8
  from rapidfuzz import fuzz
9
  import concurrent.futures
10
  import time
11
- # from sentence_transformers import SentenceTransformer
12
  import numpy as np
13
- from hazm import *
14
  import re
15
  import nltk
 
 
 
16
 
17
-
18
-
 
19
  EMBEDDING_MODEL = "text-embedding-3-large"
20
-
21
 
22
  nltk.download('punkt')
 
23
  st.set_page_config(
24
  page_title="رزم یار",
25
  page_icon="⚔️",
26
- layout="wide"
27
  )
28
 
29
-
30
- st.markdown("""
31
- <style>
32
- .stAppHeader.st-emotion-cache-12fmjuu.e4hpqof0 {
33
- background-color: rgba(46,59,46, 0.8) !important;
34
- color: #2e3b2e !important;
35
- font-family: 'Vazirmatn', Tahoma, sans-serif !important;
36
- padding: 20px !important;
37
- border-radius: 10px !important;
38
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
39
- }
40
- </style>
41
- """, unsafe_allow_html=True)
42
  st.markdown("""
43
- <style>
44
- @font-face {
45
- font-family: 'Roboto';
46
- src: url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap') format('woff2');
47
- font-weight: 400;
48
- font-style: normal;
49
- }
50
- html, body, [class*="css"] {
51
- font-family: 'Roboto', Tahoma, sans-serif !important;
52
- font-weight: 400 !important;
53
- direction: rtl;
54
- text-align: right;
55
- ظ color: #ffffff;
56
- }
57
- .stApp {
58
- background: linear-gradient(to left, #4b5e40, #2e3b2e);
59
- color: #ffffff;
60
- }
61
- [data-testid="stSidebar"] {
62
- width: 260px !important;
63
- background-color: #1a2b1e;
64
- border: none !important;
65
- padding-top: 20px;
66
- }
67
- .menu-item {
68
- display: flex;
69
- align-items: center;
70
- gap: 12px;
71
- padding: 12px 20px;
72
- font-size: 16px;
73
- font-weight: 600;
74
- color: #d4d4d4;
75
- cursor: pointer;
76
- transition: background-color 0.3s ease;
77
- }
78
- .menu-item:hover {
79
- background-color: #2e3b2e;
80
- color: #b8860b;
81
- }
82
- .menu-item img {
83
- width: 25px;
84
- height: 25px;
85
- }
86
- .stButton>button {
87
- background-color: #b8860b !important;
88
- color: #1a2b1e !important;
89
- font-family: 'Roboto', Tahoma, sans-serif;
90
- font-weight: 700 !important;
91
- border-radius: 10px;
92
- padding: 12px 24px;
93
- border: none;
94
- transition: all 0.3s ease;
95
- font-size: 16px;
96
- width: 100%;
97
- margin: 10px 0;
98
- }
99
- .stButton>button:hover {
100
- background-color: #8b6508 !important;
101
- transform: translateY(-2px);
102
- box-shadow: 0 4px 8px rgba(0,0,0,0.3);
103
- }
 
 
 
 
 
 
 
 
 
104
  .header-text {
105
  text-align: center;
106
- margin: 20px 0;
107
- background-color: rgba(26, 43, 30, 0.9);
108
- padding: 25px;
109
- border-radius: 15px;
110
- box-shadow: 0 6px 12px rgba(0,0,0,0.4);
111
- font-family: 'Roboto', Tahoma, sans-serif; /* اضافه شد */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  }
113
- .subtitle {
114
- font-size: 18px;
115
- color: #d4d4d4;
116
- font-weight: 600;
117
- margin-top: 10px;
118
- }
119
- .chat-message {
120
- flex-wrap: wrap;
121
- background-color: rgba(26, 43, 30, 0.95);
122
- border: 2px solid #b8860b;
123
- border-radius: 15px;
124
- padding: 20px;
125
- margin: 15px 0;
126
- box-shadow: 0 6px 12px rgba(0,0,0,0.3);
127
- animation: fadeIn 0.6s ease;
128
- font-size: 18px;
129
- color: #d4d4d4;
130
- font-weight: 600;
131
- display: flex;
132
- flex-wrap: wrap;
133
- align-items: center;
134
- gap: 15px;
135
- }
136
- @keyframes fadeIn {
137
- from { opacity: 0; transform: translateY(10px); }
138
- to { opacity: 1; transform: translateY(0); }
139
- }
140
- .stTextInput>div>input, .stTextArea textarea {
141
- background-color: rgba(26, 43, 30, 0.95) !important;
142
- border-radius: 10px !important;
143
- border: 1px solid #b8860b !important;
144
- padding: 12px !important;
145
- font-family: 'Roboto', Tahoma;
146
- font-weight: 500;
147
- font-size: 16px;
148
- color: #d4d4d4 !important;
149
- }
150
- hr {
151
- border: 1px solid #b8860b;
152
- margin: 15px 0;
153
- }
154
- [data-testid="stSidebar"] > div {
155
- border: none !important;
156
- }
157
  </style>
158
  """, unsafe_allow_html=True)
159
 
160
-
 
 
161
  if "authenticated" not in st.session_state:
162
  st.session_state.authenticated = False
163
 
164
  if not st.session_state.authenticated:
165
- st.markdown('<style>.stTextInput > div[data-baseweb="input"] + div, .stTextInput div:has(div[role="alert"]) { display: none !important; }</style>', unsafe_allow_html=True)
166
- st.markdown("""
167
- <style>
168
- input {
169
- background-color: #2e3b2e;
170
- color: gold;
171
- border: 1px solid gold;
172
- border-radius: 10px;
173
- padding: 10px;
174
- }
175
- </style>
176
- """, unsafe_allow_html=True)
177
- st.markdown("""
178
- <style>
179
- html, body, [class*="css"] {
180
- font-family: 'Vazir', sans-serif;
181
- }
182
- label {
183
- font-size: 20px !important;
184
- color: #ffffff !important;
185
- font-weight: 800 !important;
186
- margin-bottom: 10px !important;
187
- display: block;
188
- }
189
- input[type="text"],
190
- input[type="password"],
191
- input[type="text"]:focus,
192
- input[type="password"]:focus,
193
- input[type="text"]:hover,
194
- input[type="password"]:hover {
195
- background-color: #ffffff !important;
196
- color: #000000 !important;
197
- font-size: 18px !important;
198
- font-family: 'Vazir', sans-serif !important;
199
- }
200
- /* Placeholder style */
201
- ::placeholder {
202
- color: #bbbbbb !important;
203
- opacity: 0.8 !important;
204
- font-size: 16px;
205
- }
206
- </style>
207
- """, unsafe_allow_html=True)
208
-
209
- username = st.text_input("نام کاربری:", placeholder="شناسه خود را وارد کنید",
210
- label_visibility="visible")
211
-
212
- password = st.text_input("رمز عبور:", placeholder="رمز عبور ", type="password",
213
- label_visibility="visible")
214
- st.markdown("""
215
- <style>
216
- div.stButton > button {
217
- background-image: url("https://upload.wikimedia.org/wikipedia/commons/5/59/US_Army_Universal_Camouflage_Pattern.jpg");
218
- background-size: cover;
219
- background-repeat: no-repeat;
220
- background-position: center;
221
- color: #f5deb3;
222
- font-family: 'Vazir', sans-serif;
223
- font-size: 20px;
224
- font-weight: bold;
225
- padding: 14px 35px;
226
- border: 2px solid #d4af37;
227
- border-radius: 14px;
228
- box-shadow: 0 0 18px rgba(0,0,0,0.6);
229
- transition: all 0.3s ease-in-out;
230
- }
231
- div.stButton > button:hover {
232
- filter: brightness(1.2);
233
- box-shadow: 0 0 22px #b8860b;
234
- transform: scale(1.03);
235
- }
236
- div.stButton > button:active {
237
- transform: scale(0.97);
238
- box-shadow: 0 0 12px #000;
239
- }
240
- </style>
241
- """, unsafe_allow_html=True)
242
 
243
  if st.button("ورود"):
244
  if username == "admin" and password == "123":
245
  st.session_state.authenticated = True
246
- st.rerun()
247
  else:
248
- st.markdown("""
249
- <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);">
250
- نام کاربری یا رمز عبور اشتباه است.
251
- </div>
252
- """, unsafe_allow_html=True)
253
  st.stop()
254
 
 
 
 
255
  with st.sidebar:
256
  st.image("log.png", use_container_width=True)
257
-
258
  menu_items = [
259
  ("گزارش عملیاتی", "https://cdn-icons-png.flaticon.com/512/3596/3596165.png", "https://m17idd-reporting.hf.space"),
260
  ("تاریخچه ماموریت‌ها", "https://cdn-icons-png.flaticon.com/512/709/709496.png", None),
@@ -264,247 +164,58 @@ with st.sidebar:
264
  ("تنظیمات امنیتی", "https://cdn-icons-png.flaticon.com/512/2099/2099058.png", None),
265
  ("پشتیبانی فنی", "https://cdn-icons-png.flaticon.com/512/597/597177.png", None),
266
  ]
267
-
268
- st.markdown("""
269
- <link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazir-font@v30.1.0/dist/font-face.css" rel="stylesheet" type="text/css" />
270
- """, unsafe_allow_html=True)
271
-
272
- for idx, (text, icon, link) in enumerate(menu_items):
273
  content = f"""
274
- <div class="menu-item" style="display: flex; align-items: center; margin-bottom: 10px;">
275
- <img src="{icon}" width="20" height="20" style="margin-left: 10px;" />
276
- <span style="color: white; font-family: 'Vazir', sans-serif; font-weight: bold;">{text}</span>
277
  </div>
278
  """
279
-
280
  if link:
281
- content = f'<a href="{link}" target="_blank" style="text-decoration: none;">{content}</a>'
282
-
283
  st.markdown(content, unsafe_allow_html=True)
284
 
285
- if idx in [1, 3, 5]:
286
- st.markdown("<hr style='border-top: 1px solid #555;'/>", unsafe_allow_html=True)
287
-
288
  st.markdown("""
289
- <style>
290
- .header-text {
291
- text-align: center;
292
- margin: 50px 0;
293
- background: #2e3b2e;
294
- padding: 60px 30px;
295
- border-radius: 25px;
296
- box-shadow: 0 12px 24px rgba(0, 0, 0, 0.8);
297
- animation: slideIn 2s ease-in-out, fadeIn 3s ease-in-out;
298
- background-size: cover;
299
- background-position: center;
300
- position: relative;
301
- }
302
- @keyframes fadeIn {
303
- 0% { opacity: 0; transform: translateY(30px); }
304
- 100% { opacity: 1; transform: translateY(0); }
305
- }
306
- @keyframes slideIn {
307
- 0% { transform: translateX(-50%); opacity: 0; }
308
- 100% { transform: translateX(0); opacity: 1; }
309
- }
310
- .header-text h1 {
311
- font-family: 'Vazir', sans-serif;
312
- font-size: 62px;
313
- color: #d89b00;
314
- margin: 0;
315
- font-weight: 900;
316
- letter-spacing: 4px;
317
- text-shadow: 4px 4px 15px rgba(0, 0, 0, 0.9);
318
- transform: scale(1.08);
319
- animation: glow 2s ease-in-out infinite alternate;
320
- }
321
- .subtitle {
322
- font-family: 'Vazir', sans-serif;
323
- font-size: 24px;
324
- color: #f8f8f8;
325
- font-weight: 700;
326
- margin-top: 15px;
327
- letter-spacing: 2px;
328
- text-shadow: 3px 3px 10px rgba(0,0,0,0.8);
329
- animation: fadeInSubtitle 2s ease-in-out;
330
- }
331
- @keyframes fadeInSubtitle {
332
- 0% { opacity: 0; transform: translateY(20px); }
333
- 100% { opacity: 1; transform: translateY(0); }
334
- }
335
- .stButton>button {
336
- background-color: #e67e22 !important;
337
- color: #4b5320 !important;
338
- font-family: 'Vazir', sans-serif;
339
- font-weight: 700 !important;
340
- border-radius: 20px;
341
- padding: 15px 30px;
342
- border: none;
343
- transition: all 0.3s ease;
344
- font-size: 18px;
345
- width: 100%;
346
- margin: 20px 0;
347
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
348
- }
349
- .stButton>button:hover {
350
- background-color: #f39c12 !important;
351
- transform: translateY(-4px);
352
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6);
353
- }
354
- .stApp {
355
- background: #2e3b2e;
356
- color: white;
357
- font-family: 'Vazir', sans-serif;
358
- }
359
- </style>
360
- <div class="header-text">
361
- <h1>رزم‌‌یار‌ ارتش</h1>
362
- <div class="subtitle">دستیارهوشمند ارتش جمهوری اسلامی ایران</div>
363
- </div>
364
  """, unsafe_allow_html=True)
365
 
366
-
367
- # from transformers import pipeline
368
- # hf_api_key = os.getenv("tavana55")
369
-
370
-
371
- # model_name = "Qwen/Qwen3-0.6B"
372
-
373
- # llm = pipeline("text-generation", model=model_name)
374
-
375
- st.markdown("""
376
- <style>
377
- .st-emotion-cache-128upt6.eht7o1d3 {
378
- background-color: rgba(46,59,46, 0.8) !important;
379
- border-radius: 10px !important;
380
- color: #d4d4d4 !important;
381
- font-family: 'Vazirmatn', Tahoma, sans-serif !important;
382
- padding: 15px !important;
383
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
384
- }
385
- </style>
386
- """, unsafe_allow_html=True)
387
-
388
-
389
- st.markdown("""
390
- <style>
391
- .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 {
392
- background-color: #3a5338 !important;
393
- color: #d4d4d4 !important;
394
- border: 1px solid #c8a200 !important;
395
- border-radius: 10px;
396
- padding: 15px;
397
- }
398
- </style>
399
- """, unsafe_allow_html=True)
400
-
401
- st.markdown("""
402
- <style>
403
- .st-emotion-cache-yd4u6l.e1togvvn1 {
404
- background-color: rgba(106, 127, 83, 0.8) !important;
405
- border-radius: 10px !important;
406
- color: #d4d4d4 !important;
407
- font-family: 'Vazirmatn', Tahoma, sans-serif !important;
408
- padding: 15px !important;
409
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
410
- }
411
- </style>
412
- """, unsafe_allow_html=True)
413
- st.markdown("""
414
- <style>
415
- .stAppHeader.st-emotion-cache-12fmjuu.e4hpqof0 {
416
- background-color: rgba(42, 55, 39, 0.9) !important;
417
- color: #d4d4d4 !important;
418
- font-family: 'Vazirmatn', Tahoma, sans-serif !important;
419
- padding: 20px !important;
420
- border-radius: 10px !important;
421
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
422
- }
423
- </style>
424
- """, unsafe_allow_html=True)
425
-
426
- st.markdown("""
427
- <style>
428
- textarea::placeholder {
429
- color: #ffffff !important;
430
- opacity: 1 !important;
431
- }
432
- textarea {
433
- color: #ffffff !important;
434
- border-radius: 10px !important;
435
- padding: 10px !important;
436
- }
437
- </style>
438
- """, unsafe_allow_html=True)
439
-
440
-
441
- st.markdown("""
442
- <style>
443
- .thinking-message {
444
- display: flex;
445
- align-items: center;
446
- font-size: 18px;
447
- color: #ffffff;
448
- }
449
- .thinking-message p {
450
- margin-right: 10px;
451
- }
452
- .spinner {
453
- border: 4px solid #f3f3f3;
454
- border-top: 4px solid #4b6d3d;
455
- border-radius: 50%;
456
- width: 20px;
457
- height: 20px;
458
- animation: spin 2s linear infinite;
459
- }
460
- @keyframes spin {
461
- 0% { transform: rotate(0deg); }
462
- 100% { transform: rotate(360deg); }
463
- }
464
- </style>
465
- """, unsafe_allow_html=True)
466
-
467
- import json
468
- import requests
469
- import streamlit as st
470
- import numpy as np
471
- from sklearn.metrics.pairwise import cosine_similarity
472
- llm = ChatOpenAI(
473
  base_url="https://api.apifree.ai/v1",
474
  api_key="sk-p6nSSQGZ3174esjkw7YcFpcXj6XNp",
475
- model="openai/gpt-5-mini",
476
- max_tokens=4096,
477
  temperature=0.7,
 
478
  )
479
 
480
- EMBEDDING_FILE = "embeddings.json"
481
- # EMBEDDING_MODEL = "intfloat/multilingual-e5-large-instruct"
482
- # TOGETHER_API_KEY = "333ac33f5be91819cb7ade101134d73f5e63d299a964ae290850eeac5d82a8d5"
483
-
484
-
485
-
486
-
487
  @st.cache_data
488
  def load_embeddings(file_path):
489
  with open(file_path, "r", encoding="utf-8") as f:
490
  return json.load(f)
491
 
492
- def get_query_embedding_apifree(query: str):
493
  url = "https://api.apifree.ai/v1/embeddings"
494
-
495
  headers = {
496
- "Authorization": "Bearer sk-p6nSSQGZ3174esjkw7YcFpcXj6XNp",
497
  "Content-Type": "application/json"
498
  }
499
-
500
  payload = {
501
- "model": "text-embedding-3-large",
502
  "input": query
503
  }
504
-
505
  response = requests.post(url, headers=headers, json=payload)
506
  response.raise_for_status()
507
-
508
  return response.json()["data"][0]["embedding"]
509
 
510
  def find_most_similar_chunks(query_embedding, data, top_n=20):
@@ -518,9 +229,11 @@ def find_most_similar_chunks(query_embedding, data, top_n=20):
518
  return [chunk for chunk, _ in similarities[:top_n]]
519
 
520
  def clean_text(text):
521
- import re
522
  return re.sub(r'[^آ-یa-zA-Z0-9۰-۹,.،؟!؛\s]+', '', text)
523
 
 
 
 
524
  query = st.chat_input("چطور می‌تونم کمک کنم؟")
525
 
526
  if "chat_history" not in st.session_state:
@@ -531,32 +244,26 @@ if query:
531
  thinking.markdown("⏳ در حال پردازش...")
532
 
533
  try:
534
- query_embedding = get_query_embedding_apifree(query)
535
  data = load_embeddings(EMBEDDING_FILE)
536
  top_chunks = find_most_similar_chunks(query_embedding, data, top_n=20)
537
 
538
  context = "\n".join(top_chunks)
539
  prompt = f"""
540
- به سؤال زیر فقط بر اساس اطلاعات موجود در خطوط مرتبط پاسخ بده
541
- از تحلیل، مقدمه‌چینی، توضیح مراحل تفکر، یا حدس شخصی خودداری کن
542
- اگر اطلاعات کافی برای پاسخ دقیق در خطوط مرتبط وجود نداشت، فقط در آن صورت
543
- می‌توانی از دانش عمومی خود استفاده کنی تا یک پاسخ حرفه‌ای و دقیق ارائه دهی
544
- پاسخ باید نهایی، روان، و در حدود 256 تا 1024 کاراکتر باشد اگر در دیتا موجود نبود نزدیک ترین پاسخ به متن
545
-
546
- سوال:
547
- {query}
548
-
549
- خطوط مرتبط:
550
- {top_chunks}
551
-
552
- پاسخ نهایی:
553
- """
554
 
 
 
 
 
 
555
 
556
  response = llm.invoke([
557
- SystemMessage(
558
- content=" تو یک دستیار دقیق هستی که فقط با اطلاعات موجود در متن پاسخ می‌دهی و اگر در متن موجود نبود از شبیه ترین پاسخ به دیتای متن "
559
- ),
560
  HumanMessage(content=prompt)
561
  ])
562
  final_answer = clean_text(response.content.strip())
@@ -565,25 +272,11 @@ if query:
565
  final_answer = f"❗ خطا: {str(e)}"
566
 
567
  thinking.empty()
568
-
569
  st.session_state.chat_history.append(("🧑", query))
570
  st.session_state.chat_history.append(("🤖", final_answer))
571
 
572
- st.markdown("""
573
- <style>
574
- @import url('https://cdn.fontcdn.ir/Font/Persian/Vazir/Vazir.css');
575
- div.chat-message {
576
- font-family: 'Vazir', sans-serif;
577
- font-size: 16px;
578
- color: white;
579
- background-color: #0d4d31;
580
- padding: 10px;
581
- border-radius: 10px;
582
- margin-bottom: 5px;
583
- }
584
- </style>
585
- """, unsafe_allow_html=True)
586
-
587
- st.markdown("---")
588
  for sender, message in st.session_state.chat_history:
589
- st.markdown(f'<div class="chat-message"><strong>{sender}</strong>: {message}</div>', unsafe_allow_html=True)
 
2
  from hazm import Normalizer, SentenceTokenizer
3
  import os
4
  import docx
5
+ from langchain.chat_models import init_chat_model
6
+ from langchain.messages import SystemMessage, HumanMessage, AIMessage
7
 
8
  from rapidfuzz import fuzz
9
  import concurrent.futures
10
  import time
 
11
  import numpy as np
 
12
  import re
13
  import nltk
14
+ import json
15
+ import requests
16
+ from sklearn.metrics.pairwise import cosine_similarity
17
 
18
+ # ======================
19
+ # Config
20
+ # ======================
21
  EMBEDDING_MODEL = "text-embedding-3-large"
22
+ EMBEDDING_FILE = "embeddings.json"
23
 
24
  nltk.download('punkt')
25
+
26
  st.set_page_config(
27
  page_title="رزم یار",
28
  page_icon="⚔️",
29
+ layout="wide"
30
  )
31
 
32
+ # ======================
33
+ # Styles
34
+ # ======================
 
 
 
 
 
 
 
 
 
 
35
  st.markdown("""
36
+ <style>
37
+ /* Fonts */
38
+ @import url('https://cdn.jsdelivr.net/gh/rastikerdar/vazir-font@v30.1.0/dist/font-face.css');
39
+
40
+ /* Body */
41
+ html, body, [class*="css"] {
42
+ font-family: 'Vazir', Tahoma, sans-serif !important;
43
+ color: #ffffff;
44
+ direction: rtl;
45
+ text-align: right;
46
+ }
47
+
48
+ /* App background */
49
+ .stApp {
50
+ background: linear-gradient(to left, #4b5e40, #2e3b2e);
51
+ color: #ffffff;
52
+ }
53
+
54
+ /* Sidebar */
55
+ [data-testid="stSidebar"] {
56
+ width: 260px !important;
57
+ background-color: #1a2b1e;
58
+ border: none !important;
59
+ padding-top: 20px;
60
+ }
61
+
62
+ /* Buttons */
63
+ .stButton>button {
64
+ background-color: #b8860b !important;
65
+ color: #1a2b1e !important;
66
+ font-family: 'Vazir', Tahoma, sans-serif;
67
+ font-weight: 700 !important;
68
+ border-radius: 10px;
69
+ padding: 12px 24px;
70
+ border: none;
71
+ transition: all 0.3s ease;
72
+ font-size: 16px;
73
+ width: 100%;
74
+ margin: 10px 0;
75
+ }
76
+ .stButton>button:hover {
77
+ background-color: #8b6508 !important;
78
+ transform: translateY(-2px);
79
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
80
+ }
81
+
82
+ /* Chat messages */
83
+ .chat-message {
84
+ flex-wrap: wrap;
85
+ background-color: rgba(26, 43, 30, 0.95);
86
+ border: 2px solid #b8860b;
87
+ border-radius: 15px;
88
+ padding: 20px;
89
+ margin: 15px 0;
90
+ box-shadow: 0 6px 12px rgba(0,0,0,0.3);
91
+ animation: fadeIn 0.6s ease;
92
+ font-size: 18px;
93
+ color: #d4d4d4;
94
+ font-weight: 600;
95
+ display: flex;
96
+ flex-wrap: wrap;
97
+ align-items: center;
98
+ gap: 15px;
99
+ }
100
+ @keyframes fadeIn {
101
+ from { opacity: 0; transform: translateY(10px); }
102
+ to { opacity: 1; transform: translateY(0); }
103
+ }
104
+
105
+ /* Header */
106
  .header-text {
107
  text-align: center;
108
+ margin: 50px 0;
109
+ background: #2e3b2e;
110
+ padding: 60px 30px;
111
+ border-radius: 25px;
112
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.8);
113
+ }
114
+ .header-text h1 {
115
+ font-family: 'Vazir', sans-serif;
116
+ font-size: 62px;
117
+ color: #d89b00;
118
+ margin: 0;
119
+ font-weight: 900;
120
+ letter-spacing: 4px;
121
+ text-shadow: 4px 4px 15px rgba(0, 0, 0, 0.9);
122
+ }
123
+ .subtitle {
124
+ font-family: 'Vazir', sans-serif;
125
+ font-size: 24px;
126
+ color: #f8f8f8;
127
+ font-weight: 700;
128
+ margin-top: 15px;
129
+ letter-spacing: 2px;
130
+ text-shadow: 3px 3px 10px rgba(0,0,0,0.8);
131
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  </style>
133
  """, unsafe_allow_html=True)
134
 
135
+ # ======================
136
+ # Authentication
137
+ # ======================
138
  if "authenticated" not in st.session_state:
139
  st.session_state.authenticated = False
140
 
141
  if not st.session_state.authenticated:
142
+ username = st.text_input("نام کاربری:", placeholder="شناسه خود را وارد کنید")
143
+ password = st.text_input("رمز عبور:", placeholder="رمز عبور", type="password")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  if st.button("ورود"):
146
  if username == "admin" and password == "123":
147
  st.session_state.authenticated = True
148
+ st.experimental_rerun()
149
  else:
150
+ st.warning("نام کاربری یا رمز عبور اشتباه است.")
 
 
 
 
151
  st.stop()
152
 
153
+ # ======================
154
+ # Sidebar Menu
155
+ # ======================
156
  with st.sidebar:
157
  st.image("log.png", use_container_width=True)
 
158
  menu_items = [
159
  ("گزارش عملیاتی", "https://cdn-icons-png.flaticon.com/512/3596/3596165.png", "https://m17idd-reporting.hf.space"),
160
  ("تاریخچه ماموریت‌ها", "https://cdn-icons-png.flaticon.com/512/709/709496.png", None),
 
164
  ("تنظیمات امنیتی", "https://cdn-icons-png.flaticon.com/512/2099/2099058.png", None),
165
  ("پشتیبانی فنی", "https://cdn-icons-png.flaticon.com/512/597/597177.png", None),
166
  ]
167
+ for text, icon, link in menu_items:
 
 
 
 
 
168
  content = f"""
169
+ <div class="menu-item">
170
+ <img src="{icon}" width="20" height="20" />
171
+ <span>{text}</span>
172
  </div>
173
  """
 
174
  if link:
175
+ content = f'<a href="{link}" target="_blank" style="text-decoration:none;">{content}</a>'
 
176
  st.markdown(content, unsafe_allow_html=True)
177
 
178
+ # ======================
179
+ # Header
180
+ # ======================
181
  st.markdown("""
182
+ <div class="header-text">
183
+ <h1>رزم‌‌یار‌ ارتش</h1>
184
+ <div class="subtitle">دستیارهوشمند ارتش جمهوری اسلامی ایران</div>
185
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  """, unsafe_allow_html=True)
187
 
188
+ # ======================
189
+ # LangChain Chat Model
190
+ # ======================
191
+ llm = init_chat_model(
192
+ model="gpt-5-mini",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  base_url="https://api.apifree.ai/v1",
194
  api_key="sk-p6nSSQGZ3174esjkw7YcFpcXj6XNp",
 
 
195
  temperature=0.7,
196
+ max_tokens=4096
197
  )
198
 
199
+ # ======================
200
+ # Embeddings
201
+ # ======================
 
 
 
 
202
  @st.cache_data
203
  def load_embeddings(file_path):
204
  with open(file_path, "r", encoding="utf-8") as f:
205
  return json.load(f)
206
 
207
+ def get_query_embedding(query: str):
208
  url = "https://api.apifree.ai/v1/embeddings"
 
209
  headers = {
210
+ "Authorization": f"Bearer sk-p6nSSQGZ3174esjkw7YcFpcXj6XNp",
211
  "Content-Type": "application/json"
212
  }
 
213
  payload = {
214
+ "model": EMBEDDING_MODEL,
215
  "input": query
216
  }
 
217
  response = requests.post(url, headers=headers, json=payload)
218
  response.raise_for_status()
 
219
  return response.json()["data"][0]["embedding"]
220
 
221
  def find_most_similar_chunks(query_embedding, data, top_n=20):
 
229
  return [chunk for chunk, _ in similarities[:top_n]]
230
 
231
  def clean_text(text):
 
232
  return re.sub(r'[^آ-یa-zA-Z0-9۰-۹,.،؟!؛\s]+', '', text)
233
 
234
+ # ======================
235
+ # Chat Interface
236
+ # ======================
237
  query = st.chat_input("چطور می‌تونم کمک کنم؟")
238
 
239
  if "chat_history" not in st.session_state:
 
244
  thinking.markdown("⏳ در حال پردازش...")
245
 
246
  try:
247
+ query_embedding = get_query_embedding(query)
248
  data = load_embeddings(EMBEDDING_FILE)
249
  top_chunks = find_most_similar_chunks(query_embedding, data, top_n=20)
250
 
251
  context = "\n".join(top_chunks)
252
  prompt = f"""
253
+ به سؤال زیر فقط بر اساس اطلاعات موجود در خطوط مرتبط پاسخ بده.
254
+ اگر اطلاعات کافی نبود، نزدیک‌ترین پاسخ را بده.
255
+
256
+ سؤال:
257
+ {query}
 
 
 
 
 
 
 
 
 
258
 
259
+ خطوط مرتبط:
260
+ {top_chunks}
261
+
262
+ پاسخ نهایی:
263
+ """
264
 
265
  response = llm.invoke([
266
+ SystemMessage(content="تو یک دستیار دقیق هستی و فقط با داده‌های موجود پاسخ بده."),
 
 
267
  HumanMessage(content=prompt)
268
  ])
269
  final_answer = clean_text(response.content.strip())
 
272
  final_answer = f"❗ خطا: {str(e)}"
273
 
274
  thinking.empty()
 
275
  st.session_state.chat_history.append(("🧑", query))
276
  st.session_state.chat_history.append(("🤖", final_answer))
277
 
278
+ # ======================
279
+ # Display chat
280
+ # ======================
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  for sender, message in st.session_state.chat_history:
282
+ st.markdown(f'<div class="chat-message"><strong>{sender}</strong>: {message}</div>', unsafe_allow_html=True)