Keyvan1986 commited on
Commit
d1816c2
·
verified ·
1 Parent(s): 18b18b1

Update Emma/app.py

Browse files
Files changed (1) hide show
  1. Emma/app.py +212 -421
Emma/app.py CHANGED
@@ -8,6 +8,7 @@ import signal
8
  import logging
9
  import platform
10
 
 
11
  import gradio as gr
12
  import nltk
13
  import torch
@@ -15,543 +16,333 @@ import tiktoken
15
 
16
  from openai import OpenAI as OpenAIClient
17
  from llama_index.embeddings.openai import OpenAIEmbedding
18
-
19
- # from langchain_openai import ChatOpenAI, OpenAI as LangChainOpenAI
20
  from llama_index.llms.openai import OpenAI as LlamaIndexOpenAI
21
- from llama_index.core import (
22
- Settings,
23
- VectorStoreIndex,
24
- SimpleDirectoryReader,
25
- PromptHelper,
26
- ServiceContext # برای جلوگیری از NameError ایمپورت شده ولی استفاده نمی‌شود
27
- )
28
- from llama_index.core.indices.prompt_helper import PromptHelper
29
 
30
- # ==========================================
31
- # 1. تنظیمات اولیه و مسیرها
32
- # ==========================================
33
  prompt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../')
34
  sys.path.append(prompt_path)
35
 
36
- # فرض بر این است که این ماژول‌ها در فایل‌های دیگر موجود هستند
37
- from utils.sys_args import data_args, model_args
38
- from utils.app_modules.utils import *
39
- from utils.app_modules.presets import *
40
- from utils.app_modules.overwrites import *
41
- from utils.prompt_utils import *
42
- from utils.memory_utils import (
43
- enter_name_llamaindex,
44
- summarize_memory_event_personality,
45
- save_local_memory,
46
- extract_session_summary,
47
- extract_semantic_memory,
48
- )
 
 
 
 
49
 
50
  # ==========================================
51
- # 2. تنظیمات NLTK (اصلاح شده برای Spaces)
52
  # ==========================================
53
- # ایجاد پوشه لوکال برای NLTK اگر دسترسی روت نباشد
54
  nltk_data_dir = os.path.join(os.getcwd(), "nltk_data")
55
- if not os.path.exists(nltk_data_dir):
56
- os.makedirs(nltk_data_dir, exist_ok=True)
57
  nltk.data.path.append(nltk_data_dir)
58
 
59
  def download_nltk_resource(resource_name):
60
  try:
61
  nltk.data.find(f'tokenizers/{resource_name}')
62
  except LookupError:
63
- print(f"Downloading NLTK resource: {resource_name}")
64
- nltk.download(resource_name, download_dir=nltk_data_dir)
 
 
65
 
66
  download_nltk_resource('punkt')
67
  download_nltk_resource('punkt_tab')
68
 
69
- tokenizer = tiktoken.get_encoding("cl100k_base")
 
 
70
  GAPGPT_BASE_URL = os.getenv("GAPGPT_BASE_URL", "https://api.gapgpt.app/v1")
71
  openai_client_cache = {}
72
 
73
- # ==========================================
74
- # 3. لود مدل Hugging Face
75
- # ==========================================
76
  import torch.nn.functional as F
77
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
78
 
79
  MODEL_ID = "Keyvan1986/Emma-Classification_Model"
80
  hf_token = os.environ.get("HF_TOKEN")
81
 
82
- print(f"Loading model from Hugging Face Hub: {MODEL_ID}")
 
 
83
  try:
 
84
  _tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=hf_token)
85
  _classifier_model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID, token=hf_token)
86
  _classifier_model.eval()
87
- print("✅ Model loaded successfully!")
88
  except Exception as e:
89
- print(f" Error loading model: {e}")
90
- _tokenizer = None
91
- _classifier_model = None
92
 
93
-
94
- # حذف Type Hint برای جلوگیری از خطای Gradio
95
  def get_gapgpt_client(api_key):
96
- if not api_key:
97
- raise ValueError("API key is missing while attempting to create a GapGPT client.")
98
  if api_key not in openai_client_cache:
99
  openai_client_cache[api_key] = OpenAIClient(api_key=api_key, base_url=GAPGPT_BASE_URL)
100
  return openai_client_cache[api_key]
101
 
102
- os_name = platform.system()
103
- stop_stream = False
104
-
105
- def signal_handler(signal_number, frame):
106
- global stop_stream
107
- stop_stream = True
108
-
109
- VECTOR_SEARCH_TOP_K = 2
110
  api_path = "api_key_list.txt"
111
-
112
  def read_apis(path):
