EGYADMIN commited on
Commit
8da0277
·
verified ·
1 Parent(s): 25ebe71

Update modules/ai_assistant/ai_app.py

Browse files
Files changed (1) hide show
  1. modules/ai_assistant/ai_app.py +212 -1053
modules/ai_assistant/ai_app.py CHANGED
@@ -1,8 +1,8 @@
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
- تطبيق الذكاء الاصطناعي الشامل المعدل
5
- تم تصحيح آلية الوصول إلى مفاتيح API في بيئة هجين فيس
6
  """
7
 
8
  import os
@@ -26,106 +26,27 @@ from io import BytesIO
26
  import tempfile
27
  import PyPDF2
28
  import docx
29
- import arabic_reshaper
30
- from bidi.algorithm import get_display
31
-
32
- # Utility functions for handling Arabic text
33
- def reshape_arabic_text(text):
34
- """Reshape Arabic text for proper display"""
35
- if text and isinstance(text, str):
36
- try:
37
- reshaped_text = arabic_reshaper.reshape(text)
38
- return get_display(reshaped_text)
39
- except Exception as e:
40
- st.error(f"Error reshaping text: {e}")
41
- return text
42
- return text
43
-
44
- def validate_image(file_content, max_size_mb=10, min_width=100, min_height=100):
45
- """
46
- Validates and preprocesses an image before sending to AI API.
47
-
48
- Args:
49
- file_content: The binary content of the image file
50
- max_size_mb: Maximum file size in MB
51
- min_width: Minimum image width
52
- min_height: Minimum image height
53
-
54
- Returns:
55
- Tuple of (is_valid, message, processed_content)
56
- """
57
- try:
58
- # Check if file content exists
59
- if not file_content:
60
- return False, "ملف فارغ", None
61
-
62
- # Check file size
63
- file_size_mb = len(file_content) / (1024 * 1024)
64
- if file_size_mb > max_size_mb:
65
- return False, f"حجم الملف كبير جدًا. الحد الأقصى هو {max_size_mb} ميجابايت", None
66
-
67
- # Open image using PIL
68
- image = Image.open(BytesIO(file_content))
69
-
70
- # Check image dimensions
71
- width, height = image.size
72
- if width < min_width or height < min_height:
73
- return False, f"أبعاد الصورة صغيرة جدًا. الحد الأدنى هو {min_width}x{min_height}", None
74
-
75
- # Convert transparent images to white background
76
- if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info):
77
- # Create a white background image
78
- bg = Image.new('RGB', image.size, (255, 255, 255))
79
- if image.mode == 'P':
80
- image = image.convert('RGBA')
81
- # Paste the image on the background
82
- bg.paste(image, mask=image.split()[3] if image.mode == 'RGBA' else None)
83
- image = bg
84
-
85
- # Resize if image is too large
86
- max_dimension = 2000
87
- if width > max_dimension or height > max_dimension:
88
- if width > height:
89
- new_width = max_dimension
90
- new_height = int(height * (max_dimension / width))
91
- else:
92
- new_height = max_dimension
93
- new_width = int(width * (max_dimension / height))
94
- image = image.resize((new_width, new_height), Image.LANCZOS)
95
-
96
- # Convert to RGB if not already
97
- if image.mode != 'RGB':
98
- image = image.convert('RGB')
99
-
100
- # Save to bytes
101
- img_byte_arr = BytesIO()
102
- image.save(img_byte_arr, format='JPEG', quality=85)
103
- processed_content = img_byte_arr.getvalue()
104
-
105
- return True, "تم التحقق من الصورة بنجاح", processed_content
106
-
107
- except Exception as e:
108
- return False, f"خطأ في معالجة الصورة: {str(e)}", None
109
 
 
110
  def _get_huggingface_secret(secret_name):
111
  """
112
- Get a secret from Hugging Face environment variables
113
 
