faris1419124 commited on
Commit
fdffd2c
·
1 Parent(s): 29fe8d4
Files changed (3) hide show
  1. README.md +6 -6
  2. app.py +195 -0
  3. requirements.txt +9 -0
README.md CHANGED
@@ -1,13 +1,13 @@
1
  ---
2
- title: Qanon
3
- emoji: 👀
4
- colorFrom: blue
5
- colorTo: blue
6
  sdk: gradio
7
- sdk_version: 6.0.2
8
  app_file: app.py
9
  pinned: false
10
- short_description: كتاب القانون في الطب لابن سينا
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: 'Qanon'
3
+ emoji: 💉
4
+ colorFrom: green
5
+ colorTo: yellow
6
  sdk: gradio
7
+ sdk_version: 5.41.0
8
  app_file: app.py
9
  pinned: false
10
+ short_description: نموذج ذكاء اصطناعي يجيب من كتاب القانون
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from langchain_huggingface import HuggingFaceEmbeddings
4
+ from langchain_core.prompts import PromptTemplate
5
+ from langchain_openai import ChatOpenAI
6
+ from langchain_pinecone import PineconeVectorStore
7
+ from pinecone import Pinecone
8
+ # ===============================
9
+ # Hugging Face Spaces-ready Seerah Q&A (Gradio)
10
+ # ===============================
11
+
12
+ THEME = gr.themes.Soft(
13
+ primary_hue="emerald",
14
+ radius_size=gr.themes.sizes.radius_lg,
15
+ text_size=gr.themes.sizes.text_md,
16
+ )
17
+
18
+ CSS = """
19
+ :root { --card-bg: #ffffff; --card-br: 18px; --card-bd: rgba(15,23,42,.06); }
20
+ body {
21
+ background: radial-gradient(1400px 700px at 60% -20%, rgba(16,185,129,.08), transparent),
22
+ linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%);
23
+ font-feature-settings: "rlig" 1, "calt" 1;
24
+ }
25
+ .app-card {
26
+ max-width: 980px; margin: 36px auto; padding: 22px 24px; background: var(--card-bg);
27
+ border: 1px solid var(--card-bd); border-radius: var(--card-br);
28
+ box-shadow: 0 10px 30px rgba(2,6,23,.08);
29
+ }
30
+ textarea, .prose, .wrap.svelte-vc1pse { direction: rtl; text-align: right; }
31
+ label.svelte- { direction: rtl; }
32
+ textarea { border-radius: 14px !important; }
33
+ button.primary { border-radius: 14px; font-weight: 700; transition: transform .08s ease, box-shadow .2s ease; }
34
+ button.primary:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(16,185,129,.25); }
35
+ #output textarea { min-height: 240px; }
36
+ .brand-chip {
37
+ background: #10b981; color:white; width:44px; height:44px; display:grid; place-items:center;
38
+ border-radius:12px; font-size:22px;
39
+ }
40
+ .footer { color: #475569; text-align: center; font-size: 12px; margin-top: 10px; }
41
+ .footer a { color: #0ea5e9; text-decoration: none; }
42
+ """
43
+
44
+ PIPELINE = {
45
+ "ready": False,
46
+ "error": None,
47
+ "vectorstore": None,
48
+ "llm": None,
49
+ "prompt": None,
50
+ }
51
+
52
+
53
+ def _init_pipeline():
54
+ if PIPELINE["ready"] or PIPELINE["error"]:
55
+ return
56
+
57
+ try:
58
+ openai_api_key = os.getenv("OPENAI_API_KEY", "").strip()
59
+ pinecone_api_key = os.getenv("PINECONE_API_KEY", "").strip()
60
+ index_name = os.getenv("PINECONE_INDEX_NAME", "qanon").strip()
61
+
62
+ if not openai_api_key:
63
+ raise RuntimeError("OPENAI_API_KEY is missing.")
64
+ if not pinecone_api_key:
65
+ raise RuntimeError("PINECONE_API_KEY is missing.")
66
+
67
+ embedding = HuggingFaceEmbeddings(model_name="omarelshehy/Arabic-STS-Matryoshka-V2")
68
+
69
+ pc = Pinecone(api_key=pinecone_api_key)
70
+ if index_name not in [idx["name"] for idx in pc.list_indexes()]:
71
+ raise RuntimeError(f"Pinecone index '{index_name}' not found.")
72
+
73
+ vectorstore = PineconeVectorStore.from_existing_index(
74
+ index_name=index_name,
75
+ embedding=embedding,
76
+ namespace="qanon"
77
+ )
78
+
79
+ llm = ChatOpenAI(model="gpt-4o", temperature=0, openai_api_key=openai_api_key)
80
+
81
+ prompt = PromptTemplate.from_template(
82
+ """
83
+ أنت ذكاءٌ اصطناعي تمثل مؤلف كتاب "القانون في الطب" لابن سينا، وتجيب كما لو كنت الكاتب.
84
+
85
+ القواعد العليا:
86
+ - (إلزامي) أجب فقط من محتوى "القانون في الطب" دون أي إضافة خارجية.
87
+ - ابحث في جميع المقاطع المزوّدة والمؤشّرة برقم الصفحة ورقم الجزء، وأرجع المعلومة الصحيحة حتى لو لم تكن في أول مقطع.
88
+ - اذكر رقم الصفحة ورقم الجزء (Part) لكل معلومة بدقة.
89
+ - إذا كان هناك جواب في السياق فاذكره، وإذا لم يكن فاستنتج.
90
+ - لا تستخدم عبارات اعتذار مثل "عذرًا"، بل استنتج من المقاطع بأسلوب ابن سينا.
91
+
92
+ تنسيق الإجابة:
93
+ - أولًا: رأي ابن سينا المستنتج أو شرحه بأسلوبه.
94
+ - ثانيًا: الاقتباس الحرفي من النص بين علامتي تنصيص.
95
+ - بين كل اقتباس وآخر فاصلة, وكل اقتباس بين علامتي تنصيص
96
+ - ثالثًا: المرجع (الجزء، الصفحة).
97
+ - اذكر أرقام الصفحات والأجزاء بالعربية وبدون فواصل
98
+
99
+ السؤال:
100
+ {question}
101
+
102
+ مقاطع من الكتاب المؤشّرة برقم الصفحة ورقم الجزء:
103
+ {context}
104
+ """
105
+ )
106
+
107
+ PIPELINE.update({
108
+ "ready": True,
109
+ "vectorstore": vectorstore,
110
+ "llm": llm,
111
+ "prompt": prompt,
112
+ })
113
+ except Exception as e:
114
+ PIPELINE["error"] = str(e)
115
+
116
+
117
+ def answer(user_input: str):
118
+ question = (user_input or "").strip()
119
+ question = question.replace("أ", "ا").replace("إ", "ا").replace("آ", "ا").replace("ة", "ه")
120
+
121
+ _init_pipeline()
122
+ if PIPELINE["error"]:
123
+ return f"⚠️ لا يمكن تشغيل الخدمة الآن. السبب: {PIPELINE['error']}"
124
+
125
+ if not PIPELINE["ready"]:
126
+ return "⏳ جارٍ التهيئة… أعد المحاولة بعد لحظات."
127
+
128
+ vectorstore = PIPELINE["vectorstore"]
129
+ llm = PIPELINE["llm"]
130
+ prompt = PIPELINE["prompt"]
131
+
132
+ try:
133
+ docs = vectorstore.similarity_search(question, k=10, namespace="qanon")
134
+ except Exception as e:
135
+ return f"⚠️ خطأ أثناء الاسترجاع من Pinecone: {e}"
136
+
137
+ if not docs:
138
+ return "❌ لم يتم العثور على مقاطع مطابقة في الفهرس."
139
+
140
+ # ✅ تنظيم النصوص مع فاصل واضح بين المقاطع
141
+ context = "\n——\n".join([
142
+ f"[صفحة {d.metadata.get('Page','?')}] [جزء {d.metadata.get('part','?')}]\n{d.page_content.strip()}"
143
+ for d in docs
144
+ ])
145
+
146
+ try:
147
+ filled = prompt.format(question=question, context=context)
148
+ result = llm.invoke(filled)
149
+ return result.content
150
+ except Exception as e:
151
+ return f"⚠️ خطأ أثناء توليد الإجابة: {e}"
152
+
153
+
154
+ with gr.Blocks(theme=THEME, css=CSS, title="نموذج ذكاء اصطناعي يجيب من كتاب القانون") as interface:
155
+ gr.HTML(
156
+ """
157
+ <div class='app-card'>
158
+ <div style='display:flex; align-items:center; gap:14px; margin-bottom: 8px;'>
159
+ <div class='brand-chip'>📗</div>
160
+ <div>
161
+ <h1 style='margin:0; color:#0f172a; font-size:26px;'>سؤال وجواب من كتاب القانون</h1>
162
+ <p style='margin:4px 0 0; color:#334155;'>اكتب استفسارك وسيجيب الذكاء الاصطناعي اعتمادًا على نصوص الكتاب.</p>
163
+ </div>
164
+ </div>
165
+ <div style='margin-top:10px;'>
166
+ <a href='https://shamela.ws/book/10706/3' target='_blank' style='color:#0ea5e9;'>رابط الكتاب</a>
167
+ </div>
168
+ </div>
169
+ """
170
+ )
171
+
172
+ with gr.Group():
173
+ with gr.Row():
174
+ gr.Markdown("#### ✍️ اكتب سؤالك ثم اضغط **إرسال**")
175
+ with gr.Row(equal_height=True):
176
+ with gr.Column(scale=3):
177
+ out = gr.Textbox(label="الجواب هنا", rtl=True, elem_id="output")
178
+ with gr.Column(scale=3):
179
+ inp = gr.Textbox(label="اكتب سؤالك هنا", lines=6, rtl=True, autofocus=True)
180
+ with gr.Row():
181
+ send = gr.Button("إرسال", variant="primary", elem_classes=["primary"], scale=1)
182
+ clear = gr.ClearButton(components=[inp], value="✧ امسح")
183
+
184
+ gr.HTML("""
185
+ <div class='footer'>
186
+ مبنيّ باستخدام Gradio — تصميم واجهة حديثة مع دعم <b>RTL</b>.
187
+ </div>
188
+ """)
189
+
190
+ send.click(fn=answer, inputs=inp, outputs=out)
191
+ inp.submit(fn=answer, inputs=inp, outputs=out)
192
+
193
+
194
+ if __name__ == "__main__":
195
+ interface.queue().launch()
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ langchain
3
+ langchain-community
4
+ langchain-huggingface
5
+ transformers
6
+ sentence-transformers
7
+ langchain-pinecone
8
+ langchain-openai
9
+ pinecone-client