113
- api_keys_local = []
114
- # اولویت با متغیر محیطی
115
  env_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("GAPGPT_API_KEY")
116
- if env_key:
117
- api_keys_local.append(env_key)
118
-
119
- # اگر فایل وجود داشت
120
  if os.path.exists(path):
121
  try:
122
  with open(path, 'r', encoding='utf8') as f:
123
  for line in f:
124
- line = line.strip()
125
- if line:
126
- api_keys_local.append(line)
127
- except Exception:
128
- pass
129
- return api_keys_local
130
-
131
- # تنظیم حافظه
132
  memory_dir = os.path.join(os.getcwd(), "memories", "update_memory_0512_eng.json")
133
- directory = os.path.dirname(memory_dir)
134
- if directory:
135
- os.makedirs(directory, exist_ok=True)
136
 
137
  if not os.path.exists(memory_dir):
138
  with open(memory_dir, "w", encoding="utf-8") as f:
139
  json.dump({}, f)
140
 
141
- global memory
142
  with open(memory_dir, "r", encoding="utf-8") as f:
143
  memory = json.load(f)
144
 
145
- # تنظیمات زبان و پرامپت‌ها
146
  language = 'en'
147
- user_keyword = generate_user_keyword()[language]
148
- ai_keyword = generate_ai_keyword()[language]
149
- boot_name = boot_name_dict[language]
150
- boot_actual_name = boot_actual_name_dict[language]
151
- meta_prompt = generate_meta_prompt_dict_chatgpt()[language]
152
- meta_prompt_semantic = generate_meta_prompt_dict_semantic_chatgpt()[language]
153
- meta_prompt_semantic_episodic = generate_meta_prompt_dict_semantic_episodic_chatgpt()[language]
154
- new_user_meta_prompt = generate_new_user_meta_prompt_dict_chatgpt()[language]
155
-
156
- # بارگذاری API KEY ها به صورت سراسری
157
- api_keys = read_apis(api_path)
158
- if not api_keys:
159
- print(f"Warning: No API keys found. App may fail.")
160
-
161
- new_conversation = False
162
- deactivated_keys = []
163
- logging.basicConfig(
164
- level=logging.DEBUG,
165
- format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
166
- )
167
 
168
  # ==========================================
169
- # 4. توابع اصلی چت و طبقه‌بندی
170
  # ==========================================
171
 
172
  def chatgpt_chat(prompt, system, history, gpt_config, api_index=0):
173
- retry_times, count = 5, 0
174
- response = None
175
-
176
- while response is None and count < retry_times:
177
  try:
178
- request = copy.deepcopy(gpt_config)
179
-
180
- if data_args.language == 'en':
181
- message = [
182
- {"role": "system", "content": system.strip()},
183
- {"role": "user", "content": "Hi!"},
184
- {"role": "assistant",
185
- "content": f"Hi! I'm {boot_actual_name}! I will give you warm companion!"},
186
- ]
187
- else:
188
- message = [
189
- {"role": "system", "content": system.strip()},
190
- {"role": "user", "content": "Hi"},
191
- {"role": "assistant",
192
- "content": f"Hi! I'm {boot_actual_name}! I will give you warm companion!"},
193
- ]
194
-
195
  if history:
196
- if isinstance(history[0], list): # Old Gradio format
197
- for q, a in history:
198
- message.append({"role": "user", "content": str(q)})
199
- message.append({"role": "assistant", "content": str(a)})
200
- elif isinstance(history[0], dict): # New Gradio format
201
- for msg in history:
202
- if msg.get('role') in ['user', 'assistant']:
203
- message.append(msg)
204
-
205
- message.append({"role": "user", "content": f"{prompt}"})
206
-
207
- if not api_keys:
208
- return "Error: No API Keys available."
209
-
210
  client = get_gapgpt_client(api_keys[api_index])
211
- response_obj = client.chat.completions.create(messages=message, **request)
212
- response = response_obj.choices[0].message.content
213
-
 
214
  except Exception as e:
215
  print(f"Chat Error: {e}")
216
- if 'This key is associated with a deactivated account' in str(e):
217
- if api_keys[api_index] not in deactivated_keys:
218
- deactivated_keys.append(api_keys[api_index])
219
-
220
- if api_keys:
221
- api_index = api_index + 1 if api_index < len(api_keys) - 1 else 0
222
- loop_check = 0
223
- while api_keys[api_index] in deactivated_keys and loop_check < len(api_keys):
224
- api_index = api_index + 1 if api_index < len(api_keys) - 1 else 0
225
- loop_check += 1
226
- count += 1
227
-
228
- return response if response else ''
229
 
230
  def classify_query_local(text):