114
- In Hugging Face environment, secrets are available as environment variables
115
- in the format HF_SECRET_<SECRET_NAME>
116
  """
117
- # Convert secret name to the format used in Hugging Face environment variables
118
  env_var_name = f"HF_SECRET_{secret_name.upper()}"
119
  secret_value = os.environ.get(env_var_name, '')
120
 
121
- # If secret is not found, show a warning (only in development mode)
122
  if not secret_value:
123
- st.warning(f"لم يتم العثور على المفتاح السري: {secret_name}. يرجى التأكد من إضافته في إعدادات Hugging Face.")
124
 
125
  return secret_value
126
 
 
127
  class ClaudeAIService:
128
- """Service for interacting with Claude AI API"""
129
 
130
  def __init__(self, api_key=None):
131
  self.api_key = api_key or _get_huggingface_secret("anthropic")
@@ -138,7 +59,7 @@ class ClaudeAIService:
138
  return self.client is not None
139
 
140
  def get_available_models(self):
141
- """Return available Claude AI models"""
142
  return [
143
  "claude-3-5-sonnet-20240620",
144
  "claude-3-opus-20240229",
@@ -147,7 +68,7 @@ class ClaudeAIService:
147
  ]
148
 
149
  def chat(self, messages, model="claude-3-5-sonnet-20240620", temperature=0.7, max_tokens=4000):
150
- """Send a chat request to Claude AI"""
151
  if not self.is_available():
152
  return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
153
 
@@ -161,1008 +82,246 @@ class ClaudeAIService:
161
  return response.content[0].text
162
  except Exception as e:
163
  return f"حدث خطأ أثناء الاتصال بـ Claude AI: {str(e)}"
164
-
165
- def analyze_document(self, document_text, analysis_type, model="claude-3-5-sonnet-20240620"):
166
- """Analyze document text using Claude AI"""
167
- if not self.is_available():
168
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
169
-
170
- prompt_map = {
171
- "items_extraction": "قم بتحليل المستند التالي واستخراج جميع البنود والمواصفات الفنية. قم بتنظيمها في قائمة منسقة مع الأرقام والتفاصيل.",
172
- "contract_terms": "قم بتحليل العقد التالي واستخراج جميع الشروط التعاقدية الهامة. حدد الالتزامات والحقوق والمواعيد النهائية والغرامات.",
173
- "quantities": "قم بتحليل المستند التالي واستخراج جدول الكميات. قم بتنظيم المعلومات في جدول يتضمن البند والكمية والوحدة والسعر إن وجد.",
174
- "legal_requirements": "قم بتحليل المستند التالي وتحديد جميع المتطلبات القانونية والتنظيمية. اشرح الالتزامات القانونية والمخاطر المحتملة."
175
- }
176
-
177
- prompt = prompt_map.get(analysis_type, "قم بتحليل المستند التالي وتلخيص النقاط الرئيسية.")
178
-
179
- try:
180
- messages = [
181
- {
182
- "role": "user",
183
- "content": f"{prompt}\n\nالمستند:\n{document_text}"
184
- }
185
- ]
186
-
187
- response = self.client.messages.create(
188
- model=model,
189
- max_tokens=4000,
190
- temperature=0.3,
191
- messages=messages
192
- )
193
-
194
- return response.content[0].text
195
- except Exception as e:
196
- return f"حدث خطأ أثناء تحليل المستند: {str(e)}"
197
-
198
- def analyze_image(self, image_content, prompt, model="claude-3-5-sonnet-20240620"):
199
- """Analyze image using Claude AI"""
200
- if not self.is_available():
201
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
202
-
203
- try:
204
- # Validate and preprocess the image
205
- is_valid, message, processed_image = validate_image(image_content)
206
-
207
- if not is_valid:
208
- return f"خطأ في الصورة: {message}"
209
-
210
- # Convert image to base64
211
- file_base64 = base64.b64encode(processed_image).decode('utf-8')
212
-
213
- # Create message with image
214
- messages = [
215
- {
216
- "role": "user",
217
- "content": [
218
- {
219
- "type": "image",
220
- "source": {
221
- "type": "base64",
222
- "media_type": "image/jpeg",
223
- "data": file_base64
224
- }
225
- },
226
- {
227
- "type": "text",
228
- "text": prompt
229
- }
230
- ]
231
- }
232
- ]
233
-
234
- # Send request to Claude
235
- response = self.client.messages.create(
236
- model=model,
237
- max_tokens=4000,
238
- temperature=0.3,
239
- messages=messages
240
- )
241
-
242
- return response.content[0].text
243
- except Exception as e:
244
- return f"حدث خطأ أثناء تحليل الصورة: {str(e)}"
245
-
246
- def analyze_engineering_drawing(self, file_content, file_type, model="claude-3-5-sonnet-20240620"):
247
- """Analyze engineering drawing using Claude AI"""
248
- if not self.is_available():
249
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
250
-
251
- try:
252
- # For image-based drawings, use image analysis
253
- if file_type.lower() in ['png', 'jpg', 'jpeg']:
254
- prompt = "هذه رسمة هندسية. قم بتحليلها وتحديد العناصر الرئيسية والأبعاد والمواصفات الفنية. قدم وصفًا تفصيليًا للرسم الهندسي."
255
- return self.analyze_image(file_content, prompt, model)
256
-
257
- # For DWG/DXF files, we need to extract text and data
258
- # This is a placeholder - actual implementation would require specialized libraries
259
- return "تحليل ملفات DWG/DXF يتطلب مكتبات متخصصة. يرجى تحويل الملف إلى صورة للتحليل."
260
-
261
- except Exception as e:
262
- return f"حدث خطأ أثناء تحليل الرسم الهندسي: {str(e)}"
263
-
264
- def analyze_project_file(self, file_content, file_type, analysis_type, model="claude-3-5-sonnet-20240620"):
265
- """Analyze project management file using Claude AI"""
266
- if not self.is_available():
267
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
268
-
269
- try:
270
- # This is a placeholder - actual implementation would require specialized libraries
271
- prompt = ""
272
- if analysis_type == "schedule":
273
- prompt = "قم بتحليل الجدول الزمني للمشروع وتحديد المراحل الرئيسية والمواعيد النهائية والمسار الحرج."
274
- elif analysis_type == "quantities":
275
- prompt = "قم بتحليل جدول الكميات وتحديد البنود الرئيسية والكميات والتكاليف."
276
-
277
- # For image-based files, use image analysis
278
- if file_type.lower() in ['png', 'jpg', 'jpeg']:
279
- return self.analyze_image(file_content, prompt, model)
280
-
281
- # For other file types, this is a placeholder
282
- return "تحليل ملفات إدارة المشاريع يتطلب مكتبات متخصصة. يرجى تحويل الملف إلى صورة للتحليل."
283
-
284
- except Exception as e:
285
- return f"حدث خطأ أثناء تحليل ملف المشروع: {str(e)}"
286
-
287
- def analyze_risk(self, project_data, model="claude-3-5-sonnet-20240620"):
288
- """Analyze project risks using Claude AI"""
289
- if not self.is_available():
290
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
291
-
292
- try:
293
- prompt = "قم بتحليل بيانات المشروع التالية وتحديد المخاطر المحتملة وتصنيفها حسب الاحتمالية والتأثير. قدم توصيات للتخفيف من هذه المخاطر."
294
-
295
- messages = [
296
- {
297
- "role": "user",
298
- "content": f"{prompt}\n\nبيانات المشروع:\n{json.dumps(project_data, ensure_ascii=False)}"
299
- }
300
- ]
301
-
302
- response = self.client.messages.create(
303
- model=model,
304
- max_tokens=4000,
305
- temperature=0.3,
306
- messages=messages
307
- )
308
-
309
- return response.content[0].text
310
- except Exception as e:
311
- return f"حدث خطأ أثناء تحليل المخاطر: {str(e)}"
312
-
313
- def analyze_cost(self, cost_data, model="claude-3-5-sonnet-20240620"):
314
- """Analyze project costs using Claude AI"""
315
- if not self.is_available():
316
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
317
-
318
- try:
319
- prompt = "قم بتحليل بيانات التكاليف التالية وتقديم تقدير للتكلفة الإجمالية للمشروع. حدد البنود ذات التكلفة العالية واقترح طرقًا لتحسين الكفاءة."
320
-
321
- messages = [
322
- {
323
- "role": "user",
324
- "content": f"{prompt}\n\nبيانات التكاليف:\n{json.dumps(cost_data, ensure_ascii=False)}"
325
- }
326
- ]
327
-
328
- response = self.client.messages.create(
329
- model=model,
330
- max_tokens=4000,
331
- temperature=0.3,
332
- messages=messages
333
- )
334
-
335
- return response.content[0].text
336
- except Exception as e:
337
- return f"حدث خطأ أثناء تحليل التكاليف: {str(e)}"
338
-
339
- def analyze_local_content(self, components, model="claude-3-5-sonnet-20240620"):
340
- """Analyze local content components using Claude AI"""
341
- if not self.is_available():
342
- return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
343
-
344
- try:
345
- prompt = "قم بتحليل مكونات المحتوى المحلي التالية وتقديم توصيات لزيادة نسبة المحتوى المحلي. حدد الفرص المتاحة لتوطين المزيد من المكونات."
346
-
347
- messages = [
348
- {
349
- "role": "user",
350
- "content": f"{prompt}\n\nمكونات المحتوى المحلي:\n{json.dumps(components, ensure_ascii=False)}"
351
- }
352
- ]
353
-
354
- response = self.client.messages.create(
355
- model=model,
356
- max_tokens=4000,
357
- temperature=0.3,
358
- messages=messages
359
- )
360
-
361
- return response.content[0].text
362
- except Exception as e:
363
- return f"حدث خطأ أثناء تحليل المحتوى المحلي: {str(e)}"
364
 
365
- # Main application class
366
- class WahbiAIApp:
 
 
 
 
 
367
  def __init__(self):
368
- # Initialize session state variables
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  if 'selected_model' not in st.session_state:
370
- st.session_state.selected_model = "claude-3-5-sonnet-20240620"
371
-
372
- if 'chat_messages' not in st.session_state:
373
- st.session_state.chat_messages = []
374
-
375
- if 'local_content_components' not in st.session_state:
376
- st.session_state.local_content_components = []
377
 
378
- if 'project_risks' not in st.session_state:
379
- st.session_state.project_risks = []
380
-
381
- # Initialize AI service
382
- self.claude_service = ClaudeAIService()
383
-
384
  def run(self):
385
- st.set_page_config(page_title="Wahbi AI", page_icon="🤖", layout="wide")
386
-
387
- # Sidebar
388
- with st.sidebar:
389
- st.title("Wahbi AI")
390
- st.markdown("---")
391
-
392
- # Model selection
393
- st.subheader("إعدادات الذكاء الاصطناعي")
394
- selected_model = st.selectbox(
395
- "اختر نموذج الذكاء الاصطناعي:",
396
- self.claude_service.get_available_models(),
397
- index=0
398
- )
399
-
400
- if selected_model != st.session_state.selected_model:
401
- st.session_state.selected_model = selected_model
402
-
403
- # API key status
404
- st.markdown("---")
405
- st.subheader("حالة مفتاح API")
406
- api_status = "✅ متوفر" if self.claude_service.is_available() else "❌ غير متوفر"
407
- st.markdown(f"**Claude AI:** {api_status}")
408
-
409
- if not self.claude_service.is_available():
410
- st.warning("""
411
- لم يتم العثور على مفتاح API للذكاء الاصطناعي.
412
- يرجى إضافة المفتاح في إعدادات Hugging Face باسم 'anthropic'.
413
- """)
414
-
415
- st.markdown("---")
416
- st.markdown("© 2024 Wahbi AI")
417
-
418
- # Main content
419
- tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8 = st.tabs([
420
- "لوحة المعلومات",
421
- "تحليل المستندات",
422
  "تحليل العقود",
423
- "تحليل الرسومات الهندسية",
424
- "تحليل الجدول الزمني",
425
- "تحليل التكاليف",
426
  "تحليل المخاطر",
427
- "تحليل المحتوى المحلي"
428
  ])
429
 
430
- with tab1:
431
- self.render_dashboard()
432
-
433
- with tab2:
434
- self.render_document_analysis()
435
-
436
- with tab3:
437
- self.render_contract_analysis()
438
-
439
- with tab4:
440
- self.render_engineering_drawing_analysis()
441
-
442
- with tab5:
443
- self.render_schedule_analysis()
444
-
445
- with tab6:
446
- self.render_cost_analysis()
447
-
448
- with tab7:
449
- self.render_risk_analysis()
450
-
451
- with tab8:
452
- self.render_local_content_analysis()
453
-
454
- def render_dashboard(self):
455
- st.header("لوحة المعلومات")
456
 
457
- # Statistics
458
- col1, col2, col3, col4 = st.columns(4)
459
 
460
- with col1:
461
- st.metric(label="المناقصات المقدمة", value="24")
462
-
463
- with col2:
464
- st.metric(label="المناقصات النشطة", value="12")
465
-
466
- with col3:
467
- st.metric(label="المشاريع قيد التنفيذ", value="8")
468
-
469
- with col4:
470
- st.metric(label="نسبة المحتوى المحلي", value="42%", delta="+5%")
471
 
472
- # Charts
473
- col1, col2 = st.columns(2)
474
 
475
- with col1:
476
- st.subheader("توزيع المناقصات حسب القطاع")
477
- sector_data = {
478
- 'القطاع': ['النفط والغاز', 'البنية التحتية', 'الطاقة', 'المياه', 'النقل'],
479
- 'العدد': [8, 6, 5, 3, 2]
480
- }
481
- df_sector = pd.DataFrame(sector_data)
482
- fig = px.bar(df_sector, x='القطاع', y='العدد', color='القطاع')
483
- st.plotly_chart(fig, use_container_width=True)
484
-
485
- with col2:
486
- st.subheader("حالة المناقصات")
487
- status_data = {
488
- 'الحالة': ['قيد التقييم', 'تم الترسية', 'قيد التنفيذ', 'مكتملة', 'ملغاة'],
489
- 'العدد': [10, 5, 8, 6, 1]
490
- }
491
- df_status = pd.DataFrame(status_data)
492
- fig = px.pie(df_status, values='العدد', names='الحالة')
493
- st.plotly_chart(fig, use_container_width=True)
494
 
495
- # Recent notifications
496
- st.subheader("الإشعارات الأخيرة")
497
- notifications = [
498
- "تم إضافة مناقصة جديدة: توريد معدات لمشروع توسعة محطة معالجة المياه",
499
- "تم ترسية مناقصة: إنشاء خط أنابيب نقل النفط الخام",
500
- "تحديث في متطلبات المحتوى المحلي لمشاريع وزارة الطاقة",
501
- "إشعار بتمديد موعد تقديم العروض لمناقصة إنشاء محطة طاقة شمسية"
502
- ]
503
-
504
- for notification in notifications:
505
- st.info(notification)
506
-
507
- # AI Chat
508
- st.subheader("الدردشة مع الذكاء الاصطناعي")
509
-
510
- # Display chat messages
511
- for message in st.session_state.chat_messages:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  with st.chat_message(message["role"]):
513
- st.write(message["content"])
514
 
515
- # Chat input
516
- if prompt := st.chat_input("اكتب سؤالك هنا..."):
517
- if not self.claude_service.is_available():
518
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
519
- return
520
-
521
- # Add user message to chat
522
- st.session_state.chat_messages.append({"role": "user", "content": prompt})
523
  with st.chat_message("user"):
524
- st.write(prompt)
525
 
526
- # Get AI response
527
  with st.chat_message("assistant"):
528
- with st.spinner("جاري التفكير..."):
529
- messages = []
530
- for msg in st.session_state.chat_messages:
531
- if msg["role"] == "user":
532
- messages.append({"role": "user", "content": msg["content"]})
533
- else:
534
- messages.append({"role": "assistant", "content": msg["content"]})
535
-
536
- response = self.claude_service.chat(
537
- messages=messages,
538
- model=st.session_state.selected_model
539
- )
540
-
541
- st.write(response)
542
-
543
- # Add assistant response to chat
544
- st.session_state.chat_messages.append({"role": "assistant", "content": response})
 
 
 
 
 
545
 
546
- def render_document_analysis(self):
547
- st.header("تحليل المستندات")
548
-
549
- col1, col2 = st.columns([1, 2])
550
-
551
- with col1:
552
- st.subheader("تحميل المستند")
 
 
 
553
 
554
- uploaded_file = st.file_uploader("اختر ملف المستند", type=["pdf", "docx", "txt"])
 
555
 
556
- analysis_type = st.selectbox(
557
- "نوع التحليل",
558
- [
559
- "استخراج البنود والمواصفات",
560
- "تحليل الشروط التعاقدية",
561
- "استخراج جدول الكميات",
562
- "تحديد المتطلبات القانونية"
563
- ]
564
  )
565
 
566
- analysis_type_map = {
567
- "استخراج البنود والمواصفات": "items_extraction",
568
- "تحليل الشروط التعاقدية": "contract_terms",
569
- "استخراج جدول الكميات": "quantities",
570
- "تحديد المتطلبات القانونية": "legal_requirements"
571
- }
572
-
573
- if st.button("تحليل المستند"):
574
- if not self.claude_service.is_available():
575
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
576
- return
577
-
578
- if uploaded_file is None:
579
- st.error("يرجى تحميل ملف أولاً")
580
- return
581
-
582
- with st.spinner("جاري تحليل المستند..."):
583
- # Extract text from document
584
- text = ""
585
-
586
- if uploaded_file.type == "application/pdf":
587
- pdf_reader = PyPDF2.PdfReader(uploaded_file)
588
- for page in pdf_reader.pages:
589
- text += page.extract_text() + "\n"
590
- elif uploaded_file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
591
- doc = docx.Document(uploaded_file)
592
- for para in doc.paragraphs:
593
- text += para.text + "\n"
594
- else: # Assume text file
595
- text = uploaded_file.getvalue().decode("utf-8")
596
-
597
- # Analyze document
598
- analysis_result = self.claude_service.analyze_document(
599
- document_text=text,
600
- analysis_type=analysis_type_map[analysis_type],
601
- model=st.session_state.selected_model
602
- )
603
-
604
- # Store result in session state
605
- st.session_state.document_analysis_result = analysis_result
606
-
607
- with col2:
608
- st.subheader("نتائج التحليل")
609
-
610
- if 'document_analysis_result' in st.session_state:
611
- st.markdown(st.session_state.document_analysis_result)
612
-
613
- # Export options
614
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Word", "Text"])
615
-
616
- if st.button("تصدير النتائج"):
617
- st.success("تم تصدير النتائج بنجاح")
618
- else:
619
- st.info("قم بتحميل مستند وتحليله لعرض النتائج هنا")
620
 
621
- def render_contract_analysis(self):
622
- st.header("تحليل العقود")
623
-
624
- col1, col2 = st.columns([1, 2])
625
-
626
- with col1:
627
- st.subheader("تحميل العقد")
 
 
628
 
629
- uploaded_file = st.file_uploader("اختر ملف العقد", type=["pdf", "docx", "txt"], key="contract_uploader")
 
 
 
 
630
 
631
- analysis_focus = st.multiselect(
632
- "التركيز على",
633
- [
634
- "الالتزامات والمسؤوليات",
635
- "المواعيد النهائية",
636
- "الغرامات والتعويضات",
637
- "شروط الدفع",
638
- "بنود الإنهاء",
639
- "تسوية النزاعات"
640
- ],
641
- default=["الالتزامات والمسؤوليات", "المواعيد النهائية"]
642
  )
643
 
644
- if st.button("تحليل العقد"):
645
- if not self.claude_service.is_available():
646
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
647
- return
648
-
649
- if uploaded_file is None:
650
- st.error("يرجى تحميل ملف أولاً")
651
- return
652
-
653
- with st.spinner("جاري تحليل العقد..."):
654
- # Extract text from document
655
- text = ""
656
-
657
- if uploaded_file.type == "application/pdf":
658
- pdf_reader = PyPDF2.PdfReader(uploaded_file)
659
- for page in pdf_reader.pages:
660
- text += page.extract_text() + "\n"
661
- elif uploaded_file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
662
- doc = docx.Document(uploaded_file)
663
- for para in doc.paragraphs:
664
- text += para.text + "\n"
665
- else: # Assume text file
666
- text = uploaded_file.getvalue().decode("utf-8")
667
-
668
- # Create custom prompt based on analysis focus
669
- focus_str = ", ".join(analysis_focus)
670
-
671
- # Analyze contract
672
- analysis_result = self.claude_service.analyze_document(
673
- document_text=text,
674
- analysis_type="contract_terms",
675
- model=st.session_state.selected_model
676
- )
677
-
678
- # Store result in session state
679
- st.session_state.contract_analysis_result = analysis_result
680
-
681
- with col2:
682
- st.subheader("نتائج التحليل")
683
-
684
- if 'contract_analysis_result' in st.session_state:
685
- st.markdown(st.session_state.contract_analysis_result)
686
-
687
- # Export options
688
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Word", "Text"], key="contract_export_format")
689
-
690
- if st.button("تصدير النتائج", key="contract_export_button"):
691
- st.success("تم تصدير النتائج بنجاح")
692
  else:
693
- st.info("قم بتحميل عقد وتحليله لعرض النتائج هنا")
 
 
 
 
694
 
695
- def render_engineering_drawing_analysis(self):
696
- st.header("تحليل الرسومات الهندسية")
697
-
698
- col1, col2 = st.columns([1, 2])
699
-
700
- with col1:
701
- st.subheader("تحميل الرسم الهندسي")
702
-
703
- uploaded_file = st.file_uploader(
704
- "اختر ملف الرسم الهندسي",
705
- type=["png", "jpg", "jpeg", "dwg", "dxf"],
706
- key="drawing_uploader"
707
- )
708
-
709
- if st.button("تحليل الرسم الهندسي"):
710
- if not self.claude_service.is_available():
711
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
712
- return
713
-
714
- if uploaded_file is None:
715
- st.error("يرجى تحميل ملف أولاً")
716
- return
717
-
718
- with st.spinner("جاري تحليل الرسم الهندسي..."):
719
- file_content = uploaded_file.getvalue()
720
- file_type = uploaded_file.name.split('.')[-1]
721
-
722
- # Analyze engineering drawing
723
- analysis_result = self.claude_service.analyze_engineering_drawing(
724
- file_content=file_content,
725
- file_type=file_type,
726
- model=st.session_state.selected_model
727
- )
728
-
729
- # Store result in session state
730
- st.session_state.drawing_analysis_result = analysis_result
731
-
732
- with col2:
733
- st.subheader("نتائج التحليل")
734
-
735
- if 'drawing_analysis_result' in st.session_state:
736
- st.markdown(st.session_state.drawing_analysis_result)
737
-
738
- # Export options
739
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Word", "Text"], key="drawing_export_format")
740
-
741
- if st.button("تصدير النتائج", key="drawing_export_button"):
742
- st.success("تم تصدير النتائج بنجاح")
743
- else:
744
- st.info("قم بتحميل رسم هندسي وتحليله لعرض النتائج هنا")
745
 
746
- def render_schedule_analysis(self):
747
- st.header("تحليل الجدول الزمني")
748
-
749
- col1, col2 = st.columns([1, 2])
750
-
751
- with col1:
752
- st.subheader("تحميل ملف الجدول الزمني")
753
-
754
- uploaded_file = st.file_uploader(
755
- "اختر ملف الجدول الزمني",
756
- type=["png", "jpg", "jpeg", "mpp", "xer", "p6", "xml"],
757
- key="schedule_uploader"
758
- )
759
-
760
- analysis_type = st.radio(
761
- "نوع التحليل",
762
- ["الجدول الزمني", "جدول الكميات"]
763
- )
764
-
765
- if st.button("تحليل الملف"):
766
- if not self.claude_service.is_available():
767
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
768
- return
769
-
770
- if uploaded_file is None:
771
- st.error("يرجى تحميل ملف أولاً")
772
- return
773
-
774
- with st.spinner("جاري تحليل الملف..."):
775
- file_content = uploaded_file.getvalue()
776
- file_type = uploaded_file.name.split('.')[-1]
777
-
778
- # Analyze project file
779
- analysis_result = self.claude_service.analyze_project_file(
780
- file_content=file_content,
781
- file_type=file_type,
782
- analysis_type="schedule" if analysis_type == "الجدول الزمني" else "quantities",
783
- model=st.session_state.selected_model
784
- )
785
-
786
- # Store result in session state
787
- st.session_state.schedule_analysis_result = analysis_result
788
-
789
- with col2:
790
- st.subheader("نتائج التحليل")
791
-
792
- if 'schedule_analysis_result' in st.session_state:
793
- st.markdown(st.session_state.schedule_analysis_result)
794
-
795
- # Export options
796
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Excel", "Text"], key="schedule_export_format")
797
-
798
- if st.button("تصدير النتائج", key="schedule_export_button"):
799
- st.success("تم تصدير النتائج بنجاح")
800
- else:
801
- st.info("قم بتحميل ملف وتحليله لعرض النتائج هنا")
802
 
803
- def render_cost_analysis(self):
804
- st.header("تحليل التكاليف")
805
-
806
- col1, col2 = st.columns([1, 2])
807
-
808
- with col1:
809
- st.subheader("بيانات التكاليف")
810
-
811
- # Sample cost data input
812
- st.markdown("### إدخال بيانات التكاليف")
813
-
814
- cost_categories = [
815
- "تكاليف المواد",
816
- "تكاليف العمالة",
817
- "تكاليف المعدات",
818
- "تكاليف النقل",
819
- "تكاليف التركيب",
820
- "تكاليف أخرى"
821
- ]
822
-
823
- cost_data = {}
824
-
825
- for category in cost_categories:
826
- cost_data[category] = st.number_input(f"{category} (ريال)", min_value=0, step=1000)
827
-
828
- # Additional project information
829
- st.markdown("### معلومات إضافية")
830
- project_duration = st.slider("مدة المشروع (بالأشهر)", 1, 60, 12)
831
- contingency = st.slider("نسبة الطوارئ (%)", 0, 30, 10)
832
-
833
- if st.button("تحليل التكاليف"):
834
- if not self.claude_service.is_available():
835
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
836
- return
837
-
838
- with st.spinner("جاري تحليل التكاليف..."):
839
- # Prepare cost data for analysis
840
- analysis_data = {
841
- "cost_categories": cost_data,
842
- "project_duration": project_duration,
843
- "contingency": contingency
844
- }
845
-
846
- # Analyze cost data
847
- analysis_result = self.claude_service.analyze_cost(
848
- cost_data=analysis_data,
849
- model=st.session_state.selected_model
850
- )
851
-
852
- # Store result in session state
853
- st.session_state.cost_analysis_result = analysis_result
854
-
855
- with col2:
856
- st.subheader("نتائج التحليل")
857
-
858
- if 'cost_analysis_result' in st.session_state:
859
- st.markdown(st.session_state.cost_analysis_result)
860
-
861
- # Display cost breakdown chart
862
- if sum(cost_data.values()) > 0:
863
- st.subheader("توزيع التكاليف")
864
-
865
- df_costs = pd.DataFrame({
866
- 'الفئة': list(cost_data.keys()),
867
- 'التكلفة': list(cost_data.values())
868
- })
869
-
870
- fig = px.pie(
871
- df_costs,
872
- values='التكلفة',
873
- names='الفئة',
874
- title='توزيع التكاليف حسب الفئة'
875
- )
876
- st.plotly_chart(fig, use_container_width=True)
877
-
878
- # Export options
879
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Excel", "Text"], key="cost_export_format")
880
-
881
- if st.button("تصدير النتائج", key="cost_export_button"):
882
- st.success("تم تصدير النتائج بنجاح")
883
- else:
884
- st.info("قم بإدخال بيانات التكاليف وتحليلها لعرض النتائج هنا")
885
 
886
- def render_risk_analysis(self):
887
- st.header("تحليل المخاطر")
 
888
 
889
- col1, col2 = st.columns([1, 2])
 
890
 
891
- with col1:
892
- st.subheader("إدخال بيانات المخاطر")
893
-
894
- # Risk input form
895
- with st.form("risk_form"):
896
- risk_name = st.text_input("اسم المخاطرة")
897
- risk_category = st.selectbox(
898
- "فئة المخاطرة",
899
- ["تقنية", "مالية", "تشغيلية", "قانونية", "بيئية", "أمنية", "أخرى"]
900
- )
901
- risk_probability = st.slider("احتمالية الحدوث (%)", 0, 100, 50)
902
- risk_impact = st.selectbox(
903
- "مستوى التأثير",
904
- ["منخفض", "متوسط", "عالي", "حرج"]
905
- )
906
- risk_description = st.text_area("وصف المخاطرة")
907
-
908
- submit_button = st.form_submit_button("إضافة المخاطرة")
909
-
910
- if submit_button:
911
- if risk_name and risk_description:
912
- # Add risk to session state
913
- st.session_state.project_risks.append({
914
- "name": risk_name,
915
- "category": risk_category,
916
- "probability": risk_probability,
917
- "impact": risk_impact,
918
- "description": risk_description
919
- })
920
- st.success("تمت إضافة المخاطرة بنجاح")
921
- else:
922
- st.error("يرجى إدخال اسم ووصف المخاطرة")
923
-
924
- # Analyze risks button
925
- if st.button("تحليل المخاطر"):
926
- if not self.claude_service.is_available():
927
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
928
- return
929
-
930
- if not st.session_state.project_risks:
931
- st.error("يرجى إضافة مخاطر للتحليل أولاً")
932
- return
933
-
934
- with st.spinner("جاري تحليل المخاطر..."):
935
- # Analyze risks
936
- analysis_result = self.claude_service.analyze_risk(
937
- project_data={"risks": st.session_state.project_risks},
938
- model=st.session_state.selected_model
939
- )
940
-
941
- # Store result in session state
942
- st.session_state.risk_analysis_result = analysis_result
943
 
944
- with col2:
945
- st.subheader("المخاطر المضافة")
946
-
947
- # Display added risks
948
- if st.session_state.project_risks:
949
- for i, risk in enumerate(st.session_state.project_risks):
950
- with st.expander(f"{risk['name']} ({risk['category']})"):
951
- st.write(f"**الاحتمالية:** {risk['probability']}%")
952
- st.write(f"**التأثير:** {risk['impact']}")
953
- st.write(f"**الوصف:** {risk['description']}")
954
-
955
- if st.button(f"حذف", key=f"delete_risk_{i}"):
956
- st.session_state.project_risks.pop(i)
957
- st.rerun()
958
-
959
- # Risk matrix visualization
960
- st.subheader("مصفوفة المخاطر")
961
-
962
- # Create risk matrix data
963
- impact_map = {"منخفض": 1, "متوسط": 2, "عالي": 3, "حرج": 4}
964
-
965
- risk_data = []
966
- for risk in st.session_state.project_risks:
967
- risk_data.append({
968
- "name": risk["name"],
969
- "probability": risk["probability"] / 100, # Convert to 0-1 scale
970
- "impact": impact_map[risk["impact"]],
971
- "category": risk["category"]
972
- })
973
-
974
- if risk_data:
975
- df_risks = pd.DataFrame(risk_data)
976
-
977
- # Create risk matrix using Plotly
978
- fig = px.scatter(
979
- df_risks,
980
- x="impact",
981
- y="probability",
982
- text="name",
983
- color="category",
984
- size=[10] * len(df_risks),
985
- size_max=15,
986
- hover_name="name",
987
- labels={"impact": "التأثير", "probability": "الاحتمالية"},
988
- title="مصفوفة المخاطر"
989
- )
990
-
991
- # Customize layout
992
- fig.update_layout(
993
- xaxis=dict(
994
- tickmode='array',
995
- tickvals=[1, 2, 3, 4],
996
- ticktext=['منخفض', 'متوسط', 'عالي', 'حرج']
997
- ),
998
- yaxis=dict(
999
- tickformat=".0%"
1000
- )
1001
- )
1002
-
1003
- # Add background colors for risk levels
1004
- fig.add_shape(type="rect", x0=0.5, y0=0.5, x1=2.5, y1=0.3, fillcolor="green", opacity=0.2, line_width=0)
1005
- fig.add_shape(type="rect", x0=0.5, y0=0.3, x1=2.5, y1=0.7, fillcolor="yellow", opacity=0.2, line_width=0)
1006
- fig.add_shape(type="rect", x0=0.5, y0=0.7, x1=2.5, y1=1.0, fillcolor="orange", opacity=0.2, line_width=0)
1007
- fig.add_shape(type="rect", x0=2.5, y0=0.5, x1=4.5, y1=0.3, fillcolor="yellow", opacity=0.2, line_width=0)
1008
- fig.add_shape(type="rect", x0=2.5, y0=0.3, x1=4.5, y1=0.7, fillcolor="orange", opacity=0.2, line_width=0)
1009
- fig.add_shape(type="rect", x0=2.5, y0=0.7, x1=4.5, y1=1.0, fillcolor="red", opacity=0.2, line_width=0)
1010
-
1011
- st.plotly_chart(fig, use_container_width=True)
1012
- else:
1013
- st.info("لم يتم إضافة أي مخاطر بعد")
1014
-
1015
- # Display analysis results
1016
- if 'risk_analysis_result' in st.session_state:
1017
- st.subheader("نتائج تحليل المخاطر")
1018
- st.markdown(st.session_state.risk_analysis_result)
1019
-
1020
- # Export options
1021
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Excel", "Text"], key="risk_export_format")
1022
-
1023
- if st.button("تصدير النتائج", key="risk_export_button"):
1024
- st.success("تم تصدير النتائج بنجاح")
1025
-
1026
- def render_local_content_analysis(self):
1027
- st.header("تحليل المحتوى المحلي")
1028
 
1029
- col1, col2 = st.columns([1, 2])
 
 
1030
 
1031
- with col1:
1032
- st.subheader("إضافة مكونات المحتوى المحلي")
1033
-
1034
- # Component input form
1035
- with st.form("component_form"):
1036
- component_name = st.text_input("اسم المكون")
1037
- component_category = st.selectbox(
1038
- "فئة المكون",
1039
- ["مواد", "معدات", "خدمات", "عمالة", "أخرى"]
1040
- )
1041
- component_value = st.number_input("قيمة المكون (ريال)", min_value=0, step=1000)
1042
- local_percentage = st.slider("نسبة المحتوى المحلي (%)", 0, 100, 50)
1043
- component_description = st.text_area("وصف المكون")
1044
-
1045
- submit_button = st.form_submit_button("إضافة المكون")
1046
-
1047
- if submit_button:
1048
- if component_name and component_value > 0:
1049
- # Add component to session state
1050
- st.session_state.local_content_components.append({
1051
- "name": component_name,
1052
- "category": component_category,
1053
- "value": component_value,
1054
- "local_percentage": local_percentage,
1055
- "description": component_description
1056
- })
1057
- st.success("تمت إضافة المكون بنجاح")
1058
- else:
1059
- st.error("يرجى إدخال اسم وقيمة للمكون")
1060
-
1061
- # Analyze local content button
1062
- if st.button("تحليل المحتوى المحلي"):
1063
- if not self.claude_service.is_available():
1064
- st.error("لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face.")
1065
- return
1066
-
1067
- if not st.session_state.local_content_components:
1068
- st.error("يرجى إضافة مكونات للتحليل أولاً")
1069
- return
1070
-
1071
- with st.spinner("جاري تحليل المحتوى المحلي..."):
1072
- # Analyze local content
1073
- analysis_result = self.claude_service.analyze_local_content(
1074
- components=st.session_state.local_content_components,
1075
- model=st.session_state.selected_model
1076
- )
1077
-
1078
- # Store result in session state
1079
- st.session_state.local_content_analysis_result = analysis_result
1080
 
1081
- with col2:
1082
- st.subheader("المكونات المضافة")
1083
-
1084
- # Display added components
1085
- if st.session_state.local_content_components:
1086
- for i, component in enumerate(st.session_state.local_content_components):
1087
- with st.expander(f"{component['name']} ({component['category']})"):
1088
- st.write(f"**القيمة:** {component['value']} ريال")
1089
- st.write(f"**نسبة المحتوى المحلي:** {component['local_percentage']}%")
1090
- st.write(f"**الوصف:** {component['description']}")
1091
-
1092
- if st.button(f"حذف", key=f"delete_component_{i}"):
1093
- st.session_state.local_content_components.pop(i)
1094
- st.rerun()
1095
-
1096
- # Calculate total local content
1097
- total_value = sum(c["value"] for c in st.session_state.local_content_components)
1098
- weighted_local_content = sum(c["value"] * c["local_percentage"] / 100 for c in st.session_state.local_content_components)
1099
- overall_local_percentage = (weighted_local_content / total_value * 100) if total_value > 0 else 0
1100
-
1101
- # Display summary
1102
- st.subheader("ملخص المحتوى المحلي")
1103
- col1, col2, col3 = st.columns(3)
1104
-
1105
- with col1:
1106
- st.metric("إجمالي القيمة", f"{total_value:,.0f} ريال")
1107
-
1108
- with col2:
1109
- st.metric("قيمة المحتوى المحلي", f"{weighted_local_content:,.0f} ريال")
1110
-
1111
- with col3:
1112
- st.metric("نسبة المحتوى المحلي", f"{overall_local_percentage:.1f}%")
1113
-
1114
- # Visualizations
1115
- st.subheader("توزيع المكونات")
1116
-
1117
- # Prepare data for charts
1118
- component_data = []
1119
- for component in st.session_state.local_content_components:
1120
- component_data.append({
1121
- "name": component["name"],
1122
- "category": component["category"],
1123
- "value": component["value"],
1124
- "local_value": component["value"] * component["local_percentage"] / 100,
1125
- "local_percentage": component["local_percentage"]
1126
- })
1127
-
1128
- df_components = pd.DataFrame(component_data)
1129
-
1130
- # Component value by category
1131
- fig1 = px.bar(
1132
- df_components,
1133
- x="category",
1134
- y="value",
1135
- color="name",
1136
- title="قيمة المكونات حسب الفئة",
1137
- labels={"category": "الفئة", "value": "القيمة (ريال)", "name": "المكون"}
1138
- )
1139
- st.plotly_chart(fig1, use_container_width=True)
1140
-
1141
- # Local content percentage by component
1142
- fig2 = px.bar(
1143
- df_components,
1144
- x="name",
1145
- y="local_percentage",
1146
- color="category",
1147
- title="نسبة المحتوى المحلي حسب المكون",
1148
- labels={"name": "المكون", "local_percentage": "نسبة المحتوى المحلي (%)", "category": "الفئة"}
1149
- )
1150
- st.plotly_chart(fig2, use_container_width=True)
1151
- else:
1152
- st.info("لم يتم إضافة أي مكونات بعد")
1153
-
1154
- # Display analysis results
1155
- if 'local_content_analysis_result' in st.session_state:
1156
- st.subheader("نتائج تحليل المحتوى المحلي")
1157
- st.markdown(st.session_state.local_content_analysis_result)
1158
-
1159
- # Export options
1160
- export_format = st.selectbox("تنسيق التصدير", ["PDF", "Excel", "Text"], key="local_content_export_format")
1161
-
1162
- if st.button("تصدير النتائج", key="local_content_export_button"):
1163
- st.success("تم تصدير النتائج بنجاح")
1164
 
1165
- # Run the application
1166
  if __name__ == "__main__":
1167
- app = WahbiAIApp()
1168
  app.run()
 
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
+ وحدة مساعد الذكاء الاصطناعي
5
+ تقوم بتوفير فئة AIAssistantApp للتوافق مع التطبيق الرئيسي
6
  """
