mousvai commited on
Commit
bb288af
·
verified ·
1 Parent(s): 3bb1eb5

Update htmlTemplates.py

Browse files
Files changed (1) hide show
  1. htmlTemplates.py +200 -103
htmlTemplates.py CHANGED
@@ -1,103 +1,200 @@
1
- import base64
2
- import os
3
-
4
- def get_base64_image(path: str) -> str:
5
- """خواندن فایل عکس و تبدیل به base64"""
6
- if not os.path.exists(path):
7
- raise FileNotFoundError(f"Avatar not found: {path}")
8
- with open(path, "rb") as f:
9
- data = f.read()
10
- return base64.b64encode(data).decode("utf-8")
11
-
12
- # Define the mapping of source names to avatar file paths
13
- SOURCE_AVATAR_MAPPING = {
14
- "Khamenei": "images/bot_avatar.png",
15
- "Sistani": "images/bot_avatar1.png",
16
- "Golpaygani": "images/bot_avatar2.png"
17
- }
18
-
19
- BOT_AVATARS_BASE64 = {
20
- source: get_base64_image(path) for source, path in SOURCE_AVATAR_MAPPING.items()
21
- }
22
-
23
- USER_AVATAR = get_base64_image("images/user_avatar.png")
24
-
25
- css = '''
26
- <style>
27
- .chat-row {
28
- display: flex;
29
- align-items: flex-end;
30
- margin: 1rem 0;
31
- direction: rtl;
32
- font-family: "Vazir", Tahoma, sans-serif;
33
- }
34
-
35
- .chat-row .avatar {
36
- flex-shrink: 0;
37
- margin: 0 0.5rem;
38
- }
39
-
40
- .chat-row .avatar img {
41
- width: 52px;
42
- height: 52px;
43
- border-radius: 50%;
44
- object-fit: cover;
45
- border: 2px solid #fff;
46
- box-shadow: 0 2px 6px rgba(0,0,0,0.2);
47
- }
48
-
49
- .chat-bubble {
50
- max-width: 70%;
51
- padding: 1rem 1.2rem;
52
- border-radius: 1rem;
53
- line-height: 1.8;
54
- font-size: 1rem;
55
- word-wrap: break-word;
56
- white-space: pre-wrap;
57
- animation: fadeIn 0.3s ease-in-out;
58
- }
59
-
60
- .chat-row.user {
61
- justify-content: flex-end;
62
- }
63
- .chat-row.user .chat-bubble {
64
- background: linear-gradient(145deg, #232832, #2f3542);
65
- color: #e4e6eb;
66
- border-bottom-right-radius: 0.4rem;
67
- }
68
-
69
- .chat-row.bot {
70
- justify-content: flex-start;
71
- }
72
- .chat-row.bot .chat-bubble {
73
- background: linear-gradient(145deg, #3e4658, #50586c);
74
- color: #f8f9fa;
75
- border-bottom-left-radius: 0.4rem;
76
- }
77
-
78
- @keyframes fadeIn {
79
- from {opacity: 0; transform: translateY(10px);}
80
- to {opacity: 1; transform: translateY(0);}
81
- }
82
- </style>
83
- '''
84
-
85
- # A function to generate the bot template with the correct base64 image
86
- def get_bot_template(avatar_base64: str) -> str:
87
- return f'''
88
- <div class="chat-row bot">
89
- <div class="avatar">
90
- <img src="data:image/png;base64,{avatar_base64}">
91
- </div>
92
- <div class="chat-bubble">{{{{MSG}}}}</div>
93
- </div>
94
- '''
95
-
96
- user_template = f'''
97
- <div class="chat-row user">
98
- <div class="chat-bubble">{{{{MSG}}}}</div>
99
- <div class="avatar">
100
- <img src="data:image/png;base64,{USER_AVATAR}">
101
- </div>
102
- </div>
103
- '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import torch
4
+ from langchain_huggingface import HuggingFaceEmbeddings, HuggingFacePipeline
5
+ from langchain_community.vectorstores import FAISS
6
+ from langchain.memory import ConversationBufferMemory
7
+ from langchain.chains import ConversationalRetrievalChain
8
+ from langchain.prompts import ChatPromptTemplate
9
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
10
+
11
+ # Import the new dynamic template components
12
+ from htmlTemplates import css, user_template, get_bot_template, BOT_AVATARS_BASE64
13
+
14
+ # تنظیم مسیر کش
15
+ cache_dir = "/tmp/hf_cache"
16
+ try:
17
+ os.makedirs(cache_dir, exist_ok=True) # ایجاد دایرکتوری کش اگر وجود ندارد
18
+ except PermissionError:
19
+ st.error(f"عدم دسترسی به دایرکتوری کش {cache_dir}. لطفاً تنظیمات محیط را بررسی کنید.")
20
+ cache_dir = "./cache_fallback" # مسیر جایگزین
21
+ os.makedirs(cache_dir, exist_ok=True)
22
+
23
+ # تنظیم متغیر محیطی HF_HOME
24
+ os.environ["HF_HOME"] = cache_dir
25
+
26
+ # ------------------- Conversation Chain Function -------------------
27
+ def get_conversation_chain(vectorstore, llm):
28
+ memory = ConversationBufferMemory(
29
+ memory_key='chat_history', return_messages=True
30
+ )
31
+ return ConversationalRetrievalChain.from_llm(
32
+ llm=llm,
33
+ retriever=vectorstore.as_retriever(),
34
+ memory=memory,
35
+ )
36
+
37
+
38
+ # ------------------- Classify Question -------------------
39
+ def classify_question(question, llm):
40
+ prompt = ChatPromptTemplate.from_template("""
41
+ متن زیر را بررسی کن و فقط یکی از این سه برچسب را برگردان:
42
+ - greeting: اگر متن سلام و احوال‌پرسی یا جملات دوستانه است
43
+ - islamic_fiqh: اگر پرسش درباره احکام شرعی و فقهی اسلام است
44
+ - irrelevant: اگر غیر از این دو بود
45
+
46
+ متن: {question}
47
+ برچسب:
48
+ """)
49
+ chain = prompt | llm
50
+ result = chain.invoke({"question": question})
51
+ return result.content.strip().lower()
52
+
53
+
54
+ # ------------------- Generate Friendly Response -------------------
55
+ def generate_friendly_response(question, label, llm):
56
+ if "greeting" in label:
57
+ prompt = ChatPromptTemplate.from_template("""
58
+ کاربر به شما سلام یا احوال‌پرسی کرده است.
59
+ نام شما حاجاقا محسنی هست.
60
+ متن کاربر: {question}
61
+ """)
62
+ else:
63
+ prompt = ChatPromptTemplate.from_template("""
64
+ کاربر پرسشی غیرمرتبط با احکام شرعی پرسیده است.
65
+ تو به عنوان حاجاقا محسنی
66
+ مودبانه و دوستانه به او بگویید که وظیفه‌ی شما فقط پاسخ به پرسش‌های شرعی است
67
+ و او را به مطرح کردن یک سؤال دینی تشویق کنید.
68
+ متن کاربر: {question}
69
+ """)
70
+
71
+ chain = prompt | llm
72
+ result = chain.invoke({"question": question})
73
+ return result.content.strip()
74
+
75
+
76
+ # ------------------- Handle User Input (Logic Only) -------------------
77
+ def handle_userinput(user_question):
78
+ if st.session_state.conversation is None:
79
+ st.warning("منابع هنوز آماده نشده‌اند.")
80
+ return
81
+
82
+ # ۱. افزودن پیام کاربر به تاریخچه برای نمایش فوری
83
+ st.session_state.messages.append({"role": "user", "content": user_question})
84
+
85
+ # ۲. طبقه‌بندی و دریافت پاسخ ربات
86
+ label = classify_question(user_question, st.session_state.llm)
87
+ if "islamic_fiqh" in label:
88
+ response = st.session_state.conversation({'question': user_question})
89
+ bot_reply = response['answer']
90
+ else: # greeting یا irrelevant
91
+ bot_reply = generate_friendly_response(user_question, label, st.session_state.llm)
92
+
93
+ # ۳. افزودن پاسخ ربات به تاریخچه
94
+ st.session_state.messages.append({"role": "bot", "content": bot_reply})
95
+
96
+
97
+ # ------------------- Main Streamlit App -------------------
98
+ def main():
99
+ st.set_page_config(page_title="شیخ جی پی تی", page_icon=":mosque:")
100
+ st.write(css, unsafe_allow_html=True)
101
+
102
+ st.markdown(
103
+ "<h2 style=\"text-align: center;font-size: clamp(24px, 5vw, 48px); white-space: nowrap;\">پاسخگوی احکام شرعی 🕋</h2>",
104
+ unsafe_allow_html=True
105
+ )
106
+
107
+ # مقداردهی اولیه session_state
108
+ if "conversation" not in st.session_state:
109
+ st.session_state.conversation = None
110
+ if "messages" not in st.session_state:
111
+ st.session_state.messages = []
112
+ if "llm" not in st.session_state:
113
+ # بارگذاری توکنایزر و مدل
114
+ try:
115
+ tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-7B-Instruct", cache_dir=cache_dir)
116
+ model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-7B-Instruct", cache_dir=cache_dir)
117
+ pipe = pipeline(
118
+ "text-generation",
119
+ model=model,
120
+ tokenizer=tokenizer,
121
+ max_new_tokens=512,
122
+ temperature=0.7,
123
+ device=0 if torch.cuda.is_available() else -1
124
+ )
125
+ st.session_state.llm = HuggingFacePipeline(pipeline=pipe)
126
+ except Exception as e:
127
+ st.error(f"خطا در بارگذاری مدل: {e}")
128
+ return
129
+ if "selected_source_english" not in st.session_state:
130
+ st.session_state.selected_source_english = "Khamenei"
131
+
132
+ # نمایش پیام‌ها
133
+ current_bot_avatar_base64 = BOT_AVATARS_BASE64.get(st.session_state.selected_source_english)
134
+ bot_template_with_avatar = get_bot_template(current_bot_avatar_base64)
135
+ for msg in st.session_state.messages:
136
+ if msg["role"] == "user":
137
+ st.write(user_template.replace("{{MSG}}", msg["content"]), unsafe_allow_html=True)
138
+ else:
139
+ st.write(bot_template_with_avatar.replace("{{MSG}}", msg["content"]), unsafe_allow_html=True)
140
+
141
+ # دریافت ورودی کاربر
142
+ if user_question := st.chat_input("سؤال شرعی خود را اینجا مطرح کنید..."):
143
+ handle_userinput(user_question)
144
+ st.rerun()
145
+
146
+ # --- Sidebar ---
147
+ with st.sidebar:
148
+ st.subheader("منابع فقهی")
149
+
150
+ SOURCE_MAPPINGS = {
151
+ "آیت الله خامنه‌ای": "Khamenei",
152
+ "آیت الله سیستانی": "Sistani",
153
+ "آیت الله گلپایگانی": "Golpaygani"
154
+ }
155
+ persian_options = list(SOURCE_MAPPINGS.keys())
156
+ current_persian_name = next(
157
+ (key for key, value in SOURCE_MAPPINGS.items()
158
+ if value == st.session_state.selected_source_english),
159
+ persian_options[0]
160
+ )
161
+
162
+ selected_source_persian = st.selectbox(
163
+ "مرجع تقلید خود را انتخاب کنید:",
164
+ persian_options,
165
+ index=persian_options.index(current_persian_name)
166
+ )
167
+ selected_source_english = SOURCE_MAPPINGS[selected_source_persian]
168
+
169
+ if st.session_state.selected_source_english != selected_source_english:
170
+ st.session_state.selected_source_english = selected_source_english
171
+ st.session_state.conversation = None
172
+ st.session_state.messages = []
173
+ st.rerun()
174
+
175
+ if st.session_state.conversation is None:
176
+ placeholder = st.empty()
177
+ placeholder.info(f"⏳ در حال بارگذاری منابع {selected_source_persian}...")
178
+ try:
179
+ embeddings = HuggingFaceEmbeddings(
180
+ model_name="heydariAI/persian-embeddings",
181
+ model_kwargs={'trust_remote_code': True},
182
+ cache_folder=cache_dir
183
+ )
184
+ vector_path = f"Resources/{st.session_state.selected_source_english}/faiss_index"
185
+ vectorstore = FAISS.load_local(
186
+ vector_path,
187
+ embeddings,
188
+ allow_dangerous_deserialization=True
189
+ )
190
+ st.session_state.conversation = get_conversation_chain(vectorstore, st.session_state.llm)
191
+ placeholder.success(
192
+ f"✅ منابع {selected_source_persian} آماده شدند. اکنون می‌توانید سؤال بپرسید.")
193
+ except Exception as e:
194
+ placeholder.error(f"⚠️ خطا در بارگذاری منابع: {e}")
195
+ else:
196
+ st.success(f"✅ منابع {selected_source_persian} بارگذاری شده‌اند")
197
+
198
+
199
+ if __name__ == '__main__':
200
+ main()