231
- if _tokenizer is None or _classifier_model is None:
232
- return "unknown"
233
-
234
- id2label_map = {
235
- 0: "episodic_memory",
236
- 1: "semantic_memory",
237
- 2: "semantic-episodic",
238
- 3: "unknown"
239
- }
240
-
241
  try:
242
- inputs = _tokenizer(
243
- text,
244
- return_tensors="pt",
245
- truncation=True,
246
- padding=True,
247
- max_length=512
248
- )
249
  with torch.no_grad():
250
  outputs = _classifier_model(**inputs)
251
-
252
- logits = outputs.logits
253
- probabilities = F.softmax(logits, dim=-1)
254
- predicted_class_id = torch.argmax(probabilities, dim=-1).item()
255
-
256
- category = id2label_map.get(predicted_class_id, "unknown")
257
- print(f"Local Classifier: '{text}' -> {category}")
258
- return category
259
-
260
- except Exception as e:
261
- print(f"Error in local classification: {e}")
262
  return "unknown"
263
 
264
-
265
- # اصلاح شده: حذف تمام Type Hint ها برای جلوگیری از خطای Pydantic/Gradio
266
  def predict_new(
267
- text,
268
- history,
269
- top_p,
270
- temperature,
271
- max_length_tokens,
272
- max_context_length_tokens,
273
- user_name,
274
- user_memory,
275
- user_memory_index,
276
- service_context,
277
- api_index,
278
- semantic_memory_text,
279
- query_category,
280
  ):
281
- chatgpt_cfg = {
 
 
282
  "model": "gpt-4o",
283
  "temperature": temperature,
284
  "max_tokens": max_length_tokens,
285
  "top_p": top_p,
286
  "frequency_penalty": 0.4,
287
  "presence_penalty": 0.2,
288
- "n": 1,
289
  }
290
 
291
- if text == "":
292
- return history, history, "Empty context."
293
-
294
- if history is None:
295
- history = []
296
-
297
- # استفاده از api_keys سراسری
298
- system_prompt, related_memo = build_prompt_with_search_memory_llamaindex(
299
- history=history,
300
- query=text,
301
- user_memory=user_memory,
302
- user_name=user_name,
303
- user_memory_index=user_memory_index,
304
- service_context=service_context,
305
- api_keys=api_keys,
306
- api_index=api_index,
307
- meta_prompt=meta_prompt,
308
- new_user_meta_prompt=new_user_meta_prompt,
309
- data_args=data_args,
310
- boot_actual_name=boot_actual_name,
311
- semantic_memory_text=semantic_memory_text,
312
- query_category=query_category,
313
- meta_prompt_semantic=meta_prompt_semantic,
314
- meta_prompt_semantic_episodic=meta_prompt_semantic_episodic,
315
- )
316
-
317
- current_history_for_llm = history
318
- if len(history) > data_args.max_history * 2:
319
- current_history_for_llm = history[-(data_args.max_history * 2):]
320
-
321
- response = chatgpt_chat(
322
- prompt=text,
323
- system=system_prompt,
324
- history=current_history_for_llm,
325
- gpt_config=chatgpt_cfg,
326
- api_index=api_index,
327
- )
328
-
329
- if torch.cuda.is_available():
330
- torch.cuda.empty_cache()
331
 
 
 
 
 
332
  new_history = history + [
333
  {"role": "user", "content": text},
334
  {"role": "assistant", "content": response}
335
  ]
336
-
337
  if user_name:
338
- save_local_memory(memory, new_history, user_name, data_args)
339
-
340
- return new_history, new_history, "Generating..."
341
 
 
342
 
343
  # ==========================================
344
- # 5. رابط کاربری Gradio
345
  # ==========================================
346
 
347
- def create_gradio_interface(service_context, api_keys_arg):
348
- with gr.Blocks(title="EMMA") as demo:
349
-
350
- gr.HTML("""
351
- <style>
352
- .main-container { max-width: 800px !important; margin: auto !important; padding: 20px; border-radius: 15px; }
353
- .custom-blue-btn { background-color: #E0F7FA !important; border: 1px solid #4DD0E1 !important; color: #006064 !important; }
354
- .no-bg { background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; gap: 10px; }
355
- .no-bg > .form { background: transparent !important; border: none !important; }
356
- footer { display: none !important; }
357
- </style>
358
- """)
359
-
360
  state = gr.State({
361
  "history": [],
362
- "user_name": None,
363
- "memory": memory,
364
- "data_args": data_args,
365
- "service_context": service_context,
366
- "api_keys": api_keys_arg, # ذخیره کلیدها در استیت
367
- "api_index": 0,
368
- "semantic_memory_text": "",
369
- "new_conversation": True,
370
- "initialized": False
371
  })
372
 