7
 
8
  import os
 
26
  import tempfile
27
  import PyPDF2
28
  import docx
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ # تعريف دالة الحصول على الأسرار من هجين فيس
31
  def _get_huggingface_secret(secret_name):
32
  """
33
+ الحصول على قيمة السر من أسرار هجين فيس
34
 
35
+ في بيئة هجين فيس، الأسرار متاحة كمتغيرات بيئة بالتنسيق HF_SECRET_<SECRET_NAME>
 
36
  """
37
+ # تحويل اسم السر إلى الصيغة المستخدمة في متغيرات البيئة في هجين فيس
38
  env_var_name = f"HF_SECRET_{secret_name.upper()}"
39
  secret_value = os.environ.get(env_var_name, '')
40
 
41
+ # إذا لم يتم العثور على السر، نعرض رسالة تحذير (في وضع التطوير فقط)
42
  if not secret_value:
43
+ st.warning(f"لم يتم العثور على السر: {secret_name}. تأكد من إضافته في إعدادات الأسرار في هجين فيس.")
44
 
45
  return secret_value
46
 
47
+ # تعريف فئة خدمة Claude AI
48
  class ClaudeAIService:
49
+ """خدمة التفاعل مع Claude AI API"""
50
 
51
  def __init__(self, api_key=None):
