YoussefYBrix commited on
Commit
da233ff
·
verified ·
1 Parent(s): 47e07e8

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +235 -61
src/streamlit_app.py CHANGED
@@ -2,99 +2,273 @@ import streamlit as st
2
  import os
3
  import requests
4
  import hmac
5
- import re
6
 
7
  # ──────────────────────────────────────────────
8
- # 1. إعدادات الصفحة والستايل
9
  # ──────────────────────────────────────────────
10
- st.set_page_config(page_title="شركة يوسف علاء 🤖", page_icon="🏢", layout="wide")
 
 
 
 
 
11
 
 
 
 
12
  st.markdown("""
13
  <style>
14
- [data-testid="stChatMessageContent"] { font-size: 16px; direction: rtl; text-align: right; }
15
- section[data-testid="stSidebar"] { background: #1a1a2e; color: white; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  </style>
17
  """, unsafe_allow_html=True)
18
 
19
  # ──────────────────────────────────────────────
20
- # 2. سحب المفاتيح والروابط (التعديل المهم)
21
  # ──────────────────────────────────────────────
22
- HF_TOKEN = os.getenv("HF_TOKEN", "")
23
- JINA_KEY = os.getenv("JINA_KEY", "")
24
- PASSWORD = "132733yy"
25
-
26
- # ده الرابط اللي هيحل مشكلة الـ 404 والـ 410 اللي في الصور
27
- HF_API_URL = "https://api-inference.huggingface.co/models/meta-llama/Meta-Llama-3-8B-Instruct"
28
 
29
  # ──────────────────────────────────────────────
30
- # 3. نظام الدخول
31
  # ──────────────────────────────────────────────
32
- if "authenticated" not in st.session_state:
33
- st.session_state["authenticated"] = False
 
 
34
 
35
- if not st.session_state["authenticated"]:
36
- st.title("🔐 مقر الشركة - يوسف علاء")
37
- pwd = st.text_input("ادخل الباسورد السري:", type="password")
38
  if st.button("دخول"):
39
- if pwd == PASSWORD:
40
  st.session_state["authenticated"] = True
41
  st.rerun()
42
  else:
43
- st.error("الباسورد غلط!")
 
 
 
44
  st.stop()
45
 
46
  # ──────────────────────────────────────────────
47
- # 4. تعريف الموظفين (الـ Agents)
48
  # ──────────────────────────────────────────────