373
- with gr.Column(elem_classes=["main-container"]):
374
- header = gr.Markdown("## 🧠 EMMA: Your Empathetic Mental Health Assistant")
375
-
376
- with gr.Accordion("🔐 Start New Session", open=True):
377
- with gr.Column() as username_row:
378
- username_input = gr.Textbox(label="Your Name", placeholder="e.g., Alex")
379
- age_input = gr.Textbox(label="Age", placeholder="e.g., 28")
380
- gender_input = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other"])
381
- occupation_input = gr.Textbox(label="Occupation")
382
- residence_input = gr.Textbox(label="Place of Residence")
383
- submit_name_btn = gr.Button("🎯 Start Session", elem_classes=["custom-blue-btn"])
384
-
385
- system_msg = gr.Textbox(label="System", interactive=False, max_lines=1)
386
-
387
- with gr.Column(visible=False) as chat_interface:
388
- active_header = gr.Markdown()
389
- chatbot = gr.Chatbot(label="Chat", height=500, type="messages")
390
-
391
- with gr.Row(elem_classes=["no-bg"]):
392
- user_input = gr.Textbox(placeholder="Type here...", show_label=False, scale=4, container=False)
393
- submit_btn = gr.Button("Send", elem_classes=["custom-blue-btn"], scale=1)
394
-
395
- with gr.Row(elem_classes=["no-bg"]):
396
- clear_btn = gr.Button("Clear", elem_classes=["custom-blue-btn"])
397
- new_session_btn = gr.Button("New Session", elem_classes=["custom-blue-btn"])
398
- switch_user_btn = gr.Button("Switch User", elem_classes=["custom-blue-btn"])
 
 
399
 
400
- def initialize_session(name, age, gender, occupation, residence, state):
401
- if not name.strip():
402
- return (gr.update(visible=True), gr.update(visible=False), "Invalid name.", gr.update(value=""), gr.update(), state, gr.update())
403
 
404
- hello_msg, user_memory, sessions_memory, episodic_memory, semantic_memory = enter_name_llamaindex(name, memory, data_args)
 
 
405
 
406
- # Update memory dict locally
407
- memory[name]["profile"] = {"age": age, "gender": gender, "occupation": occupation, "residence": residence}
408
 
409
- new_state = state.copy()
410
- new_state["user_name"] = name
411
- new_state["memory"] = memory
412
- new_state["initialized"] = True
413
- new_state["semantic_memory_text"] = semantic_memory
414
- new_state["history"] = []
415
-
416
- return (
417
- gr.update(visible=False),
418
- gr.update(visible=True),
419
- hello_msg if hello_msg else f"Welcome {name}!",
420
- gr.update(value=f"## 🧠 EMMA: Session for {name}"),
421
- gr.update(value=""),
422
- new_state,
423
- gr.update(value="")
424
- )
425
-
426
- def respond(message, state):
427
- if not state["initialized"]:
428
- return [], state, "Please enter your name first."
429
- if not message.strip():
430
- return state["history"], state, "Empty input."
431
-
432
- # ذخیره و طبقه‌بندی
433
- enter_name_llamaindex(state["user_name"], memory, data_args)
434
- memo, semantic_memory_text = save_local_memory(memory, state["history"], state["user_name"], data_args)
435
- query_category = classify_query_local(message)
436
-
437
- if query_category == "semantic_memory":
438
- user_memory_index = semantic_memory # این متغیر باید از ورودی تابع یا استیت بیاید، اما فعلاً طبق کد شما پیش می‌رویم
439
- # نکته: در کد اصلی شما semantic_memory در enter_name_llamaindex برمی‌گردد ولی اینجا در اسکوپ نیست
440
- # فرض می‌کنیم enter_name_llamaindex مقادیر را آپدیت می‌کند یا از استیت می‌خوانیم
441
- # برای سادگی فعلاً None می‌گذاریم اگر در استیت نیست، اما بهتر است از new_state استفاده کنید.
442
- user_memory_index = None
443
- elif query_category == "episodic_memory":
444
- user_memory_index = None
445
- else:
446
- user_memory_index = None
447
-
448
- # فراخوانی تابع پیش‌بینی
449
- chatbot_view, history, msg = predict_new(
450
- text=message,
451
- history=state["history"],
452
- top_p=0.95,
453
- temperature=1,
454
- max_length_tokens=1024,
455
- max_context_length_tokens=200,
456
- user_name=state["user_name"],
457
- user_memory=state["memory"],
458
- user_memory_index=user_memory_index,
459
- service_context=state["service_context"],
460
- api_index=state["api_index"],
461
- semantic_memory_text=state["semantic_memory_text"],
462
- query_category=query_category
463
  )