52
  self.api_key = api_key or _get_huggingface_secret("anthropic")
 
59
  return self.client is not None
60
 
61
  def get_available_models(self):
62
+ """إرجاع نماذج Claude AI المتاحة"""
63
  return [
64
  "claude-3-5-sonnet-20240620",
65
  "claude-3-opus-20240229",
 
68
  ]
69
 
70
  def chat(self, messages, model="claude-3-5-sonnet-20240620", temperature=0.7, max_tokens=4000):
71
+ """إرسال طلب محادثة إلى Claude AI"""
72
  if not self.is_available():
73
  return "لم يتم تكوين مفتاح API للذكاء الاصطناعي. يرجى إضافة المفتاح في إعدادات Hugging Face."
74
 
 
82
  return response.content[0].text
83
  except Exception as e:
84
  return f"حدث خطأ أثناء الاتصال بـ Claude AI: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ # فئة AIAssistantApp الرئيسية
87
+ class AIAssistantApp:
88
+ """
89
+ فئة مساعد الذكاء الاصطناعي
90
+ توفر واجهة للتفاعل مع خدمات الذكاء الاصطناعي
91
+ """
92
+
93
  def __init__(self):
94
+ """تهيئة تطبيق مساعد الذكاء الاصطناعي"""
95
+ self.uploaded_files = {}
96
+ self.analysis_results = {}
97
+
98
+ # تهيئة مفاتيح API لنماذج هجين فيس
99
+ # تحميل المفاتيح من أسرار هجين فيس (Hugging Face Secrets)
100
+ if 'ai_api_key' not in st.session_state:
101
+ # الحصول على المفتاح من أسرار هجين فيس
102
+ ai_key = _get_huggingface_secret('ai')
103
+ st.session_state.ai_api_key = ai_key
104
+
105
+ if 'anthropic_api_key' not in st.session_state:
106
+ # الحصول على المفتاح من أسرار هجين فيس
107
+ anthropic_key = _get_huggingface_secret('anthropic')
108
+ st.session_state.anthropic_api_key = anthropic_key
109
+
110
+ # تهيئة محادثة الذكاء الاصطناعي
111
+ if 'messages' not in st.session_state:
112
+ st.session_state.messages = []
113
+
114
  if 'selected_model' not in st.session_state:
