1yahoo commited on
Commit
8a3b8db
·
verified ·
1 Parent(s): fd613c2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -72
app.py CHANGED
@@ -1,76 +1,85 @@
1
  import os
2
  import uuid
3
  import gradio as gr
4
- import chromadb
 
5
  from openai import OpenAI
6
- from chromadb.utils import embedding_functions
7
- from pypdf import PdfReader
8
- from docx import Document
9
 
10
- # --- 1. إعداد الذاكرة السيبرانية (ChromaDB) ---
11
- CHROMA_DATA_PATH = "./neural_memory_v2"
12
- chroma_client = chromadb.PersistentClient(path=CHROMA_DATA_PATH)
13
- ef = embedding_functions.DefaultEmbeddingFunction()
14
- collection = chroma_client.get_or_create_collection(name="yousef_vault", embedding_function=ef)
15
 
16
- DEFAULT_MODEL = "Meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- # --- 2. دالة استخراج النصوص ---
19
- def process_document(file):
20
- if file is None: return "⚠️ لم يتم اختيار ملف."
21
- text = ""
22
  try:
23
- file_ext = os.path.splitext(file.name)[1].lower()
24
- if file_ext == ".pdf":
25
- reader = PdfReader(file.name)
26
- for page in reader.pages:
27
- text += (page.extract_text() or "") + "\n"
28
- elif file_ext == ".docx":
29
- doc = Document(file.name)
30
- text = "\n".join([para.text for para in doc.paragraphs])
31
- elif file_ext in [".txt", ".md"]:
32
- with open(file.name, "r", encoding="utf-8", errors="ignore") as f:
33
- text = f.read()
34
 
35
- if not text.strip():
36
- return "❌ تعذر استخراج نص."
 
 
 
 
37
 
38
- chunks = [text[i:i+2000] for i in range(0, len(text), 2000)]
39
- ids = [str(uuid.uuid4()) for _ in chunks]
40
- collection.add(documents=chunks, ids=ids)
 
 
41
 
42
- return f"✅ تمت الأرشفة: {len(chunks)} شظية معرفية."
43
- except Exception as e:
44
- return f" خطأ: {str(e)}"
45
 
46
- # --- 3. دالة الحوار المصححة ---
47
- def predict(message, history, system_prompt, temperature, custom_model):
48
- # ملاحظة: تم إزالة oauth_token من هنا لتبسيط الاتصال بالـ API
49
- # تأكد من ��ضع HF_TOKEN في Secrets إذا كنت ترفعه على Space
50
- api_key = os.getenv("HF_TOKEN")
51
 
52
- if not api_key:
53
- yield "⚠️ يرجى ضبط HF_TOKEN في الإعدادات."
54
  return
55
 
56
- client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=api_key)
57
- active_model = custom_model.strip() if custom_model and custom_model.strip() else DEFAULT_MODEL
 
 
58
 
59
- # RAG: استرجاع السياق
60
- results = collection.query(query_texts=[message], n_results=5)
61
- context = "\n".join(results['documents'][0]) if results['documents'] else ""
 
62
 
63
- messages = [{"role": "system", "content": f"{system_prompt}\n\n[CONTEXT]:\n{context}"}]
64
- # تحويل التاريخ إلى صيغة OpenAI المتوافقة
65
  for msg in history:
66
  messages.append({"role": msg["role"], "content": msg["content"]})
 
67
  messages.append({"role": "user", "content": message})
68
 
69
  try:
70
  response = client.chat.completions.create(
71
  model=active_model,
72
  messages=messages,
73
- temperature=float(temperature),
74
  stream=True
75
  )
76
 
@@ -80,38 +89,31 @@ def predict(message, history, system_prompt, temperature, custom_model):
80
  partial_message += chunk.choices[0].delta.content
81
  yield partial_message
82
  except Exception as e:
83
- yield f"❌ فشل: {str(e)}"
84
 
85
- # --- 4. بناء الواجهة (التحديث الجديد) ---
86
  with gr.Blocks(fill_height=True) as demo:
87
  with gr.Sidebar():
88
- gr.Markdown("# 🧬 دهليز يوسف")
 
89
 