464
 
465
- new_state = state.copy()
466
- new_state["history"] = history
467
- return chatbot_view, new_state, ""
468
-
469
- def clear_chat(state):
470
- new_state = state.copy()
471
- new_state["history"] = []
472
- return [], new_state, "Cleared."
473
-
474
- def new_session_func(state):
475
- # منطق شروع جلسه جدید (خلاصه کد شما)
476
- new_state = state.copy()
477
- new_state["history"] = []
478
- new_state["new_conversation"] = True
479
- return [], new_state, "New session started."
480
-
481
- def switch_user_func(state):
482
- # ریست کردن استیت
483
- new_state = state.copy()
484
- new_state.update({"history": [], "user_name": None, "initialized": False})
485
- return (
486
- gr.update(visible=True), gr.update(visible=False),
487
- "User switched.", gr.update(value=""), gr.update(), new_state, gr.update(), []
488
- )
489
-
490
- submit_name_btn.click(initialize_session, [username_input, age_input, gender_input, occupation_input, residence_input, state], [username_row, chat_interface, system_msg, active_header, header, state, username_input])
491
- submit_btn.click(respond, [user_input, state], [chatbot, state, system_msg])
492
- user_input.submit(respond, [user_input, state], [chatbot, state, system_msg])
493
- clear_btn.click(clear_chat, [state], [chatbot, state, system_msg])
494
- new_session_btn.click(new_session_func, [state], [chatbot, state, system_msg])
495
- switch_user_btn.click(switch_user_func, [state], [username_row, chat_interface, system_msg, active_header, header, state, username_input, chatbot])
496
 
497
  return demo
498
 
499
- # ==========================================
500
- # 6. اجرای اصلی برنامه
501
- # ==========================================
502
-
503
  def main():
504
- # 1. تنظیم کلیدها برای LlamaIndex
505
  if api_keys:
506
- os.environ["OPENAI_API_KEY"] = api_keys[0] # استفاده از اولین کلید موجود
507
- else:
508
- print("CRITICAL WARNING: No API Keys found. LlamaIndex initialization may fail.")
509
-
510
- # 2. تنظیم مدل زبانی (LLM)
511
- llm = LlamaIndexOpenAI(
512
- model="gpt-4o",
513
- temperature=1,
514
- max_tokens=1024,
515
- top_p=0.95,
516
- api_key=os.environ.get("OPENAI_API_KEY"),
517
- api_base=GAPGPT_BASE_URL,
518
- )
519
-
520
- # 3. تنظیم مدل امبدینگ
521
- embed_model = OpenAIEmbedding(
522
- api_key=os.environ.get("OPENAI_API_KEY"),
523
- api_base=GAPGPT_BASE_URL,
524
- model="text-embedding-3-small"
525
- )
526
-
527
- # 4. تنظیم Prompt Helper
528
- prompt_helper = PromptHelper(
529
- context_window=4096,
530
- num_output=256,
531
- chunk_overlap_ratio=0.1,
532
- tokenizer=tokenizer,
533
- )
534
 
535
- # 5. اعمال تنظیمات سراسری LlamaIndex (نسخه جدید)
536
- Settings.llm = llm
537
- Settings.embed_model = embed_model
538
- Settings.prompt_helper = prompt_helper
539
-
540
- # برای نسخه جدید نیازی به ServiceContext نیست، اما چون توابع شما آن را می‌خواهند None می‌دهیم
541
- service_context = None
 
 
 
 
 
 
 
542
 
543
- # 6. ساخت و اجرای رابط کاربری
544
- # نکته مهم: ما لیست api_keys گلوبال را پاس می‌دهیم تا مشکل خواندن فایل حل شود
545
- demo = create_gradio_interface(service_context, api_keys)
546
 
547
- demo.queue()
548
- demo.launch(
 
549
  server_name="0.0.0.0",
550
  server_port=7860,
551
  ssr_mode=False,
552
- show_api=False # حیاتی برای جلوگیری از خطای Pydantic
 
553
  )
554
 
555
  if __name__ == "__main__":
556
- signal.signal(signal.SIGINT, signal_handler)
557
  main()
 
8
  import logging
9
  import platform
10
 
11
+ # 1. ایمپورت‌ها
12
  import gradio as gr
13
  import nltk
14
  import torch
 
16
 
17
  from openai import OpenAI as OpenAIClient
18
  from llama_index.embeddings.openai import OpenAIEmbedding
 
 
19
  from llama_index.llms.openai import OpenAI as LlamaIndexOpenAI
20
+ from llama_index.core import Settings
 
 
 
 
 
 
 
21
 
22
+ # تنظیمات مسیر
 
 
23
  prompt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../')