115
+ st.session_state.selected_model = "anthropic"
 
 
 
 
 
 
116
 
117
+ # تهيئة خدمة Claude AI
118
+ self.claude_service = ClaudeAIService(api_key=st.session_state.anthropic_api_key)
119
+
 
 
 
120
  def run(self):
121
+ """تشغيل تطبيق مساعد الذكاء الاصطناعي"""
122
+ # عرض عنوان التطبيق
123
+ st.title("مساعد الذكاء الاصطناعي")
124
+
125
+ # إنشاء تبويبات التطبيق
126
+ tabs = st.tabs([
127
+ "المحادثة",
128
+ "تحليل المستندات",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  "تحليل العقود",
130
+ "تقدير التكاليف",
 
 
131
  "تحليل المخاطر",
132
+ "الإعدادات"
133
  ])
134
 
135
+ # عرض محتوى كل تبويب
136
+ with tabs[0]:
137
+ self._render_chat_tab()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
+ with tabs[1]:
140
+ self._render_document_analysis_tab()
141
 
142
+ with tabs[2]:
143
+ self._render_contract_analysis_tab()
 
 
 
 
 
 
 
 
 
144
 
145
+ with tabs[3]:
146
+ self._render_cost_estimation_tab()
147
 
148
+ with tabs[4]:
149
+ self._render_risk_analysis_tab()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ with tabs[5]:
152
+ self._render_settings_tab()
153
+
154
+ def _render_chat_tab(self):
155
+ """عرض تبويب المحادثة مع الذكاء الاصطناعي"""
156
+ st.markdown("### المحادثة مع الذكاء الاصطناعي")
157
+
158
+ # اختيار نموذج الذكاء الاصطناعي
159
+ selected_model = st.selectbox(
160
+ "اختر نموذج الذكاء الاصطناعي:",
161
+ ["anthropic", "ai"],
162
+ index=0 if st.session_state.selected_model == "anthropic" else 1
163
+ )
164
+
165
+ # تحديث النموذج المختار إذا تغير
166
+ if selected_model != st.session_state.selected_model:
167
+ st.session_state.selected_model = selected_model
168
+ st.rerun()
169
+
170
+ # التحقق من وجود مفتاح API للنموذج المختار
171
+ api_key_available = False
172
+ if selected_model == "anthropic" and st.session_state.anthropic_api_key:
173
+ api_key_available = True
174
+ elif selected_model == "ai" and st.session_state.ai_api_key:
175
+ api_key_available = True
176
+
177
+ if not api_key_available:
178
+ st.error(f"مفتاح API لنموذج {selected_model} غير متوفر. يرجى إضافته في إعدادات الأسرار في هجين فيس.")
179
+ return
180
+
181
+ # عرض المحادثة السابقة
182
+ for message in st.session_state.messages:
183
  with st.chat_message(message["role"]):
