Keyvan1986 commited on
Commit
ab83207
·
verified ·
1 Parent(s): 92d62a5

Update Emma/app.py

Browse files
Files changed (1) hide show
  1. Emma/app.py +497 -73
Emma/app.py CHANGED
@@ -1,95 +1,519 @@
1
- # -*- coding: utf-8 -*-
2
- import sys
3
  import os
 
4
  import json
5
  import time
6
  import copy
7
- from datetime import datetime
 
8
 
9
- # --- 1. Dynamic Path Configuration (FIXED) ---
10
- # پیدا کردن مسیر نسبی برای اینکه روی هر سیستم عاملی کار کند
11
- CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) # پوشه utils
12
- BASE_DIR = os.path.dirname(CURRENT_DIR) # پوشه اصلی پروژه (Emma)
 
13
 
14
- # مسیر پوشه حافظه و فایل جیسون
15
- MEMORIES_DIR = os.path.join(BASE_DIR, "memories")
16
- MEMORY_FILE_PATH = os.path.join(MEMORIES_DIR, "update_memory_0512_eng.json")
 
 
 
17
 
18
- # ایجاد پوشه اگر وجود نداشته باشد
19
- os.makedirs(MEMORIES_DIR, exist_ok=True)
 
 
20
 
21
- print(f"✅ Memory System Initialized. Target Path: {MEMORY_FILE_PATH}")
 
 
22
 