24
  sys.path.append(prompt_path)
25
 
26
+ # ایمپورت‌های داخلی رض بر وجود این فایل‌هاست)
27
+ try:
28
+ from utils.sys_args import data_args, model_args
29
+ from utils.app_modules.utils import *
30
+ from utils.app_modules.presets import *
31
+ from utils.app_modules.overwrites import *
32
+ from utils.prompt_utils import *
33
+ from utils.memory_utils import enter_name_llamaindex, save_local_memory
34
+ except ImportError as e:
35
+ print(f"Warning: Local module import failed: {e}")
36
+ # تعریف متغیرهای ضروری برای جلوگیری از کرش اگر ماژول‌ها نبودند
37
+ data_args = {}
38
+ boot_actual_name_dict = {'en': 'Emma'}
39
+ generate_meta_prompt_dict_chatgpt = lambda: {'en': 'You are Emma.'}
40
+ generate_meta_prompt_dict_semantic_chatgpt = lambda: {'en': ''}
41
+ generate_meta_prompt_dict_semantic_episodic_chatgpt = lambda: {'en': ''}
42
+ generate_new_user_meta_prompt_dict_chatgpt = lambda: {'en': ''}
43
 
44
  # ==========================================
45
+ # 2. تنظیمات NLTK
46
  # ==========================================
 
47
  nltk_data_dir = os.path.join(os.getcwd(), "nltk_data")
48
+ os.makedirs(nltk_data_dir, exist_ok=True)
 
49
  nltk.data.path.append(nltk_data_dir)
50
 
51
  def download_nltk_resource(resource_name):
52
  try:
53
  nltk.data.find(f'tokenizers/{resource_name}')
54
  except LookupError:
55
+ try:
56
+ nltk.download(resource_name, download_dir=nltk_data_dir)
57
+ except Exception as e:
58
+ print(f"Failed to download NLTK resource {resource_name}: {e}")
59
 
60
  download_nltk_resource('punkt')
61
  download_nltk_resource('punkt_tab')
62
 
63
+ # ==========================================
64
+ # 3. تنظیمات مدل‌ها و API
65
+ # ==========================================
66
  GAPGPT_BASE_URL = os.getenv("GAPGPT_BASE_URL", "https://api.gapgpt.app/v1")
67
  openai_client_cache = {}
68
 
69
+ # مدل Hugging Face
 
 
70
  import torch.nn.functional as F
71
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
72
 
73
  MODEL_ID = "Keyvan1986/Emma-Classification_Model"
74
  hf_token = os.environ.get("HF_TOKEN")
75
 
76
+ _tokenizer = None
77
+ _classifier_model = None
78
+
79
  try:
80
+ print(f"Loading HF Model: {MODEL_ID}")
81
  _tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=hf_token)
82
  _classifier_model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID, token=hf_token)
83
  _classifier_model.eval()
84
+ print("✅ HF Model loaded.")
85
  except Exception as e:
86
+ print(f"⚠️ Warning: Could not load HF model: {e}")
 
 
87
 
 
 
88
  def get_gapgpt_client(api_key):
89
+ if not api_key: return None
 
90
  if api_key not in openai_client_cache:
91
  openai_client_cache[api_key] = OpenAIClient(api_key=api_key, base_url=GAPGPT_BASE_URL)
92
  return openai_client_cache[api_key]
93
 
94
+ # خواندن کلیدها
 
 
 
 
 
 
 
95
  api_path = "api_key_list.txt"
 
96
  def read_apis(path):
97
+ keys = []
 
98
  env_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("GAPGPT_API_KEY")
99
+ if env_key: keys.append(env_key)
 
 
 
100
  if os.path.exists(path):
101
  try:
102
  with open(path, 'r', encoding='utf8') as f:
103
  for line in f:
104
+ if line.strip(): keys.append(line.strip())
105
+ except: pass
106
+ return keys
107
+
108
+ api_keys = read_apis(api_path)
109
+
110
+ # حافظه
 
111
  memory_dir = os.path.join(os.getcwd(), "memories", "update_memory_0512_eng.json")
112
+ if not os.path.exists(os.path.dirname(memory_dir)):
113
+ os.makedirs(os.path.dirname(memory_dir), exist_ok=True)
 
114
 
115
  if not os.path.exists(memory_dir):
116
  with open(memory_dir, "w", encoding="utf-8") as f:
117
  json.dump({}, f)
118
 
 
119
  with open(memory_dir, "r", encoding="utf-8") as f:
120
  memory = json.load(f)
121
 
122
+ # پرامپت‌ها
123
  language = 'en'