184
+ st.markdown(message["content"])
185
 
186
+ # إدخال رسالة جديدة
187
+ if prompt := st.chat_input("اكتب رسالتك هنا..."):
188
+ # إضافة رسالة المستخدم إلى المحادثة
189
+ st.session_state.messages.append({"role": "user", "content": prompt})
 
 
 
 
190
  with st.chat_message("user"):
191
+ st.markdown(prompt)
192
 
193
+ # إضافة رسالة المساعد إلى المحادثة
194
  with st.chat_message("assistant"):
195
+ message_placeholder = st.empty()
196
+ full_response = ""
197
+
198
+ # الحصول على الرد من نموذج الذكاء الاصطناعي
199
+ try:
200
+ if selected_model == "anthropic":
201
+ response = self._get_anthropic_response(prompt)
202
+ else: # ai
203
+ response = self._get_ai_response(prompt)
204
+
205
+ # عرض الرد بشكل تدريجي
206
+ for chunk in response.split():
207
+ full_response += chunk + " "
208
+ time.sleep(0.05)
209
+ message_placeholder.markdown(full_response + "▌")
210
+
211
+ message_placeholder.markdown(full_response)
212
+
213
+ # إضافة الرد إلى المحادثة
214
+ st.session_state.messages.append({"role": "assistant", "content": full_response})
215
+ except Exception as e:
216
+ st.error(f"حدث خطأ أثناء الاتصال بنموذج {selected_model}: {str(e)}")
217
 