90
- with gr.Accordion("⚙️ الضبط", open=True):
91
- model_input = gr.Textbox(label="المحرك المستهدف", placeholder="Default Qwen", value="")
92
- system_input = gr.Textbox(value="أنت رفيق حكيم.", label="البرومبت النظامي")
93
- temp_slider = gr.Slider(0.1, 1.5, 0.8, label="Temperature")
94
-
95
- gr.Markdown("---")
96
- file_box = gr.File(label="ارفع مسوداتك", file_types=[".pdf", ".docx", ".txt"])
97
- status_msg = gr.Markdown("*الذاكرة بانتظار الملفات...*")
98
-
99
- file_box.change(process_document, inputs=file_box, outputs=status_msg)
100
 
101
- clear_btn = gr.Button("🗑 مسح الذاكرة")
102
- def clear_memory():
103
- all_data = collection.get()
104
- if all_data['ids']: collection.delete(ids=all_data['ids'])
105
- return "🗑 تم تصفير الذاكرة."
106
- clear_btn.click(clear_memory, outputs=status_msg)
107
 
108
- # التعديل هنا: حذف type="messages" لأنه أصبح افتراضياً أو غير مطلوب بهذا الشكل
109
  gr.ChatInterface(
110
  predict,
111
- additional_inputs=[system_input, temp_slider, model_input],
112
- fill_height=True
 
 
113
  )
114
 
115
  if __name__ == "__main__":
116
- # تم نقل theme إلى هنا حسب تحذير Gradio 6
117
- demo.launch(theme=gr.themes.Soft(primary_hue="indigo"))
 
1
  import os
2
  import uuid
3
  import gradio as gr
4
+ from supabase import create_client, Client
5
+ from sentence_transformers import SentenceTransformer
6
  from openai import OpenAI
 
 
 
7
 
8
+ # --- 1. الإعدادات ---
9
+ URL = os.environ.get("SUPABASE_URL")
10
+ KEY = os.environ.get("SUPABASE_KEY")
11
+ supabase: Client = create_client(URL, KEY)
 
12
 
13
+ model_st = SentenceTransformer('all-MiniLM-L6-v2')
14
+ DEFAULT_MODEL = "MiniMaxAI/MiniMax-M2.1"
15
+ DEFAULT_SYSTEM = """أنت محرك رواية تفاعلية 'لورد الغوامض' متطور وخبير في ألعاب الأدوار (RPG). مهمتك هي سرد قصة حية وتفاعلية بناءً على قراراتي.
16
+ 1. أسلوب السرد:
17
+ استخدم أسلوب السرد الوصفي العميق (Show, Don't Tell). ركز على الحواس: الروائح، الأصوات، برودة الهواء، وتعبيرات الوجوه.
18
+ حافظ على نبرة سوداوية مليئة بالرعب والخيال الملحمي.
19
+ اكتب بضمير المخاطب "أنت".
20
+ 2. القواعد التفاعلية:
21
+ لا تتحدث أبداً بلسان شخصيتي. انتظر قراراتي وردود أفعالي.
22
+ انهِ كل رد بوصف للموقف الحالي الذي يواجهني، متبوعاً بسؤال ضمني أو خيارات مفتوحة.
23
+ اجعل العالم يستجيب لقراراتي؛ إذا فشلت في مهمة، يجب أن تكون هناك عواقب دائمة في القصة.
24
+ 3. إدارة العالم والشخصيات:
25
+ احتفظ بـ "سجل حي" في ذاكرتك لكل الشخصيات الجانبية التي أقابلها ومواقعها وعلاقتها بي.
26
+ التزم بالمنطق الداخلي للعالم (السحر له ثمن، وفقدان العقل خطر دائم).
27
+ 4. تعليمات السياق الضخم:
28
+ تذكر التفاصيل الدقيقة واستخدمها كعناصر مفاجئة لاحقاً. راقب التطور النفسي لشخصيتي.
29
+ تعليمات البداية:
30
+ اطرح سؤالاً على المستخدم هل يريد البدء كـ 'كلاين' أم كـ 'شخصية' جديدة، وإن اختار شخصية جديدة وضح له أنه سيبدأ قبل 10 أيام من انتقال كلاين إلى هذا العالم."""
31
 