23
- # --- 2. Safe Saving Function ---
24
- def save_json_safe(data, filepath):
25
- """
26
- ذخیره سازی ایمن داده‌ها در فرمت JSON.
27
- ابتدا در یک فایل موقت می‌نویسد و سپس جایگزین می‌کند تا از خرابی فایل جلوگیری شود.
28
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  try:
30
- temp_file = filepath + ".tmp"
31
- with open(temp_file, "w", encoding="utf-8") as f:
32
- json.dump(data, f, ensure_ascii=False, indent=4)
33
- f.flush()
34
- os.fsync(f.fileno()) # اطمینان از نوشتن روی دیسک
35
-
36
- # جایگزینی اتمیک (در لینوکس و ویندوزهای جدید امن است)
37
- os.replace(temp_file, filepath)
38
- # print(f"💾 Memory saved successfully to {os.path.basename(filepath)}")
39
- except Exception as e:
40
- print(f"❌ CRITICAL ERROR saving JSON: {e}")
41
 
42
- # --- 3. Memory Logic Functions ---
 
43
 
44
- def load_memory():
45
- """بارگذاری حافظه از فایل جیسون"""
46
- if os.path.exists(MEMORY_FILE_PATH):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  try:
48
- with open(MEMORY_FILE_PATH, 'r', encoding='utf-8') as f:
49
- return json.load(f)
50
- except json.JSONDecodeError:
51
- print("⚠️ Memory file is corrupted or empty. Starting fresh.")
52
- return []
53
- return []
54
-
55
- def save_local_memory(history, user_input=None, bot_response=None):
56
- """
57
- تابع اصلی برای ذخیره تاریخچه چت.
58
- این تابع باید از app.py فراخوانی شود.
59
- """
60
- # ساختار داده‌ای که می‌خواهیم ذخیره کنیم
61
- # فرض بر این است که history لیستی از تاپل‌ها (user, bot) است یا ساختار مشابه
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- current_data = load_memory()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
- # ایجاد یک رکورد جدید با زمان فعلی
66
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
 
67
 
68
- new_entry = {
69
- "timestamp": timestamp,
70
- "interaction": {
71
- "user": user_input if user_input else "N/A",
72
- "bot": bot_response if bot_response else "N/A"
73
- },
74
- "full_history_snapshot": history[-1] if history else [] # ذخیره آخرین وضعیت
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
-
77
- # اضافه کردن به لیست و ذخیره
78
- if isinstance(current_data, list):
79
- current_data.append(new_entry)
80
- else:
81
- # اگر ساختار قبلی لیست نبوده، ریست می‌شود (یا منطق مرج دلخواه شما)
82
- current_data = [current_data, new_entry]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
- save_json_safe(current_data, MEMORY_FILE_PATH)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
 
86
 
87
- # --- 4. Prompt Generation Functions (From your original code) ---
88
- # این توابع برای خلاصه‌سازی استفاده می‌شوند
 
89
 
90
- def summarize_content_prompt(content, user_name, boot_name, language='en'):
91
- prompt = 'Please summarize the following dialogue from a psychological perspective...\n'
92
- # ... (منطق کد شما در اینجا) ...
93
- return prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
- # (سایر توابع prompt generation را می‌توانید اینجا نگه دارید)
 
 
 
 
1
  import os
2
+ import sys
3
  import json
4
  import time
5
  import copy
6
+ import logging
7
+ import platform
8
 
9
+ # 1. ایمپورت‌ها
10
+ import gradio as gr
11
+ import nltk
12
+ import torch
13
+ import tiktoken
14
 
15
+ from openai import OpenAI as OpenAIClient
16
+ from llama_index.embeddings.openai import OpenAIEmbedding
17
+ from llama_index.llms.openai import OpenAI as LlamaIndexOpenAI
18
+ from llama_index.core import Settings
19
+ import torch.nn.functional as F
20
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
21
 
22
+ # ==========================================
23
+ # IMPORTANT: ایمپورت‌های متمرکز مدیریت حافظه
24
+ # ==========================================
25
+ from utils.memory_utils import load_memory, save_memory, enter_name_llamaindex
26
 
27
+ # تنظیم مسیرها
28
+ prompt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../')
29
+ sys.path.append(prompt_path)
30
 
31
+ # تلاش برای ایمپورت ماژول‌های داخلی
32
+ try:
33
+ from utils.sys_args import data_args, model_args
34
+ from utils.app_modules.utils import *
35
+ from utils.app_modules.presets import *
36
+ from utils.app_modules.overwrites import *
37
+ from utils.prompt_utils import *
38
+ # نکته: save_local_memory قدیمی حذف شد و با save_memory جایگزین شد
39
+ except ImportError:
40
+ data_args = {}
41
+ boot_actual_name_dict = {'en': 'Emma'}
42
+ generate_meta_prompt_dict_chatgpt = lambda: {'en': 'You are Emma.'}
43
+ generate_meta_prompt_dict_semantic_chatgpt = lambda: {'en': ''}
44
+ generate_meta_prompt_dict_semantic_episodic_chatgpt = lambda: {'en': ''}
45
+ generate_new_user_meta_prompt_dict_chatgpt = lambda: {'en': ''}
46
+
47
+ # ==========================================
48
+ # 2. تنظیمات NLTK
49
+ # ==========================================
50
+ nltk_data_dir = os.path.join(os.getcwd(), "nltk_data")
51
+ os.makedirs(nltk_data_dir, exist_ok=True)
52
+ nltk.data.path.append(nltk_data_dir)
53
+
54
+ def download_nltk_resource(resource_name):
55
  try:
56
+ nltk.data.find(f'tokenizers/{resource_name}')
57
+ except LookupError:
58
+ try:
59
+ nltk.download(resource_name, download_dir=nltk_data_dir)
60
+ except: pass
 
 
 
 
 
 
61
 
62
+ download_nltk_resource('punkt')
63
+ download_nltk_resource('punkt_tab')
64
 
65
+ # ==========================================
66
+ # 3. لود مدل و تنظیمات
67
+ # ==========================================
68
+ GAPGPT_BASE_URL = os.getenv("GAPGPT_BASE_URL", "https://api.gapgpt.app/v1")
69
+ openai_client_cache = {}
70
+
71
+ MODEL_ID = "Keyvan1986/Emma-Classification_Model"
72
+ hf_token = os.environ.get("HF_TOKEN")
73
+
74
+ _tokenizer = None
75
+ _classifier_model = None
76
+
77
+ try:
78
+ _tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=hf_token)
79
+ _classifier_model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID, token=hf_token)
80
+ _classifier_model.eval()
81
+ except Exception as e:
82
+ print(f"⚠️ Model load failed (ignoring): {e}")
83
+
84
+ def get_gapgpt_client(api_key):
85
+ if not api_key: return None
86
+ if api_key not in openai_client_cache:
87
+ openai_client_cache[api_key] = OpenAIClient(api_key=api_key, base_url=GAPGPT_BASE_URL)
88
+ return openai_client_cache[api_key]
89
+
90
+ api_path = "api_key_list.txt"
91
+ def read_apis(path):
92
+ keys = []
93
+ env_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("GAPGPT_API_KEY")
94
+ if env_key: keys.append(env_key)
95
+ if os.path.exists(path):
96
  try:
97
+ with open(path, 'r', encoding='utf8') as f:
98
+ for line in f:
99
+ if line.strip(): keys.append(line.strip())
100
+ except: pass
101
+ return keys
102
+
103
+ api_keys = read_apis(api_path)
104
+
105
+ # نکته: بخش لود کردن memory به صورت سراسری از اینجا حذف شد.
106
+ # مدیریت فایل کاملاً به memory_utils سپرده شده است.
107
+
108
+ language = 'en'
109
+ boot_actual_name = boot_actual_name_dict.get(language, "Emma")
110
+ meta_prompt = generate_meta_prompt_dict_chatgpt().get(language, "")
111
+ meta_prompt_semantic = generate_meta_prompt_dict_semantic_chatgpt().get(language, "")
112
+ meta_prompt_semantic_episodic = generate_meta_prompt_dict_semantic_episodic_chatgpt().get(language, "")
113
+ new_user_meta_prompt = generate_new_user_meta_prompt_dict_chatgpt().get(language, "")
114
+
115
+ # ==========================================
116
+ # 4. توابع هسته (Core Logic)
117
+ # ==========================================
118
+
119
+ def chatgpt_chat(prompt, system, history, gpt_config, api_index=0):
120
+ if not api_keys: return "Error: No API Key."
121
+ retry_times = 3
122
+ for _ in range(retry_times):
123
+ try:
124
+ messages = [{"role": "system", "content": system}]
125
+ if history:
126
+ for h in history:
127
+ if isinstance(h, dict) and 'role' in h and 'content' in h:
128
+ messages.append(h)
129
+ messages.append({"role": "user", "content": prompt})
130
+ client = get_gapgpt_client(api_keys[api_index])
131
+ if not client: return "Error: Client error."
132
+ resp = client.chat.completions.create(messages=messages, **gpt_config)
133
+ return resp.choices[0].message.content
134
+ except Exception as e:
135
+ print(f"Chat failed: {e}")
136
+ api_index = (api_index + 1) % len(api_keys)
137
+ return "Error: Service unavailable."
138
+
139
+ def classify_query_local(text):
140
+ if not _tokenizer or not _classifier_model: return "unknown"
141
+ try:
142
+ inputs = _tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
143
+ with torch.no_grad():
144
+ outputs = _classifier_model(**inputs)
145
+ pid = torch.argmax(F.softmax(outputs.logits, dim=-1), dim=-1).item()
146
+ labels = {0: "episodic_memory", 1: "semantic_memory", 2: "semantic-episodic", 3: "unknown"}
147
+ return labels.get(pid, "unknown")
148
+ except:
149
+ return "unknown"
150
+
151
+ def predict_new(
152
+ text, history, top_p, temperature, max_length_tokens, max_context_length_tokens,
153
+ user_name, user_memory, api_index, semantic_memory_text, query_category
154
+ ):
155
+ if history is None: history = []
156
 
157
+ chat_cfg = {
158
+ "model": "gpt-4o",
159
+ "temperature": temperature,
160
+ "max_tokens": max_length_tokens,
161
+ "top_p": top_p,
162
+ "frequency_penalty": 0.4,
163
+ "presence_penalty": 0.2,
164
+ "n": 1
165
+ }
166
+
167
+ try:
168
+ system_prompt, _ = build_prompt_with_search_memory_llamaindex(
169
+ history=history,
170
+ query=text,
171
+ user_memory=user_memory, # اینجا user_memory تازه دریافت می‌شود
172
+ user_name=user_name,
173
+ user_memory_index=None,
174
+ service_context=None,
175
+ api_keys=api_keys,
176
+ api_index=api_index,
177
+ meta_prompt=meta_prompt,
178
+ new_user_meta_prompt=new_user_meta_prompt,
179
+ data_args=data_args,
180
+ boot_actual_name=boot_actual_name,
181
+ semantic_memory_text=semantic_memory_text,
182
+ query_category=query_category,
183
+ meta_prompt_semantic=meta_prompt_semantic,
184
+ meta_prompt_semantic_episodic=meta_prompt_semantic_episodic
185
+ )
186
+ except Exception as e:
187
+ print(f"Prompt Error: {e}")
188
+ system_prompt = "You are a helpful assistant."
189
+
190
+ hist_for_llm = history[-10:] if len(history) > 10 else history
191
+ response = chatgpt_chat(text, system_prompt, hist_for_llm, chat_cfg, api_index)
192
 
193
+ new_history = history + [
194
+ {"role": "user", "content": text},
195
+ {"role": "assistant", "content": response}
196
+ ]
197
 
198
+ # نکته مهم: عملیات ذخیره‌سازی از اینجا حذف شد.
199
+ # ذخیره‌سازی باید در لایه بالاتر (respond) انجام شود که کل آبجکت مموری را در اختیار دارد.
200
+
201
+ return new_history, new_history
202
+
203
+ # ==========================================
204
+ # 5. رابط کاربری (Full Featured + Safe)
205
+ # ==========================================
206
+
207
+ def create_gradio_interface():
208
+ # استایل‌های CSS قدیمی شما
209
+ css = """
210
+ .mobile-button button {
211
+ width: 100% !important;
212
+ padding: 12px !important;
213
+ font-size: 16px !important;
214
+ border-radius: 10px !important;
215
+ background: linear-gradient(to right, #ff9966, #ff5e62) !important;
216
+ color: white !important;
217
+ font-weight: bold;
218
  }
219
+ .gr-button-primary {
220
+ background-color: #6a11cb !important;
221
+ background-image: linear-gradient(to right, #6a11cb, #2575fc) !important;
222
+ color: #fff !important;
223
+ font-weight: bold !important;
224
+ border: none !important;
225
+ }
226
+ .gr-button-secondary {
227
+ background-color: #f7971e !important;
228
+ background-image: linear-gradient(to right, #f7971e, #ffd200) !important;
229
+ color: #000 !important;
230
+ font-weight: bold !important;
231
+ border: none !important;
232
+ }
233
+ """
234
+
235
+ with gr.Blocks(title="EMMA AI", css=css) as demo:
236
+
237
+ # State: فقط داده‌های موقت را نگه می‌دارد.
238
+ # کل مموری دیگر در State نیست تا از تداخل جلوگیری شود.
239
+ state = gr.State({
240
+ "history": [],
241
+ "user_name": None,
242
+ "api_index": 0,
243
+ "semantic": "",
244
+ "init": False
245
+ })
246
+
247
+ header = gr.Markdown("## 🧠 EMMA: Your Empathetic Mental Health Assistant\nWelcome! Please enter your name to begin.")
248
+
249
+ # --- بخش لاگین ---
250
+ with gr.Column(visible=True) as login_col:
251
+ with gr.Accordion("🔐 Start New Session", open=True):
252
+ with gr.Row():
253
+ name_input = gr.Textbox(label="Your Name", placeholder="e.g., Alex")
254
+ age_input = gr.Textbox(label="Age", placeholder="e.g., 28")
255
+ with gr.Row():
256
+ gender_input = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other"])
257
+ occupation_input = gr.Textbox(label="Occupation", placeholder="e.g., Engineer")
258
+ residence_input = gr.Textbox(label="Place of Residence", placeholder="e.g., Berlin")
259
+ start_btn = gr.Button("🎯 Start Session", variant="primary")
260
+
261
+ # --- بخش اعلان سیستم ---
262
+ system_msg = gr.Textbox(label="🔔 System Messages", interactive=False, max_lines=2, visible=False)
263
+
264
+ # --- بخش چت ---
265
+ with gr.Column(visible=False) as chat_col:
266
+ chatbot = gr.Chatbot(height=500, type="messages", label="💬 EMMA Conversation")
267
+
268
+ with gr.Row():
269
+ msg_input = gr.Textbox(show_label=False, placeholder="Type a message...", scale=4)
270
+ send_btn = gr.Button("📤 Send", variant="primary", scale=1, elem_classes=["mobile-button"])
271
+
272
+ with gr.Row(equal_height=True):
273
+ clear_btn = gr.Button("🧹 Clear", variant="secondary", elem_classes=["mobile-button"])
274
+ new_session_btn = gr.Button("🔄 New Session", variant="secondary", elem_classes=["mobile-button"])
275
+ switch_user_btn = gr.Button("👥 Switch User", variant="primary", elem_classes=["mobile-button"])
276
+
277
+ # ==========================================
278
+ # توابع کنترلی UI (اصلاح شده برای ذخیره‌سازی صحیح)
279
+ # ==========================================
280
+
281
+ def start_session(name, age, gender, job, city, current_state):
282
+ if not name or not name.strip():
283
+ return (
284
+ gr.update(), gr.update(), gr.update(), # login, chat, system
285
+ gr.update(value="## ⚠️ Please enter a valid name."),
286
+ current_state,
287
+ [] # empty history for chatbot
288
+ )
289
+
290
+ # 1. بارگذاری حافظه تازه از دیسک
291
+ memory_data = load_memory()
292
+
293
+ # 2. ایجاد یا به‌روزرسانی یوزر در حافظه
294
+ # enter_name_llamaindex روی دیکشنری memory_data تغییر ایجاد می‌کند
295
+ enter_name_llamaindex(name, memory_data, data_args)
296
+
297
+ # 3. ذخیره اطلاعات پروفایل
298
+ if name in memory_data:
299
+ memory_data[name]["profile"] = {
300
+ "age": age, "gender": gender,
301
+ "occupation": job, "residence": city
302
+ }
303
+
304
+ # اطمینان از وجود لیست سشن‌ها
305
+ if "sessions" not in memory_data[name] or not memory_data[name]["sessions"]:
306
+ memory_data[name]["sessions"] = [{
307
+ "session_id": 0, "date": time.strftime("%Y-%m-%d"), "conversation": []
308
+ }]
309
+
310
+ # 4. ذخیره کل حافظه روی دیسک (بسیار مهم)
311
+ save_memory(memory_data)
312
+ print(f"✅ User {name} loaded/created and saved.")
313
+
314
+ # 5. به‌روزرسانی State
315
+ new_st = copy.deepcopy(current_state)
316
+ new_st["user_name"] = name
317
+ new_st["init"] = True
318
+
319
+ # استخراج حافظه معنایی برای کانتکست
320
+ try:
321
+ user_mem = memory_data.get(name, {})
322
+ if isinstance(user_mem, dict):
323
+ new_st["semantic"] = str(user_mem.get("semantic_memory", ""))
324
+
325
+ # بازیابی تاریخچه آخرین سشن (اختیاری - اگر می‌خواهید چت قبلی نمایش داده شود)
326
+ last_history = user_mem.get("sessions", [{}])[-1].get("conversation", [])
327
+ new_st["history"] = last_history
328
+ except:
329
+ last_history = []
330
+ new_st["history"] = []
331
+
332
+ welcome_txt = f"## 🧠 EMMA: Session for {name}"
333
+ sys_txt = f"Welcome {name}! Memory loaded successfully."
334
+
335
+ return (
336
+ gr.update(visible=False), # Hide Login
337
+ gr.update(visible=True), # Show Chat
338
+ gr.update(visible=True, value=sys_txt), # Show System Msg
339
+ gr.update(value=welcome_txt), # Update Header
340
+ new_st,
341
+ last_history # نمایش تاریخچه در چت‌بات
342
+ )
343
+
344
+ def respond(msg, current_state):
345
+ if not msg or not current_state.get("init"):
346
+ return [], current_state, "Please login first."
347
+
348
+ user_name = current_state["user_name"]
349
+
350
+ # 1. بارگذاری حافظه تازه برای اطمینان از داشتن آخرین وضعیت
351
+ memory_data = load_memory()
352
+ user_memory = memory_data.get(user_name, {})
353
+
354
+ cat = classify_query_local(msg)
355
+
356
+ # 2. تولید پاسخ (فقط تولید، بدون ذخیره)
357
+ _, new_hist = predict_new(
358
+ text=msg,
359
+ history=current_state["history"],
360
+ top_p=0.9, temperature=0.7, max_length_tokens=512, max_context_length_tokens=200,
361
+ user_name=user_name,
362
+ user_memory=user_memory, # ارسال آبجکت یوزر مموری
363
+ api_index=current_state["api_index"],
364
+ semantic_memory_text=current_state["semantic"],
365
+ query_category=cat
366
+ )
367
+
368
+ # 3. اعمال تغییرات در آبجکت کل حافظه
369
+ if user_name in memory_data and memory_data[user_name].get("sessions"):
370
+ memory_data[user_name]["sessions"][-1]["conversation"] = new_hist
371
+
372
+ # 4. ذخیره نهایی روی دیسک
373
+ save_memory(memory_data)
374
+
375
+ # 5. آپدیت State گرادیو
376
+ new_st = copy.deepcopy(current_state)
377
+ new_st["history"] = new_hist
378
+
379
+ return new_hist, new_st, ""
380
+
381
+ def clear_chat(current_state):
382
+ # پاک کردن چت فقط در محیط بصری و State فعلی (بدون حذف از دیتابیس برای امنیت)
383
+ new_st = copy.deepcopy(current_state)
384
+ new_st["history"] = []
385
+ return [], new_st, "Conversation view cleared (Memory retained)."
386
+
387
+ def new_session_logic(current_state):
388
+ u_name = current_state.get("user_name")
389
+ if not u_name:
390
+ return [], current_state, "Error: No user."
391
+
392
+ # 1. بارگذاری حافظه
393
+ memory_data = load_memory()
394
+
395
+ if u_name in memory_data:
396
+ try:
397
+ user_sessions = memory_data[u_name].get("sessions", [])
398
+
399
+ # ایجاد سشن جدید
400
+ new_s = {
401
+ "session_id": len(user_sessions),
402
+ "date": time.strftime("%Y-%m-%d"),
403
+ "conversation": []
404
+ }
405
+
406
+ if "sessions" not in memory_data[u_name]:
407
+ memory_data[u_name]["sessions"] = []
408
+
409
+ memory_data[u_name]["sessions"].append(new_s)
410
+
411
+ # 2. ذخیره تغییرات
412
+ save_memory(memory_data)
413
+ print(f"🔄 New session created for {u_name}")
414
+
415
+ except Exception as e:
416
+ print(f"New Session Error: {e}")
417
+
418
+ # ریست کردن State
419
+ new_st = copy.deepcopy(current_state)
420
+ new_st["history"] = []
421
+
422
+ return [], new_st, "🆕 New session started & Saved."
423
+
424
+ def switch_user_logic(current_state):
425
+ # ریست کامل State
426
+ new_st = {
427
+ "history": [],
428
+ "user_name": None,
429
+ "api_index": 0,
430
+ "semantic": "",
431
+ "init": False
432
+ }
433
+ return (
434
+ gr.update(visible=True), # Show Login
435
+ gr.update(visible=False), # Hide Chat
436
+ gr.update(visible=False, value=""), # Hide System Msg
437
+ gr.update(value="## 🧠 EMMA: Switch User Mode"),
438
+ new_st,
439
+ gr.update(value=""), # Clear Name Input
440
+ [] # Clear Chatbot
441
+ )
442
+
443
+ # ==========================================
444
+ # اتصال رویدادها (Event Listeners)
445
+ # ==========================================
446
+
447
+ start_btn.click(
448
+ start_session,
449
+ inputs=[name_input, age_input, gender_input, occupation_input, residence_input, state],
450
+ outputs=[login_col, chat_col, system_msg, header, state, chatbot],
451
+ api_name=False
452
+ )
453
 
454
+ send_btn.click(
455
+ respond,
456
+ inputs=[msg_input, state],
457
+ outputs=[chatbot, state, msg_input],
458
+ api_name=False
459
+ )
460
+
461
+ msg_input.submit(
462
+ respond,
463
+ inputs=[msg_input, state],
464
+ outputs=[chatbot, state, msg_input],
465
+ api_name=False
466
+ )
467
+
468
+ clear_btn.click(
469
+ clear_chat,
470
+ inputs=[state],
471
+ outputs=[chatbot, state, system_msg],
472
+ api_name=False
473
+ )
474
+
475
+ new_session_btn.click(
476
+ new_session_logic,
477
+ inputs=[state],
478
+ outputs=[chatbot, state, system_msg],
479
+ api_name=False
480
+ )
481
+
482
+ switch_user_btn.click(
483
+ switch_user_logic,
484
+ inputs=[state],
485
+ outputs=[login_col, chat_col, system_msg, header, state, name_input, chatbot],
486
+ api_name=False
487
+ )
488
 
489
+ return demo
490
 
491
+ def main():
492
+ if api_keys:
493
+ os.environ["OPENAI_API_KEY"] = api_keys[0]
494
 
495
+ try:
496
+ Settings.llm = LlamaIndexOpenAI(
497
+ model="gpt-4o", temperature=1,
498
+ api_key=os.environ.get("OPENAI_API_KEY"),
499
+ api_base=GAPGPT_BASE_URL
500
+ )
501
+ Settings.embed_model = OpenAIEmbedding(
502
+ api_key=os.environ.get("OPENAI_API_KEY"),
503
+ api_base=GAPGPT_BASE_URL,
504
+ model="text-embedding-3-small"
505
+ )
506
+ except Exception as e:
507
+ print(f"Settings Warning: {e}")
508
+
509
+ demo = create_gradio_interface()
510
+
511
+ print("🚀 Launching Gradio Server (Fixed Memory Saving)...")
512
+ demo.queue().launch(
513
+ server_name="0.0.0.0",
514
+ server_port=7860,
515
+ ssr_mode=False
516
+ )
517
 
518
+ if __name__ == "__main__":
519
+ main()