218
+ def _get_anthropic_response(self, prompt):
219
+ """الحصول على رد من نموذج Anthropic"""
220
+ try:
221
+ # إنشاء سلسلة المحادثة
222
+ messages = []
223
+ for msg in st.session_state.messages:
224
+ if msg["role"] == "user":
225
+ messages.append({"role": "user", "content": msg["content"]})
226
+ else:
227
+ messages.append({"role": "assistant", "content": msg["content"]})
228
 
229
+ # إضافة الرسالة الحالية
230
+ messages.append({"role": "user", "content": prompt})
231
 
232
+ # الحصول على الرد
233
+ response = self.claude_service.chat(
234
+ messages=messages,
235
+ model="claude-3-opus-20240229"
 
 
 
 
236
  )
237
 
238
+ return response
239
+ except Exception as e:
240
+ st.error(f"خطأ في الاتصال بـ Anthropic: {str(e)}")
241
+ return "عذرًا، حدث خطأ أثناء الاتصال بنموذج Anthropic."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
+ def _get_ai_response(self, prompt):
244
+ """الحصول على رد من نموذج AI"""
245
+ try:
246
+ # هنا يمكن استبدال هذا بالكود الفعلي للاتصال بنموذج AI
247
+ # هذا مجرد مثال
248
+ headers = {
249
+ "Authorization": f"Bearer {st.session_state.ai_api_key}",
250
+ "Content-Type": "application/json"
251
+ }
252
 