32
+ # --- 2. محرك جلب البيانات ---
33
+ def get_brain_context(query, selected_volume):
 
 
34
  try:
35
+ vol_res = supabase.table("novel_vault_v3").select("metadata->>text").eq("metadata->>source", selected_volume).execute()
36
+ full_vol_text = " ".join([d['text'] for d in vol_res.data]) if vol_res.data else ""
 
 
 
 
 
 
 
 
 
37
 
38
+ query_vec = model_st.encode(query).tolist()
39
+ search_res = supabase.rpc('match_documents', {
40
+ 'query_embedding': query_vec,
41
+ 'match_threshold': 0.25,
42
+ 'match_count': 10
43
+ }).execute()
44
 
45
+ other_ctx = ""
46
+ if search_res.data:
47
+ for item in search_res.data:
48
+ if item['metadata'].get('source') != selected_volume:
49
+ other_ctx += f"\n[سياق خارجي]: {item['content']}\n"
50
 
51
+ return full_vol_text, other_ctx
52
+ except:
53
+ return "", ""
54
 
55
+ # --- 3. دالة التنبؤ (تستخدم توكن المستخدم تلقائياً) ---
56
+ def predict(message, history, system_prompt, model_id, selected_volume, request: gr.Request):
57
+ # هنا يتم جلب التوكن من حساب المستخدم المسجل في HF تلقائياً
58
+ token = request.auth_token if request else None
 
59
 
60
+ if not token:
61
+ yield "⚠️ يرجى تسجيل الدخول عبر زر 'Login with Hugging Face' بالأعلى لاستخدام رصيدك."
62
  return
63
 
64
+ client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=token)
65
+ active_model = model_id.strip() if model_id.strip() else DEFAULT_MODEL
66
+
67
+ vol_text, other_text = get_brain_context(message, selected_volume)
68
 
69
+ # حقن السياق والمجلد الكامل
70
+ context_injection = f"\n\n[FULL VOLUME CONTEXT]:\n{vol_text[:150000]}\n\n[RAG SEARCH]:\n{other_text}"
71
+
72
+ messages = [{"role": "system", "content": f"{system_prompt}{context_injection}"}]
73
 
 
 
74
  for msg in history:
75
  messages.append({"role": msg["role"], "content": msg["content"]})
76
+
77
  messages.append({"role": "user", "content": message})
78
 
79
  try:
80
  response = client.chat.completions.create(
81
  model=active_model,
82
  messages=messages,
 
83
  stream=True
84
  )
85
 
 
89
  partial_message += chunk.choices[0].delta.content
90
  yield partial_message
91
  except Exception as e:
92
+ yield f"❌ خطأ في الاتصال: {str(e)}"
93
 
94
+ # --- 4. واجهة Gradio ---
95
  with gr.Blocks(fill_height=True) as demo:
96
  with gr.Sidebar():
97
+ gr.Markdown("# 🕸️ محرك لورد الغوامض التفاعلي")
98
+ gr.LoginButton() # زر الدخول لتمرير التوكن
99
 
100
+ vol_list = gr.Dropdown(
101
+ choices=["Volume1-Clown.pdf", "Volume2-Faceless.pdf", "Volume3-Traveler.pdf", "Volume4-Undying.pdf"],
102
+ label="📖 المجلد النشط",
103
+ value="Volume1-Clown.pdf"
104
+ )
 
 
 
 
 
105
 
106
+ with gr.Accordion("⚙️ الإعدادات", open=False):
107
+ model_input = gr.Textbox(label="Model ID", value=DEFAULT_MODEL)
108
+ system_input = gr.TextArea(label="System Prompt", value=DEFAULT_SYSTEM, lines=15)
 
 
 
109
 
 
110
  gr.ChatInterface(
111
  predict,
112
+ additional_inputs=[system_input, model_input, vol_list],
113
+ fill_height=True,
114
+ type="messages",
115
+ examples=[["ابدأ اللعبة"], ["من أنا؟"]]
116
  )
117
 
118
  if __name__ == "__main__":
119
+ demo.launch()