49
  AGENTS = {
50
- "🏆 CEO - المدير": "أنت مدير شركة AI، ترد بالعامية المصرية بذكاء.",
51
- "🔍 Researcher - الباحث": "أنت باحث، تلخص الروابط باستخدام Jina وترد بالعامية.",
52
- "💻 Coder - المبرمج": "أنت مبرمج خبير، تكتب كود وتشرحه بالعامية المصرية.",
53
- "📚 Tutor - الدحيح القناوي": "أنت مدرس يوسف، بتشرح منهج أولى ثانوي بلهجة قناوية ممتعة."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
 
 
 
 
56
  with st.sidebar:
57
- st.title("👥 فريق العمل")
58
- agent_name = st.sidebar.radio("اختار الموظف:", list(AGENTS.keys()))
59
- if st.sidebar.button("🗑️ مسح الشات"):
60
- st.session_state.messages = []
 
 
 
 
 
 
 
 
 
 
 
 
61
  st.rerun()
62
 
 
 
63
  # ──────────────────────────────────────────────
64
- # 5. منطق الشات والـ API
65
  # ──────────────────────────────────────────────
 
 
 
 
 
66
  if "messages" not in st.session_state:
67
- st.session_state.messages = []
68
 
69
- for msg in st.session_state.messages:
70
- with st.chat_message(msg["role"]):
 
71
  st.markdown(msg["content"])
72
 
73
- if prompt := st.chat_input("تأمرني بإيه يا مدير؟"):
74
- st.session_state.messages.append({"role": "user", "content": prompt})
75
- with st.chat_message("user"):
76
- st.markdown(prompt)
77
-
78
- with st.chat_message("assistant"):
79
- headers = {"Authorization": f"Bearer {HF_TOKEN}"}
80
- # استخدمنا هنا الـ Payload التقليدي لضمان التوافق
81
- payload = {
82
- "inputs": f"System: {AGENTS[agent_name]}\nUser: {prompt}\nAssistant:",
83
- "parameters": {"max_new_tokens": 500, "temperature": 0.7}
84
- }
85
-
86
- try:
87
- with st.spinner("✍️ جاري التنفيذ..."):
88
- response = requests.post(HF_API_URL, headers=headers, json=payload, timeout=20)
89
-
90
- if response.status_code == 200:
91
- res_data = response.json()
92
- # استخراج النص بذكاء من الرد
93
- full_text = res_data[0].get('generated_text', "")
94
- answer = full_text.split("Assistant:")[-1].strip() if "Assistant:" in full_text else full_text
95
- st.markdown(answer)
96
- st.session_state.messages.append({"role": "assistant", "content": answer})
97
- else:
98
- st.error(f"❌ السيرفر مش مستجيب (كود: {response.status_code})")
99
- except Exception as e:
100
- st.error(f"🤯 حصلت مشكلة: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import os
3
  import requests
4
  import hmac
 
5
 
6
  # ──────────────────────────────────────────────
7
+ # PAGE CONFIG
8
  # ──────────────────────────────────────────────
9
+ st.set_page_config(
10
+ page_title="AI Agent System 🤖",
11
+ page_icon="🤖",
12
+ layout="wide",
13
+ initial_sidebar_state="expanded",
14
+ )
15
 
16
+ # ──────────────────────────────────────────────
17
+ # CUSTOM CSS – Chat Bubbles (WhatsApp / Telegram style)
18
+ # ──────────────────────────────────────────────
19
  st.markdown("""
20
  <style>
21
+ /* General */
22
+ body { font-family: 'Segoe UI', sans-serif; }
23
+
24
+ /* User bubble */
25
+ [data-testid="stChatMessageContent"]:has(+ div [data-testid="stChatMessageAvatar"] svg) {
26
+ background: #DCF8C6;
27
+ border-radius: 18px 18px 4px 18px;
28
+ padding: 10px 14px;
29
+ max-width: 75%;
30
+ margin-left: auto;
31
+ box-shadow: 0 1px 2px rgba(0,0,0,0.15);
32
+ }
33
+
34
+ /* Assistant bubble */
35
+ div[data-testid="stChatMessage"]:not([class*="user"]) [data-testid="stChatMessageContent"] {
36
+ background: #FFFFFF;
37
+ border-radius: 18px 18px 18px 4px;
38
+ padding: 10px 14px;
39
+ max-width: 75%;
40
+ box-shadow: 0 1px 2px rgba(0,0,0,0.15);
41
+ border: 1px solid #e0e0e0;
42
+ }
43
+
44
+ /* Sidebar styling */
45
+ section[data-testid="stSidebar"] {
46
+ background: linear-gradient(160deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
47
+ color: white;
48
+ }
49
+ section[data-testid="stSidebar"] * { color: white !important; }
50
+ section[data-testid="stSidebar"] .stRadio label {
51
+ font-size: 15px;
52
+ padding: 4px 0;
53
+ }
54
+
55
+ /* Input box */
56
+ .stChatInputContainer textarea {
57
+ border-radius: 25px !important;
58
+ border: 2px solid #0f3460 !important;
59
+ padding: 10px 18px !important;
60
+ font-size: 15px !important;
61
+ }
62
+
63
+ /* Main header */
64
+ h1 { color: #0f3460 !important; }
65
  </style>
66
  """, unsafe_allow_html=True)
67
 
68
  # ──────────────────────────────────────────────
69
+ # SECRETS / ENV VARS
70
  # ──────────────────────────────────────────────
71
+ HF_TOKEN = os.getenv("HF_TOKEN", "")
72
+ JINA_KEY = os.getenv("JINA_KEY", "")
73
+ PASSWORD = "132733yy"
74
+ MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct:cerebras"
75
+ HF_API = "https://router.huggingface.co/v1/chat/completions"
 
76
 
77
  # ──────────────────────────────────────────────
78
+ # LOGIN GATE
79
  # ──────────────────────────────────────────────
80
+ def check_password() -> bool:
81
+ """Returns True only after correct password."""
82
+ if st.session_state.get("authenticated"):
83
+ return True
84
 
85
+ st.markdown("## 🔐 ادخل الباسورد عشان تكمل")
86
+ pwd = st.text_input("الباسورد:", type="password", key="login_input")
 
87
  if st.button("دخول"):
88
+ if hmac.compare_digest(pwd, PASSWORD):
89
  st.session_state["authenticated"] = True
90
  st.rerun()
91
  else:
92
+ st.error("الباسورد غلط! حاول تاني.")
93
+ return False
94
+
95
+ if not check_password():
96
  st.stop()
97
 
98
  # ──────────────────────────────────────────────
99
+ # AGENT DEFINITIONS
100
  # ──────────────────────────────────────────────
101
  AGENTS = {
102
+ "🏆 CEO المدير التنفيذي": {
103
+ "icon": "🏆",
104
+ "prompt": (
105
+ "أنت المدير التنفيذي (CEO) لشركة AI ناجحة. بتتكلم بالعامية المصرية الذكية. "
106
+ "وظيفتك: تحليل المواقف بعيون استراتيجية، تعطي قرارات واضحة وجريئة، "
107
+ "وتوجّه الفريق بحسم وثقة. بتحب تضرب أمثلة من واقع الأعمال. "
108
+ "مبتألفش معلومات أبدًا – لو مش عارف حاجة تقول 'محتاج أتحقق الأول'. "
109
+ "ردودك منطقية، مختصرة، وفيها قيمة حقيقية."
110
+ )
111
+ },
112
+ "🔍 Researcher – الباحث": {
113
+ "icon": "🔍",
114
+ "prompt": (
115
+ "أنت باحث محترف بتتكلم بالعامية المصرية. "
116
+ "وظيفتك تحليل المعلومات والروابط اللي بتتدخل ليك بدقة شديدة. "
117
+ "لو المستخدم كتب رابط (URL) في رسالته، هتقرأه وتحلله وتلخصه. "
118
+ "بتقدم معلومات موثوقة مع مصادرها. لو مش عندك مصدر، صرّح بده بوضوح. "
119
+ "أسلوبك علمي لكن مبسّط ومفهوم للعامة."
120
+ )
121
+ },
122
+ "💻 Coder – المبرمج": {
123
+ "icon": "💻",
124
+ "prompt": (
125
+ "أنت مبرمج خبير بتتكلم بالعامية المصرية مع مصطلحات تقنية إنجليزية. "
126
+ "بتكتب كود نظيف، موثق، وشغّال من أول مرة. "
127
+ "بتشرح كل سطر مهم بتعليق مفيد. لو الكود فيه bug محتمل، تنبّه عليه فورًا. "
128
+ "مبتختارش أسهل طريقة – بتختار الأصح والأقوى. "
129
+ "لو السؤال غامض، اسأل عن البيئة والمطلوب بالتفصيل الأول."
130
+ )
131
+ },
132
+ "📚 Tutor – المعلم": {
133
+ "icon": "📚",
134
+ "prompt": (
135
+ "أنت معلم متميز بتشرح بالعامية المصرية بطريقة ممتعة وسهلة. "
136
+ "بتبدأ من الصفر مع المبتدئين وبتعمّق مع المتقدمين. "
137
+ "بتستخدم أمثلة من الحياة اليومية عشان المفاهيم الصعبة تبقى واضحة. "
138
+ "ما بتكدبش أبدًا ولا بتختلق أمثلة – لو مش متأكد تقول كده صراحة. "
139
+ "في الآخر دايمًا بتسأل سؤال تتأكد إن الطالب فهم."
140
+ )
141
+ },
142
  }
143
 
144
+ # ──────────────────────────────────────────────
145
+ # SIDEBAR – Agent Selector
146
+ # ──────────────────────────────────────────────
147
  with st.sidebar:
148
+ st.markdown("## 🤖 اختار موظفك")
149
+ st.markdown("---")
150
+ agent_name = st.radio(
151
+ "الموظفين المتاحين:",
152
+ list(AGENTS.keys()),
153
+ key="agent_selector"
154
+ )
155
+ st.markdown("---")
156
+ st.markdown(f"**الموظف الحالي:**\n\n{agent_name}")
157
+ st.markdown("---")
158
+ st.markdown("**🔑 حالة الـ Keys:**")
159
+ st.markdown(f"HF_TOKEN: {'✅' if HF_TOKEN else '❌ مش موجود'}")
160
+ st.markdown(f"JINA_KEY: {'✅' if JINA_KEY else '⚠️ اختياري'}")
161
+ st.markdown("---")
162
+ if st.button("🗑️ امسح المحادثة"):
163
+ st.session_state["messages"] = []
164
  st.rerun()
165
 
166
+ agent = AGENTS[agent_name]
167
+
168
  # ──────────────────────────────────────────────
169
+ # MAIN UI
170
  # ──────────────────────────────────────────────
171
+ st.markdown(f"# {agent_name}")
172
+ st.markdown(f"*{agent['prompt'][:80]}...*")
173
+ st.divider()
174
+
175
+ # Init chat history
176
  if "messages" not in st.session_state:
177
+ st.session_state["messages"] = []
178
 
179
+ # Render existing messages
180
+ for msg in st.session_state["messages"]:
181
+ with st.chat_message(msg["role"], avatar="🧑" if msg["role"] == "user" else agent["icon"]):
182
  st.markdown(msg["content"])
183
 
184
+ # ──────────────────────────────────────────────
185
+ # JINA READER – fetch URL content
186
+ # ──────────────────────────────────────────────
187
+ def fetch_url_with_jina(url: str) -> str:
188
+ """Use Jina AI Reader to extract clean text from a URL."""
189
+ try:
190
+ headers = {}
191
+ if JINA_KEY:
192
+ headers["Authorization"] = f"Bearer {JINA_KEY}"
193
+ resp = requests.get(f"https://r.jina.ai/{url}", headers=headers, timeout=15)
194
+ if resp.status_code == 200:
195
+ # Limit to first 3000 chars to avoid token overflow
196
+ return resp.text[:3000]
197
+ return f"[تعذّر قراءة الرابط: HTTP {resp.status_code}]"
198
+ except Exception as e:
199
+ return f"[خطأ في قراءة الرابط: {e}]"
200
+
201
+ def extract_urls(text: str) -> list[str]:
202
+ """Extract URLs from user message."""
203
+ import re
204
+ return re.findall(r'https?://[^\s]+', text)
205
+
206
+ # ──────────────────────────────────────────────
207
+ # LLM CALL – HF Inference API
208
+ # ──────────────────────────────────────────────
209
+ def call_llm(messages: list[dict]) -> str:
210
+ """Call HuggingFace Inference API with chat messages."""
211
+ if not HF_TOKEN:
212
+ return "❌ مفيش HF_TOKEN! حط الـ Token في الـ Secrets عشان أشتغل."
213
+
214
+ headers = {
215
+ "Authorization": f"Bearer {HF_TOKEN}",
216
+ "Content-Type": "application/json",
217
+ }
218
+ payload = {
219
+ "model": MODEL_ID,
220
+ "messages": messages,
221
+ "max_tokens": 1024,
222
+ "temperature": 0.7,
223
+ "stream": False,
224
+ }
225
+ try:
226
+ resp = requests.post(HF_API, headers=headers, json=payload, timeout=60)
227
+ resp.raise_for_status()
228
+ data = resp.json()
229
+ return data["choices"][0]["message"]["content"]
230
+ except requests.exceptions.HTTPError as e:
231
+ if resp.status_code == 503:
232
+ return "⏳ النموذج بيتحمّل دلوقتي، استنّى شوية وجرب تاني!"
233
+ return f"❌ HTTP Error {resp.status_code}: {resp.text[:300]}"
234
+ except Exception as e:
235
+ return f"❌ خطأ غير متوقع: {e}"
236
+
237
+ # ──────────────────────────────────────────────
238
+ # CHAT INPUT & RESPONSE
239
+ # ──────────────────────────────────────────────
240
+ if user_input := st.chat_input(f"اكتب رسالتك لـ {agent_name.split('–')[1].strip()}..."):
241
+
242
+ # ── Enrich message if Researcher + URLs
243
+ enriched_input = user_input
244
+ if "Researcher" in agent_name:
245
+ urls = extract_urls(user_input)
246
+ if urls:
247
+ fetched_parts = []
248
+ for url in urls[:2]: # max 2 URLs per message
249
+ with st.spinner(f"📡 بقرأ الرابط: {url}"):
250
+ content = fetch_url_with_jina(url)
251
+ fetched_parts.append(f"\n\n[محتوى الرابط {url}]:\n{content}")
252
+ enriched_input = user_input + "".join(fetched_parts)
253
+
254
+ # Show user message
255
+ st.session_state["messages"].append({"role": "user", "content": user_input})
256
+ with st.chat_message("user", avatar="🧑"):
257
+ st.markdown(user_input)
258
+
259
+ # Build messages for API
260
+ api_messages = [{"role": "system", "content": agent["prompt"]}]
261
+ # Add history (last 10 turns to avoid token overflow)
262
+ history = st.session_state["messages"][:-1] # exclude last user msg
263
+ for m in history[-10:]:
264
+ api_messages.append({"role": m["role"], "content": m["content"]})
265
+ # Add current (possibly enriched) user message
266
+ api_messages.append({"role": "user", "content": enriched_input})
267
+
268
+ # Call LLM with spinner
269
+ with st.chat_message("assistant", avatar=agent["icon"]):
270
+ with st.spinner("✍️ بفكر..."):
271
+ response = call_llm(api_messages)
272
+ st.markdown(response)
273
+
274
+ st.session_state["messages"].append({"role": "assistant", "content": response})