253
+ data = {
254
+ "model": "gpt-4",
255
+ "messages": [{"role": "user", "content": prompt}],
256
+ "max_tokens": 2000
257
+ }
258
 
259
+ response = requests.post(
260
+ "https://api.openai.com/v1/chat/completions",
261
+ headers=headers,
262
+ json=data
 
 
 
 
 
 
 
263
  )
264
 
265
+ if response.status_code == 200:
266
+ return response.json()["choices"][0]["message"]["content"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  else:
268
+ st.error(f"خطأ في الاستجابة: {response.status_code} - {response.text}")
269
+ return "عذرًا، حدث خطأ أثناء الاتصال بنموذج AI."
270
+ except Exception as e:
271
+ st.error(f"خطأ في الاتصال بـ AI: {str(e)}")
272
+ return "عذرًا، حدث خطأ أثناء الاتصال بنموذج AI."
273
 
274
+ def _render_document_analysis_tab(self):
275
+ """عرض تبويب تحليل المستندات"""
276
+ st.markdown("### تحليل المستندات")
277
+ st.write("هذه الميزة قيد التطوير.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
 
279
+ def _render_contract_analysis_tab(self):
280
+ """عرض تبويب تحليل العقود"""
281
+ st.markdown("### تحليل العقود")
282
+ st.write("هذه الميزة قيد التطوير.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
+ def _render_cost_estimation_tab(self):
285
+ """عرض تبويب تقدير التكاليف"""
286
+ st.markdown("### تقدير التكاليف")
287
+ st.write("هذه الميزة قيد التطوير.")
288
+
289
+ def _render_risk_analysis_tab(self):
290
+ """عرض تبويب تحليل المخاطر"""
291
+ st.markdown("### تحليل المخاطر")
292
+ st.write("هذه الميزة قيد التطوير.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
+ def _render_settings_tab(self):
295
+ """عرض تبويب الإعدادات"""
296
+ st.markdown("### إعدادات التطبيق")
297
 
298
+ # عرض حالة مفاتيح API
299
+ st.subheader("حالة مفاتيح API")
300
 
301
+ # التحقق من حالة مفتاح Anthropic
302
+ anthropic_key_status = "✅ متوفر" if st.session_state.anthropic_api_key else " غير متوفر"
303
+ st.markdown(f"**مفتاح Anthropic:** {anthropic_key_status}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
 
305
+ # التحقق من حالة مفتاح AI
306
+ ai_key_status = "✅ متوفر" if st.session_state.ai_api_key else " غير متوفر"
307
+ st.markdown(f"**مفتاح AI:** {ai_key_status}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
+ # إضافة معلومات حول كيفية إضافة المفاتيح
310
+ st.markdown("""
311
+ ### كيفية إضافة مفاتيح API
312
 
313
+ لإضافة مفاتيح API إلى التطبيق، يرجى اتباع الخطوات التالية:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
 
315
+ 1. انتقل إلى إعدادات التطبيق في منصة هجين فيس (Hugging Face)
316
+ 2. اختر قسم "الأسرار" (Secrets)
317
+ 3. أضف المفاتيح التالية:
318
+ - `ai`: مفتاح API لنموذج AI
319
+ - `anthropic`: مفتاح API لنموذج Anthropic
320
+
321
+ بعد إضافة المفاتيح، قم بإعادة تشغيل التطبيق لتطبيق التغييرات.
322
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
+ # تشغيل التطبيق
325
  if __name__ == "__main__":
326
+ app = AIAssistantApp()
327
  app.run()