124
+ boot_actual_name = boot_actual_name_dict.get(language, "Emma")
125
+ meta_prompt = generate_meta_prompt_dict_chatgpt().get(language, "")
126
+ meta_prompt_semantic = generate_meta_prompt_dict_semantic_chatgpt().get(language, "")
127
+ meta_prompt_semantic_episodic = generate_meta_prompt_dict_semantic_episodic_chatgpt().get(language, "")
128
+ new_user_meta_prompt = generate_new_user_meta_prompt_dict_chatgpt().get(language, "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  # ==========================================
131
+ # 4. منطق برنامه (Logic) - بدون Type Hint
132
  # ==========================================
133
 
134
  def chatgpt_chat(prompt, system, history, gpt_config, api_index=0):
135
+ if not api_keys: return "Error: No API Key."
136
+
137
+ retry_times = 3
138
+ for _ in range(retry_times):
139
  try:
140
+ messages = [{"role": "system", "content": system}]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  if history:
142
+ for h in history:
143
+ if isinstance(h, dict) and h.get('role') in ['user', 'assistant']:
144
+ messages.append(h)
145
+ messages.append({"role": "user", "content": prompt})
146
+
 
 
 
 
 
 
 
 
 
147
  client = get_gapgpt_client(api_keys[api_index])
148
+ if not client: return "Error: Client init failed."
149
+
150
+ resp = client.chat.completions.create(messages=messages, **gpt_config)
151
+ return resp.choices[0].message.content
152
  except Exception as e:
153
  print(f"Chat Error: {e}")
154
+ api_index = (api_index + 1) % len(api_keys)
155
+ return "Error: Failed to get response."
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  def classify_query_local(text):
158
+ if not _tokenizer or not _classifier_model: return "unknown"
 
 
 
 
 
 
 
 
 
159
  try:
160
+ inputs = _tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
 
 
 
 
 
 
161
  with torch.no_grad():
162
  outputs = _classifier_model(**inputs)
163
+ pid = torch.argmax(F.softmax(outputs.logits, dim=-1), dim=-1).item()
164
+ labels = {0: "episodic_memory", 1: "semantic_memory", 2: "semantic-episodic", 3: "unknown"}
165
+ return labels.get(pid, "unknown")
166
+ except:
 
 
 
 
 
 
 
167
  return "unknown"
168
 
169
+ # نکته مهم: حذف تمام Type Hint ها از آرگومان‌ها برای جلوگیری از کرش Gradio
 
170
  def predict_new(
171
+ text, history, top_p, temperature, max_length_tokens, max_context_length_tokens,
172
+ user_name, user_memory, api_index, semantic_memory_text, query_category
 
 
 
 
 
 
 
 
 
 
 
173
  ):
174
+ if history is None: history = []
175
+
176
+ chat_cfg = {
177
  "model": "gpt-4o",
178
  "temperature": temperature,
179
  "max_tokens": max_length_tokens,
180
  "top_p": top_p,
181
  "frequency_penalty": 0.4,
182
  "presence_penalty": 0.2,
183
+ "n": 1
184
  }
185
 
186
+ # فراخوانی تابع ساخت پرامپت (بدون ServiceContext)
187
+ try:
188
+ # فرض بر این است که این تابع در utils تعریف شده است
189
+ # ما ServiceContext را None پاس می‌دهیم چون در LlamaIndex جدید منسوخ شده
190
+ system_prompt, _ = build_prompt_with_search_memory_llamaindex(
191
+ history=history,
192
+ query=text,
193
+ user_memory=user_memory,
194
+ user_name=user_name,
195
+ user_memory_index=None,
196
+ service_context=None,
197
+ api_keys=api_keys,
198
+ api_index=api_index,
199
+ meta_prompt=meta_prompt,
200
+ new_user_meta_prompt=new_user_meta_prompt,
201
+ data_args=data_args,
202
+ boot_actual_name=boot_actual_name,
203
+ semantic_memory_text=semantic_memory_text,
204
+ query_category=query_category,
205
+ meta_prompt_semantic=meta_prompt_semantic,
206
+ meta_prompt_semantic_episodic=meta_prompt_semantic_episodic
207
+ )
208
+ except Exception as e:
209
+ print(f"Prompt Error: {e}")
210
+ system_prompt = "You are a helpful assistant."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
+ hist_for_llm = history[-10:] if len(history) > 10 else history
213
+
214
+ response = chatgpt_chat(text, system_prompt, hist_for_llm, chat_cfg, api_index)
215
+
216
  new_history = history + [
217
  {"role": "user", "content": text},
218
  {"role": "assistant", "content": response}
219
  ]
220
+
221
  if user_name:
222
+ try:
223
+ save_local_memory(memory, new_history, user_name, data_args)
224
+ except: pass
225
 
226
+ return new_history, new_history
227
 
228
  # ==========================================
229
+ # 5. رابط کاربری (Gradio UI)
230
  # ==========================================
231
 
232
+ def create_ui():
233
+ with gr.Blocks(title="EMMA AI") as demo:
234
+ # استیت اولیه ساده (بدون آبجکت‌های پیچیده)
 
 
 
 
 
 
 
 
 
 
235
  state = gr.State({
236
  "history": [],
237
+ "user_name": None,
238
+ "memory": memory,
239
+ "api_index": 0,
240
+ "semantic": "",
241
+ "init": False
 
 
 
 
242
  })
243
 
244
+ gr.Markdown("### EMMA Assistant")
245
+
246
+ with gr.Column(visible=True) as login_col:
247
+ name_input = gr.Textbox(label="Enter Name")
248
+ start_btn = gr.Button("Start Chat")
249
+
250
+ with gr.Column(visible=False) as chat_col:
251
+ chatbot = gr.Chatbot(height=500, type="messages")
252
+ msg_input = gr.Textbox(show_label=False, placeholder="Type message...")
253
+ send_btn = gr.Button("Send")
254
+
255
+ # توابع Wrapper بدون Type Hint
256
+ def start_session_wrapper(name, current_state):
257
+ if not name: return gr.update(), gr.update(), current_state
258
+
259
+ try:
260
+ enter_name_llamaindex(name, memory, data_args)
261
+ except: pass
262
+
263
+ new_st = current_state.copy()
264
+ new_st["user_name"] = name
265
+ new_st["init"] = True
266
+
267
+ # تلاش برای خواندن حافظه معنایی
268
+ try:
269
+ val = memory.get(name, {}).get("semantic_memory", "")
270
+ new_st["semantic"] = str(val)
271
+ except: pass
272
 
273
+ return gr.update(visible=False), gr.update(visible=True), new_st
 
 
274
 
275
+ def respond_wrapper(msg, current_state):
276
+ if not msg or not current_state["init"]:
277
+ return [], current_state, ""
278
 
279
+ cat = classify_query_local(msg)
 
280
 
281
+ _, new_hist = predict_new(
282
+ text=msg,
283
+ history=current_state["history"],
284
+ top_p=0.9, temperature=0.7, max_length_tokens=512, max_context_length_tokens=200,
285
+ user_name=current_state["user_name"],
286
+ user_memory=current_state["memory"],
287
+ api_index=current_state["api_index"],
288
+ semantic_memory_text=current_state["semantic"],
289
+ query_category=cat
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  )
291
 
292
+ new_st = current_state.copy()
293
+ new_st["history"] = new_hist
294
+ return new_hist, new_st, ""
295
+
296
+ # اتصال رویدادها
297
+ start_btn.click(
298
+ start_session_wrapper,
299
+ inputs=[name_input, state],
300
+ outputs=[login_col, chat_col, state]
301
+ )
302
+
303
+ send_btn.click(
304
+ respond_wrapper,
305
+ inputs=[msg_input, state],
306
+ outputs=[chatbot, state, msg_input]
307
+ )
308
+ msg_input.submit(
309
+ respond_wrapper,
310
+ inputs=[msg_input, state],
311
+ outputs=[chatbot, state, msg_input]
312
+ )
 
 
 
 
 
 
 
 
 
 
313
 
314
  return demo
315
 
 
 
 
 
316
  def main():
 
317
  if api_keys:
318
+ os.environ["OPENAI_API_KEY"] = api_keys[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
+ # تنظیمات سراسری LlamaIndex
321
+ try:
322
+ Settings.llm = LlamaIndexOpenAI(
323
+ model="gpt-4o", temperature=1,
324
+ api_key=os.environ.get("OPENAI_API_KEY"),
325
+ api_base=GAPGPT_BASE_URL
326
+ )
327
+ Settings.embed_model = OpenAIEmbedding(
328
+ api_key=os.environ.get("OPENAI_API_KEY"),
329
+ api_base=GAPGPT_BASE_URL,
330
+ model="text-embedding-3-small"
331
+ )
332
+ except Exception as e:
333
+ print(f"LlamaIndex Settings Error: {e}")
334
 
335
+ demo = create_ui()
 
 
336
 
337
+ print("Starting Gradio...")
338
+ # پارامتر حیاتی: show_api=False
339
+ demo.queue().launch(
340
  server_name="0.0.0.0",
341
  server_port=7860,
342
  ssr_mode=False,
343
+ show_api=False, # جلوگیری از ساخت Schema که باعث خطا می‌شود
344
+ share=False
345
  )
346
 
347
  if __name__ == "__main__":
 
348
  main()