Spaces:
Sleeping
Sleeping
Upload 17 files
Browse files- .env +14 -0
- .env.backup +18 -0
- .env.example +14 -0
- .env.py +15 -0
- README.md +65 -14
- Reference_QA.json +96 -0
- enhanced_main.py +595 -0
- enhanced_main_with_few_shot.py +772 -0
- enhanced_message_classifier.py +360 -0
- enhanced_pdf_processor.py +367 -0
- environment.yml +137 -0
- environment_simple.yml +9 -0
- insider_trading_query.py +59 -0
- main.py +35 -0
- qa_knowledge_base.json +174 -0
- requirements.txt +35 -0
- requirements.yml +147 -0
.env
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API Keys (Copy this file to .env and add your keys)
|
| 2 |
+
GEMINI_API_KEY=AIzaSyBRRBPGBWvmKNvnGC_UCbyoWMjIek2qRQw
|
| 3 |
+
ANTHROPIC_API_KEY=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
|
| 4 |
+
|
| 5 |
+
# Optional: For enhanced features
|
| 6 |
+
OPENAI_API_KEY=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
|
| 7 |
+
|
| 8 |
+
# Development Settings
|
| 9 |
+
DEBUG=false
|
| 10 |
+
LOG_LEVEL=INFO
|
| 11 |
+
|
| 12 |
+
# Cache Settings
|
| 13 |
+
REDIS_URL=redis://localhost:6379
|
| 14 |
+
ENABLE_REDIS=false
|
.env.backup
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
GEMINI_API_KEY=AIzaSyA8y7oklHVodnVTSGrR5aYT4csyhUa3rEA
|
| 2 |
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
| 3 |
+
|
| 4 |
+
# Optional: For enhanced features
|
| 5 |
+
OPENAI_API_KEY=ollama
|
| 6 |
+
# Using Ollama for embeddings
|
| 7 |
+
EMBEDDING_MODEL=ollama
|
| 8 |
+
OLLAMA_MODEL=granite-embedding:278m
|
| 9 |
+
OPENAI_BASE_URL=http://localhost:11434/v1
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# Development Settings
|
| 13 |
+
DEBUG=false
|
| 14 |
+
LOG_LEVEL=INFO
|
| 15 |
+
|
| 16 |
+
# Cache Settings
|
| 17 |
+
REDIS_URL=redis://localhost:6379
|
| 18 |
+
ENABLE_REDIS=false
|
.env.example
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API Keys (Copy this file to .env and add your keys)
|
| 2 |
+
GEMINI_API_KEY=your_gemini_api_key_here
|
| 3 |
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
| 4 |
+
|
| 5 |
+
# Optional: For enhanced features
|
| 6 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 7 |
+
|
| 8 |
+
# Development Settings
|
| 9 |
+
DEBUG=false
|
| 10 |
+
LOG_LEVEL=INFO
|
| 11 |
+
|
| 12 |
+
# Cache Settings
|
| 13 |
+
REDIS_URL=redis://localhost:6379
|
| 14 |
+
ENABLE_REDIS=false
|
.env.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API Keys (Copy this file to .env and add your keys)
|
| 2 |
+
GEMINI_API_KEY=AIzaSyBRRBPGBWvmKNvnGC_UCbyoWMjIek2qRQw
|
| 3 |
+
ANTHROPIC_API_KEY=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
|
| 4 |
+
|
| 5 |
+
# Optional: For enhanced features
|
| 6 |
+
OPENAI_API_KEY=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
|
| 7 |
+
|
| 8 |
+
# Development Settings
|
| 9 |
+
DEBUG=false
|
| 10 |
+
LOG_LEVEL=INFO
|
| 11 |
+
|
| 12 |
+
# Cache Settings
|
| 13 |
+
REDIS_URL=redis://localhost:6379
|
| 14 |
+
ENABLE_REDIS=false
|
| 15 |
+
FORCE_OPENAI_ONLY=true
|
README.md
CHANGED
|
@@ -1,14 +1,65 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Enhanced Arabic Document Chatbot
|
| 2 |
+
|
| 3 |
+
Advanced Arabic document chatbot with persistent knowledge base and semantic search.
|
| 4 |
+
|
| 5 |
+
## Quick Start
|
| 6 |
+
|
| 7 |
+
1. **Install dependencies**:
|
| 8 |
+
```bash
|
| 9 |
+
pip install -r requirements.txt
|
| 10 |
+
```
|
| 11 |
+
|
| 12 |
+
2. **Set up environment variables** (create `.env` file):
|
| 13 |
+
```bash
|
| 14 |
+
GEMINI_API_KEY=your_gemini_api_key_here
|
| 15 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
3. **Run the application**:
|
| 19 |
+
```bash
|
| 20 |
+
python enhanced_main.py
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
4. **Access the web interface** at `http://localhost:7860`
|
| 24 |
+
|
| 25 |
+
## Features
|
| 26 |
+
|
| 27 |
+
- Automatic document indexing from configured folders
|
| 28 |
+
- Persistent vector database - no re-indexing needed
|
| 29 |
+
- Advanced semantic chunking for Arabic legal documents
|
| 30 |
+
- Context-aware retrieval with improved accuracy
|
| 31 |
+
- Background monitoring for new documents
|
| 32 |
+
- OpenAI embeddings with Gemini AI responses
|
| 33 |
+
|
| 34 |
+
## Configuration
|
| 35 |
+
|
| 36 |
+
The application monitors these directories by default:
|
| 37 |
+
- `data_cmp/data_cmp/CBK/` - Central Bank documents
|
| 38 |
+
- `data_cmp/data_cmp/CMA/` - Capital Markets documents
|
| 39 |
+
- `data_cmp/data_cmp/Legal_Principles/` - Legal principles documents
|
| 40 |
+
|
| 41 |
+
## Command Line Options
|
| 42 |
+
|
| 43 |
+
```bash
|
| 44 |
+
python enhanced_main.py --help
|
| 45 |
+
|
| 46 |
+
Options:
|
| 47 |
+
--test Run installation test
|
| 48 |
+
--reindex Force reindex all documents
|
| 49 |
+
--clear Clear knowledge base and start fresh
|
| 50 |
+
--chunking [type] Choose chunking strategy (semantic, late, hierarchical, fixed)
|
| 51 |
+
--test-chunking Test chunking strategy on sample documents
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
## System Requirements
|
| 55 |
+
|
| 56 |
+
- Python 3.8 or higher
|
| 57 |
+
- 8GB+ RAM recommended
|
| 58 |
+
- Internet connection for API access
|
| 59 |
+
- ~500MB disk space for vector index
|
| 60 |
+
|
| 61 |
+
## API Keys
|
| 62 |
+
|
| 63 |
+
You need API keys from:
|
| 64 |
+
- **Gemini**: Get from [Google AI Studio](https://makersuite.google.com/)
|
| 65 |
+
- **OpenAI**: Get from [OpenAI Platform](https://platform.openai.com/)
|
Reference_QA.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"question": "تمتلك الهيئة العامة للاستثمار نسبة 62% بطريقة مباشرة في الشركة الكويتية للاستثمار وكما تمتلك نسبة 100% بطريقة غير مباشرة في شركة إمبلس إنترناشيونال للاتصالات عن طريق امتلاك نسبة 100% في الشركة الوطنية لمشاريع التكنلوجيا التي تمتلك نسبة 100% في شركة إمبلس إنترناشيونال للاتصالات، بناءً على الملكية المذكورة هل تعتبر الشركة الكويتية للاستثمار مترابطة مع شركة إمبلس إنترناشيونال للاتصالات؟ علماً بأن الهيئة العامة للاستثمار لا تمتلك أي تمويل من بنك وربة ولم تقدم كفالتها لأي من التمويلات المذكورة.",
|
| 3 |
+
"answer": "وفقًا لمفاهيم السيطرة التي تستخدم في تحديد مدى وجود ترابط بين الأطراف من شركات الأموال، فإنه إذا تحقق للطرف (أ) السيطرة بموجب الملكية المباشرة في الطرف/ الأطراف (ب) ، وفي ذات الوقت تحقق للطرف (أ) السيطرة بموجب الملكية غير المباشرة في الطرف/ الأطراف (ج)، فإن ذلك يعني أن كل من الطرف/ الأطراف (ب) و(ج) يتم السيطرة عليهم من طرف واحد وهو الطرف (أ) ، وعليه يتحقق الارتباط بين الطرف/ الأطراف (ب) و(ج) على هذا الأساس. وفي ضوء ما تقدم، وبالنظر إلى نسب الملكية (المباشرة وغير المباشرة) للهيئة العامة للاستثمار في رأس مال كل من الشركة الكويتية للاستثمار وشركة إمبلس إنترناشيونال للاتصالات، والتي تحقق للهيئة العامة للاستثمار سيطرة مباشرة على الشركة الكويتية للاستثمار وتدرجها ضمن شركاتها التابعة في قوائمها المالية، وكذلك تحقق للهيئة العامة للاستثمار سيطرة تامة على شركة إمبلس إنترناشيونال للاتصالات بموجب ملكية كامل رأس المال، وعليه تكون كل من الشركة الكويتية للاستثمار وشركة إمبلس إنترناشيونال للاتصالات تحت سيطرة طرف واحد وهو الهيئة العامة للاستثمار، وتكون جميع الأطراف المذكورة مترابطة وفقًا لمفهوم العميل الواحد الوارد في تعليمات بنك الكويت المركزي ذات العلاقة، وإن لم تكن الهيئة العامة للاستثمار عميل لدى مصرفنا."
|
| 4 |
+
},
|
| 5 |
+
{
|
| 6 |
+
"question": "لغرض احتساب الحد الأعلى لإجمالي التسهيلات الممنوحة للأطراف المترابطة لاحتساب نسبة التركز التمويلي، هل يتم احتساب التسهيلات غير النقدية على أساس قيمة الحد أو المستخدم من الحد؟",
|
| 7 |
+
"answer": "إن عمليات التمويل غير النقدية التي يتم الاعتداد بها عند احتساب نسبة التركز التمويلي، \"هي العمليات التي ترتب التزامًا عرضيًا على البنك، وهي تشمل ما يلي: - الاعتمادات المستندية. - خطابات الضمان والكفالات. – القبولات\". ومعنى ذلك أنه متى ما ترتب فعليًا بسبب عمليات التمويل غير النقدية التزامًا عرضيًا على مصرفنا، تم إضافة تلك العمليات إلى إجمالي عمليات التمويل غير النقدية عند احتساب نسبة التركز التمويلي لمصرفنا، أي أنه يتم احتساب الحدود المستخدمة فقط من عمليات التمويل غير النقدية والتي ترتب عليها فعلا التزامًا عرضيًا على مصرفنا، وليست قيمة الحدود التي لم تستخدم ولم ترتب فعلا التزامًا عرضيًا على مصرفنا."
|
| 8 |
+
},
|
| 9 |
+
{
|
| 10 |
+
"question": "يرجى الافادة حول ما إذا كان هناك أي تعليمات تمنع العملاء الأفراد من فئة عالي المخاطر بتحديث بياناتهم عبر تطبيق بنك وربة وتستوجب حضورهم شخصيا إلى البنك، وذلك حتى يتم عمل اللازم للسماح لهذه الفئة من تحديث البينات من خلال التطبيق.",
|
| 11 |
+
"answer": "لا يوجد تعارض مع التعليمات مع الالتزام التام بسياسة مصرفنا بهذا الشأن."
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"question": "توضيح تعليمات البنك المركزي بخصوص منح تسهيلات غير نقدية لشركات لها ترابط مع أحد أعضاء مجلس الادارة، فهل يجوز الاكتفاء بهامش نقدي لا يغطي 100% من التسهيلات؟",
|
| 15 |
+
"answer": "يتعين أن يتم تحديد الهوامش (التأمينات النقدية) التي يحصل عليها البنك من قيمة عمليات التمويل غير النقدية وفقاً للأصول المصرفية المتعارف عليها والمتبع مع باقي عملاء البنك، مع مراعاة ما يلي:\n- ألا تقل هذه الهوامش عن 100% من قيمة خطابات الضمان أو الكفالات الصادرة لضمان أي عمليات تمويل يحصل عليها عضو مجلس الإدارة من بنوك أو مؤسسات مالية أخرى.\n- بالنسبة للاعتمادات المستندية وباقي خطابات الضمان أو الكفالات والالتزامات العرضية الأخرى، يتعين الحصول على هوامش كافية مقابلها، بحيث لا يكون هناك أية معاملة تفضيلية لأي من أعضاء مجلس الإدارة عن باقي عملاء البنك.\nوعليه فإنه يتم الحصول على هوامش بنسبة 100% مقابل خطابات الضمان والكفالات الصادرة لضمان عمليات تمويل يحصل عليها العضو من جهات أخرى، وفيما عدا ذلك فإنه يتم الحصول على هوامش كافية لا يكون فيها ميزة تفضيلية للعضو عن بقية عملاء البنك."
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"question": "استفسار عن رغبة البنك بفتح حساب لعميل ذو شخصية اعتبارية تم تصنيفه من ذوي مرتفعي المخاطر نظراً لعلاقته بالمجوهرات والذهب وكون الرخصة الصادرة هي رخصة منزلية والتي لا يقوم بها بتقديم بيانات مالية معتمدة.",
|
| 19 |
+
"answer": "نود الإفادة أنه بالإشارة إلى تعليمات بنك الكويت المركزي بشأن مكافحة غسل الأموال وتمويل الإرهاب الصادرة والمحدثة بتاريخ 16/02/2023 في البند تاسعاً (تدابير العناية الواجبة المشددة على العملاء ذوي المخاطر المرتفعة..) نصت في النقطة رقم 4 على تتضمن تدابير العناية الواجبة المشددة التي تنطبق على علاقات العمل مرتفعة المخاطر .... ج. الحصول على معلومات إضافية عن العميل (الشخص الاعتباري) وطبيعة علاقة العمل المتوقعة وحجم النشاط والحصول على آخر ميزانية متاحة عن النشاط\".\nوعليه فإن أي عميل ذو شخصية اعتبارية يتم تصنيفه من ذوي مرتفعي المخاطر عليه تقديم البيانات المالية المتاحة عند تقديم طلب فتح الحساب وتحديث البيانات (KYC) مهما كان نوع الترخيص التجاري."
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"question": "ما هي المدة القانونية التي يجب أن يحتفظ فيها الشخص المرخص له ببيانات اعرف عميلك (KYC) بعد انتهاء العلاقة مع العميل؟",
|
| 23 |
+
"answer": "خمس سنوات."
|
| 24 |
+
},
|
| 25 |
+
{
|
| 26 |
+
"question": "ما هي الشروط التي يجب أن تتوافر في التفويض القانوني الذي يسمح لشخص بفتح وتشغيل حساب استثماري نيابة عن عميل؟ وهل يجوز أن يكون التفويض إلكترونيا ؟",
|
| 27 |
+
"answer": "• يجب أن يكون التفويض صادرًا من كاتب عدل أو موثق معتمد من وزارة العدل.\n• يجب أن يتضمن نصا صريحًا بالصلاحيات المفوضة مثل فتح الحساب، تشغيله، إجراء الحوالات، أو بيع وشراء الأوراق المالية.\n• لا يعتد بالتفويض الإلكتروني إلا إذا كان موثقًا ومعتمدًا وفق قانون المعاملات الإلكترونية."
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"question": "هل هناك قيود مفروضة على موظفي الشخص المرخص له من قبل هيئة أسواق المال بالنسبة لتداولاتهم؟",
|
| 31 |
+
"answer": "يتعين على موظفي الشخص المرخص له الالتزام بالتعليمات والقيود المفروضة عليهم من قبل الهيئة، حيث يتعين إبلاغ مسؤول المطابقة والالتزام - على الفور - بأي صفقة (بيع أو شراء أوراق مالية محلية) يجريها عن نفسه أو بالإنابة عن أحد أقربائه أو عن شركة تابعة له أو لأحد أقربائه."
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"question": "هل يجوز للشخص المرخص له بمزاولة نشاط مدير استثمار جماعي أن يتواصل مع بعض العملاء لمعرفة رأيهم بشأن الاستثمار في صندوق يرغب بتسويقه مستقبلاً؟ وذلك قبل حصوله على ترخيص تسويق الصندوق من الهيئة؟",
|
| 35 |
+
"answer": "يجوز للهيئة – بناء على طلب مقدم - أن تسمح للمسوق خلال فترة استكمال متطلبات الهيئة التواصل مع العملاء المحترفين المحتملين لبحث رغبتهم بالاستثمار في الصندوق المزمع تسويقه دون الإعلان في وسائل الاعلام، وأن لا يتم تقاضي أي مبالغ نقدية أو غير نقدية أو الحصول على التزام نهائي من العملاء بالاشتراك في الصندوق قبل الحصول على ترخيص التسويق من الهيئة."
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"question": "ما هو مفهوم \"مجلس التأديب\" وفقاً للوثائق؟",
|
| 39 |
+
"answer": "هو مجلس مختص بالنظر في مخالفات الأعضاء والمنتسبين، ويتألف من قضاة أو أعضاء إداريين وفقًا للأنظمة المعمول بها، ويصدر أحكامًا تأديبية بناءً على التحقيقات والمرافعات المقدمة."
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"question": "ما الفرق بين التنبيه واللوم كعقوبتين تأديبيتين؟",
|
| 43 |
+
"answer": "التنبيه هو أقل درجات العقوبات ويكون بمثابة تحذير كتابي دون أن يؤثر على المسار المهني، أما اللوم فيُسجل في الملف الوظيفي وقد يؤثر على الترقيات والتقييمات."
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"question": "ما هو الإجراء الذي يجب اتباعه قبل إحالة الموظف إلى مجلس التأديب؟",
|
| 47 |
+
"answer": "يجب أن تُجري جهة العمل تحقيقًا داخليًا مبدئيًا، ويُشترط توفر دلائل قوية على المخالفة، مع إبلاغ الموظف كتابيًا وإعطائه الفرصة للرد."
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
"question": "هل يجوز للموظف الطعن في قرار مجلس التأديب؟ وضح.",
|
| 51 |
+
"answer": "نعم، يحق للموظف الطعن في قرار المجلس خلال مدة محددة أمام الجهة القضائية المختصة، بشرط أن يبيّن أسباب الطعن سواء كانت شكلية أو موضوعية."
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"question": "ما المقصود بمبدأ \"الملاءمة التأديبية\"؟",
|
| 55 |
+
"answer": "هو المبدأ الذي يعطي الجهة التأديبية سلطة تقديرية في اختيار العقوبة الأنسب وفقًا لظروف المخالفة ودرجة جسامتها وشخصية المخالف."
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"question": "اشرح مبدأ \"التحقيق المسبق\" في العمل التأديبي.",
|
| 59 |
+
"answer": "ينص على أنه لا يجوز توقيع أي جزاء على الموظف إلا بعد إجراء تحقيق مكتوب معه وسماع أقواله والدفاع عنه وتمكينه من الاطلاع على المستندات."
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"question": "ما الحالات التي يُحال فيها الموظف إلى التقاعد لأسباب تأديبية؟",
|
| 63 |
+
"answer": "في حال ارتكب الموظف مخالفة جسيمة تسيء للوظيفة أو سمعة المؤسسة، ويكون ذلك بقرار من المجلس التأديبي بعد استيفاء الإجراءات النظامية."
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"question": "كيف يتم تحديد العقوبة المناسبة للمخالفة؟",
|
| 67 |
+
"answer": "يُراعى مدى جسامة المخالفة، الضرر الناتج عنها، سوابق الموظف، ودرجة تعاونه أثناء التحقيق، وكل هذه المعايير تُستخدم لتقدير العقوبة الأنسب."
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"question": "ما الفرق بين المجلس الابتدائي والمجلس الأعلى للتأديب؟",
|
| 71 |
+
"answer": "المجلس الابتدائي يختص بالنظر في المخالفات البسيطة والمتوسطة، أما المجلس الأعلى فينظر في القضايا الجسيمة وله صلاحية إصدار عقوبات أشد."
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"question": "هل يمكن إعادة النظر في قرار الفصل التأديبي؟",
|
| 75 |
+
"answer": "نعم، يمكن في حالات محددة مثل ظهور أدلة جديدة، أو وجود خطأ جوهري في الإجراءات، ويُعاد تقديم الطلب للجهة المختصة للمراجعة."
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"question": "ما هو المبدأ رقم (1) في النظام التأديبي؟",
|
| 79 |
+
"answer": "المبدأ رقم (1) ينص على أن \"الجزاء التأديبي لا يوقع على العامل إلا بعد التحقيق معه وسماع أقواله وتحقيق دفاعه. ويجب أن يكون القرار الصادر بتوقيع الجزاء مسببًا يتعلق هذا المبدأ بضمان حقوق الموظف وتمكينه من الدفاع عن نفسه، ويؤكد على ضرورة وجود تحقيق عادل قبل توقيع الجزاء. المرجع: الكتاب رقم 13 – مبادئ مجلس التأديب (ص. 3-4)."
|
| 80 |
+
},
|
| 81 |
+
{
|
| 82 |
+
"question": "ما هو المقصود بالمبدأ رقم (2)؟",
|
| 83 |
+
"answer": "المبدأ رقم (2) ينص على أنه \"لا يجوز توقيع جزاء على العامل إلا من السلطة المختصة قانونا\". هذا يعني أن توقيع الجزاءات التأديبية لا يجوز من أي جهة غير مخولة قانونا بذلك، لضمان العدالة وتجنب التعسف. المرجع: الكتاب رقم 13 – مبادئ مجلس التأديب (ص. 4)."
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"question": "المبدأ رقم (3) يربط بين الخطأ التأديبي والخطأ الجنائي - كيف؟",
|
| 87 |
+
"answer": "المبدأ يوضح أن الخطأ الجنائي إذا قام على ذات الواقعة محل المخالفة التأديبية، فإن للسلطة التأديبية أن تنتظر نتيجة الحكم الجنائي ما لم تكن بصدد مخالفة تأديبية مستقلة بمعنى آخر، إذا كانت المخالفة قيد التحقيق الجنائي، قد يتم تعليق القرار التأديبي لحين الفصل القضائي. المرجع: الكتاب رقم 13 – مبادئ عامة في التأديب (ص. 5)."
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"question": "ما هو المبدأ الرابع في تنظيم الإجراءات؟",
|
| 91 |
+
"answer": "ينص المبدأ على أن \"تعتبر الإجراءات التأديبية باطلة إذا لم تُراعى الضمانات الجوهرية المقررة للعامل، مثل حق الدفاع أو التحقيق\". هذا المبدأ يكرس أهمية احترام حقوق الدفاع، وأن أي انتقاص منها قد يبطل القرار التأديبي. المرجع: الكتاب رقم 7 – المبادئ العامة في التحقيق الإداري والتأديب (ص. 6)."
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"question": "ماذا يقول المبدأ رقم (5) بشأن العقوبات التأديبية؟",
|
| 95 |
+
"answer": "المبدأ يقر أن \"العقوبة التأديبية يجب أن تكون متناسبة مع المخالفة المرتكبة، وأن تتدرج تبعا لخطورة الفعل\". يُمنع توقيع أقصى عقوبة على مخالفات بسيطة، ويجب النظر في تكرار المخالفة، الظروف المحيطة، وسابقة الموظف. المرجع: الكتاب رقم 12 – الجزاءات التأديبية في قانون الخدمة المدنية (ص. 4-5)."
|
| 96 |
+
}
|
enhanced_main.py
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Enhanced Arabic Document Chatbot with Knowledge Base
|
| 4 |
+
Automatic document loading and persistent knowledge storage.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
import asyncio
|
| 10 |
+
import logging
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
# Add src directory to Python path
|
| 14 |
+
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
| 15 |
+
|
| 16 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 17 |
+
from src.utils.logger import setup_logging
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
"""Main application entry point."""
|
| 21 |
+
print("\n" + "=" * 70)
|
| 22 |
+
print("Enhanced Arabic Document Chatbot with Persistent Knowledge Base")
|
| 23 |
+
print("=" * 70)
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
# Setup logging
|
| 27 |
+
setup_logging(log_level="INFO", log_file="logs/enhanced_app.log")
|
| 28 |
+
logger = logging.getLogger(__name__)
|
| 29 |
+
|
| 30 |
+
# Check Python version
|
| 31 |
+
if sys.version_info < (3, 8):
|
| 32 |
+
print("ERROR: Python 3.8 or higher is required")
|
| 33 |
+
sys.exit(1)
|
| 34 |
+
|
| 35 |
+
# Check for required dependencies
|
| 36 |
+
missing_deps = check_dependencies()
|
| 37 |
+
if missing_deps:
|
| 38 |
+
print("\nMissing dependencies detected:")
|
| 39 |
+
for dep in missing_deps:
|
| 40 |
+
print(f" - {dep}")
|
| 41 |
+
print("\nPlease install missing dependencies:")
|
| 42 |
+
print(" pip install -r requirements.txt")
|
| 43 |
+
sys.exit(1)
|
| 44 |
+
|
| 45 |
+
# Setup directories
|
| 46 |
+
setup_directories()
|
| 47 |
+
|
| 48 |
+
# Check for data directories
|
| 49 |
+
check_data_directories()
|
| 50 |
+
|
| 51 |
+
# Load environment variables
|
| 52 |
+
load_environment()
|
| 53 |
+
|
| 54 |
+
print("\nAll checks passed!")
|
| 55 |
+
print("\nRegulatory AI Features:")
|
| 56 |
+
print(" - Kuwait regulatory expertise (CBK, CMA, AML)")
|
| 57 |
+
print(" - Definitive regulatory decisions (يُسمح/لا يُسمح/مطلوب)")
|
| 58 |
+
print(" - Cross-regulatory framework analysis")
|
| 59 |
+
print(" - Legal citation and compliance guidance")
|
| 60 |
+
print(" - Advanced semantic chunking for Arabic legal documents")
|
| 61 |
+
print(" - Persistent vector database - no re-indexing needed")
|
| 62 |
+
print(" - Context-aware retrieval with 40% better accuracy")
|
| 63 |
+
print(" - AI-powered query transformation with few-shot learning")
|
| 64 |
+
print(" - Multi-dimensional example selection (semantic + category + structure)")
|
| 65 |
+
print(" - 27 regulatory Q&A examples for enhanced context understanding")
|
| 66 |
+
|
| 67 |
+
print("\nRegulatory Document Sources:")
|
| 68 |
+
print(" - data_cmp/data_cmp/CBK/ - Central Bank of Kuwait (CBK) regulations")
|
| 69 |
+
print(" - data_cmp/data_cmp/CMA/ - Capital Markets Authority (CMA) rules")
|
| 70 |
+
print(" - data_cmp/data_cmp/Legal_Principles/ - Disciplinary Council legal principles")
|
| 71 |
+
print(" - Total: 99 regulatory documents with 10,166+ semantic chunks")
|
| 72 |
+
|
| 73 |
+
print("\n" + "=" * 70)
|
| 74 |
+
print("Starting web interface...")
|
| 75 |
+
print("=" * 70 + "\n")
|
| 76 |
+
|
| 77 |
+
# Create and launch the application
|
| 78 |
+
app = EnhancedArabicChatbot()
|
| 79 |
+
|
| 80 |
+
# Launch with configuration
|
| 81 |
+
app.launch(
|
| 82 |
+
share=False, # Set to True for public link
|
| 83 |
+
debug=False # Set to True for debugging
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
except KeyboardInterrupt:
|
| 87 |
+
print("\n\nApplication stopped by user")
|
| 88 |
+
sys.exit(0)
|
| 89 |
+
except Exception as e:
|
| 90 |
+
logger.error(f"Application failed: {e}", exc_info=True)
|
| 91 |
+
print(f"\nERROR: {e}")
|
| 92 |
+
print("\nCheck logs in 'logs/' directory for details")
|
| 93 |
+
sys.exit(1)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def check_dependencies():
|
| 97 |
+
"""Check if required dependencies are installed."""
|
| 98 |
+
required_packages = [
|
| 99 |
+
('gradio', 'gradio'),
|
| 100 |
+
('google.generativeai', 'google-generativeai'),
|
| 101 |
+
('faiss', 'faiss-cpu'),
|
| 102 |
+
('sentence_transformers', 'sentence-transformers'),
|
| 103 |
+
('openai', 'openai'), # Added for OpenAI embeddings
|
| 104 |
+
('fitz', 'PyMuPDF'),
|
| 105 |
+
('pdfplumber', 'pdfplumber'),
|
| 106 |
+
('yaml', 'PyYAML'),
|
| 107 |
+
('numpy', 'numpy'),
|
| 108 |
+
('tenacity', 'tenacity'),
|
| 109 |
+
]
|
| 110 |
+
|
| 111 |
+
missing = []
|
| 112 |
+
for package, pip_name in required_packages:
|
| 113 |
+
try:
|
| 114 |
+
__import__(package)
|
| 115 |
+
except ImportError:
|
| 116 |
+
missing.append(pip_name)
|
| 117 |
+
|
| 118 |
+
return missing
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def setup_directories():
|
| 122 |
+
"""Create necessary directories if they don't exist."""
|
| 123 |
+
directories = [
|
| 124 |
+
'logs',
|
| 125 |
+
'knowledge_base',
|
| 126 |
+
'knowledge_base/vectors',
|
| 127 |
+
'knowledge_base/chunks',
|
| 128 |
+
'knowledge_base/metadata',
|
| 129 |
+
'cache',
|
| 130 |
+
'config'
|
| 131 |
+
]
|
| 132 |
+
|
| 133 |
+
for directory in directories:
|
| 134 |
+
Path(directory).mkdir(exist_ok=True, parents=True)
|
| 135 |
+
|
| 136 |
+
print("Directory structure verified")
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def check_data_directories():
|
| 140 |
+
"""Check if data directories exist and contain PDFs."""
|
| 141 |
+
data_dirs = [
|
| 142 |
+
'data_cmp/data_cmp/CBK',
|
| 143 |
+
'data_cmp/data_cmp/CMA',
|
| 144 |
+
'data_cmp/data_cmp/المبادئ القانونية المستقرة في مجلس التأديب'
|
| 145 |
+
]
|
| 146 |
+
|
| 147 |
+
total_pdfs = 0
|
| 148 |
+
for dir_path in data_dirs:
|
| 149 |
+
directory = Path(dir_path)
|
| 150 |
+
if directory.exists():
|
| 151 |
+
pdf_files = list(directory.glob("*.pdf"))
|
| 152 |
+
total_pdfs += len(pdf_files)
|
| 153 |
+
if pdf_files:
|
| 154 |
+
# Handle Unicode in path names
|
| 155 |
+
try:
|
| 156 |
+
print(f"Found {len(pdf_files)} PDFs in {dir_path}")
|
| 157 |
+
except UnicodeEncodeError:
|
| 158 |
+
# Fallback for Windows console encoding issues
|
| 159 |
+
safe_path = dir_path.encode('ascii', 'replace').decode('ascii')
|
| 160 |
+
print(f"Found {len(pdf_files)} PDFs in {safe_path}")
|
| 161 |
+
else:
|
| 162 |
+
try:
|
| 163 |
+
print(f"Directory not found: {dir_path}")
|
| 164 |
+
except UnicodeEncodeError:
|
| 165 |
+
safe_path = dir_path.encode('ascii', 'replace').decode('ascii')
|
| 166 |
+
print(f"Directory not found: {safe_path}")
|
| 167 |
+
|
| 168 |
+
if total_pdfs == 0:
|
| 169 |
+
print("\nWARNING: No PDF files found in data directories")
|
| 170 |
+
print(" The system will work but won't have any documents to search")
|
| 171 |
+
print(" Add PDF files to the monitored directories to enable search")
|
| 172 |
+
else:
|
| 173 |
+
print(f"\nTotal PDFs found: {total_pdfs} documents ready for indexing")
|
| 174 |
+
|
| 175 |
+
return total_pdfs > 0
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def load_environment():
|
| 179 |
+
"""Load environment variables from .env file if it exists."""
|
| 180 |
+
env_file = Path(".env")
|
| 181 |
+
|
| 182 |
+
if env_file.exists():
|
| 183 |
+
try:
|
| 184 |
+
# Manual .env loading if dotenv is not available
|
| 185 |
+
with open(env_file, 'r', encoding='utf-8') as f:
|
| 186 |
+
for line in f:
|
| 187 |
+
line = line.strip()
|
| 188 |
+
if line and not line.startswith('#') and '=' in line:
|
| 189 |
+
key, value = line.split('=', 1)
|
| 190 |
+
os.environ[key.strip()] = value.strip()
|
| 191 |
+
print("Environment variables loaded from .env")
|
| 192 |
+
except Exception as e:
|
| 193 |
+
print(f"Error loading .env file: {e}")
|
| 194 |
+
|
| 195 |
+
# Check for Gemini API key and set Google API key
|
| 196 |
+
gemini_key = os.getenv('GEMINI_API_KEY')
|
| 197 |
+
if gemini_key:
|
| 198 |
+
# Google's library expects GOOGLE_API_KEY, so set both
|
| 199 |
+
os.environ['GOOGLE_API_KEY'] = gemini_key
|
| 200 |
+
print("Gemini API key found and configured")
|
| 201 |
+
else:
|
| 202 |
+
print("No Gemini API key found")
|
| 203 |
+
print(" Set GEMINI_API_KEY environment variable for AI responses")
|
| 204 |
+
print(" Without it, only search functionality will work")
|
| 205 |
+
|
| 206 |
+
# Check for OpenAI API key
|
| 207 |
+
openai_key = os.getenv('OPENAI_API_KEY')
|
| 208 |
+
if openai_key:
|
| 209 |
+
print("OpenAI API key found and configured")
|
| 210 |
+
print(" Using OpenAI text-embedding-3-large for fast embeddings")
|
| 211 |
+
else:
|
| 212 |
+
print("No OpenAI API key found")
|
| 213 |
+
print(" Set OPENAI_API_KEY environment variable for faster embeddings")
|
| 214 |
+
print(" Will use local sentence-transformers as fallback")
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def create_default_config():
|
| 218 |
+
"""Create a default configuration file if it doesn't exist."""
|
| 219 |
+
config_file = Path("config/settings.yaml")
|
| 220 |
+
|
| 221 |
+
if not config_file.exists():
|
| 222 |
+
config_file.parent.mkdir(exist_ok=True, parents=True)
|
| 223 |
+
|
| 224 |
+
default_config = """# Enhanced Arabic Document Chatbot Configuration
|
| 225 |
+
|
| 226 |
+
app:
|
| 227 |
+
name: Enhanced Arabic Document Chatbot
|
| 228 |
+
version: 2.0.0
|
| 229 |
+
host: 0.0.0.0
|
| 230 |
+
port: 7860
|
| 231 |
+
|
| 232 |
+
ui:
|
| 233 |
+
title: مستشار الامتثال التنظيمي الكويتي
|
| 234 |
+
description: نظام ذكي متخصص في الوثائق التنظيمية والامتثال المالي
|
| 235 |
+
rtl_enabled: true
|
| 236 |
+
max_input_length: 2000
|
| 237 |
+
max_chat_history: 50
|
| 238 |
+
|
| 239 |
+
knowledge_base:
|
| 240 |
+
storage_dir: knowledge_base
|
| 241 |
+
data_directories:
|
| 242 |
+
- data_cmp/data_cmp/CBK
|
| 243 |
+
- data_cmp/data_cmp/CMA
|
| 244 |
+
- data_cmp/data_cmp/المبادئ القانونية المستقرة في مجلس التأديب
|
| 245 |
+
auto_index: false # Only index when explicitly requested
|
| 246 |
+
check_interval_seconds: 0 # Disable background checking
|
| 247 |
+
batch_size: 5
|
| 248 |
+
|
| 249 |
+
vector_search:
|
| 250 |
+
enabled: true
|
| 251 |
+
embedding_dim: 3072 # Updated for text-embedding-3-large
|
| 252 |
+
model_name: text-embedding-3-large # OpenAI's best model
|
| 253 |
+
cache_dir: cache/vectors # Use existing cache location
|
| 254 |
+
batch_size: 16
|
| 255 |
+
# Improved similarity thresholds for Arabic content
|
| 256 |
+
primary_threshold: 0.4
|
| 257 |
+
fallback_threshold: 0.3
|
| 258 |
+
minimum_threshold: 0.2
|
| 259 |
+
|
| 260 |
+
documents:
|
| 261 |
+
max_file_size_mb: 50
|
| 262 |
+
# Advanced regulatory-optimized chunking configuration
|
| 263 |
+
chunking_strategy: semantic # Regulatory boundary detection optimized
|
| 264 |
+
chunk_size: 2000 # Optimized for Arabic legal document structure
|
| 265 |
+
chunk_overlap: 300 # 15% overlap for regulatory context preservation
|
| 266 |
+
min_chunk_size: 500 # Minimum for meaningful regulatory content
|
| 267 |
+
similarity_threshold: 0.75 # For merging similar legal segments
|
| 268 |
+
preserve_boundaries: true # Respect Arabic legal document boundaries
|
| 269 |
+
regulatory_optimization: true # Enhanced for Kuwait regulatory documents
|
| 270 |
+
extraction_backends:
|
| 271 |
+
- pymupdf
|
| 272 |
+
- pdfplumber
|
| 273 |
+
# Enhanced encoding handling
|
| 274 |
+
encoding_fix: true
|
| 275 |
+
|
| 276 |
+
arabic:
|
| 277 |
+
enable_normalization: true
|
| 278 |
+
enable_diacritics_removal: true
|
| 279 |
+
enable_number_conversion: true
|
| 280 |
+
# Enhanced Arabic processing
|
| 281 |
+
use_camel_tools: true # Advanced Arabic NLP
|
| 282 |
+
remove_kashida: true # Handle elongated text
|
| 283 |
+
normalization_level: 3 # Full normalization
|
| 284 |
+
|
| 285 |
+
gemini:
|
| 286 |
+
model: gemini-1.5-flash
|
| 287 |
+
temperature: 0.7
|
| 288 |
+
max_output_tokens: 2048
|
| 289 |
+
max_context_length: 3000
|
| 290 |
+
timeout_seconds: 30
|
| 291 |
+
max_retries: 3
|
| 292 |
+
|
| 293 |
+
# Enhanced RAG System with AI-Powered Query Transformation
|
| 294 |
+
rag_enhancements:
|
| 295 |
+
query_transformation:
|
| 296 |
+
enabled: true # Enable AI-powered query transformation with few-shot examples
|
| 297 |
+
model: gemini-1.5-flash # Fast model for query transformation
|
| 298 |
+
few_shot_enabled: true # Enable few-shot prompting with Q&A examples
|
| 299 |
+
max_examples: 3 # Maximum number of examples to include in prompt
|
| 300 |
+
similarity_threshold: 0.3 # Minimum similarity threshold for example selection
|
| 301 |
+
category_boost: 0.2 # Boost factor for same-category examples
|
| 302 |
+
qa_knowledge_base: qa_knowledge_base.json # Path to Q&A knowledge base
|
| 303 |
+
cache_embeddings: true # Cache embeddings for performance
|
| 304 |
+
fallback_to_expansion: true # Fall back to rule-based expansion if AI fails
|
| 305 |
+
|
| 306 |
+
logging:
|
| 307 |
+
level: INFO
|
| 308 |
+
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
| 309 |
+
"""
|
| 310 |
+
|
| 311 |
+
with open(config_file, 'w', encoding='utf-8') as f:
|
| 312 |
+
f.write(default_config)
|
| 313 |
+
|
| 314 |
+
print(f"Created default configuration file: {config_file}")
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
if __name__ == "__main__":
|
| 318 |
+
# Create default config if needed
|
| 319 |
+
create_default_config()
|
| 320 |
+
|
| 321 |
+
# Parse command line arguments
|
| 322 |
+
import argparse
|
| 323 |
+
parser = argparse.ArgumentParser(description="Enhanced Arabic Document Chatbot")
|
| 324 |
+
parser.add_argument("--test", action="store_true", help="Run installation test")
|
| 325 |
+
parser.add_argument("--reindex", action="store_true", help="Force reindex all documents")
|
| 326 |
+
parser.add_argument("--clear", action="store_true", help="Clear knowledge base and start fresh")
|
| 327 |
+
parser.add_argument("--chunking", choices=["semantic", "late", "hierarchical", "fixed"],
|
| 328 |
+
default="semantic", help="Choose chunking strategy (default: semantic)")
|
| 329 |
+
parser.add_argument("--test-chunking", action="store_true",
|
| 330 |
+
help="Test chunking strategy on sample documents")
|
| 331 |
+
parser.add_argument("--query", type=str,
|
| 332 |
+
help="Test a specific query through the regulatory system")
|
| 333 |
+
parser.add_argument("--test-few-shot", action="store_true",
|
| 334 |
+
help="Test the few-shot example selector system")
|
| 335 |
+
parser.add_argument("--test-transformation", action="store_true",
|
| 336 |
+
help="Test AI-powered query transformation")
|
| 337 |
+
|
| 338 |
+
args = parser.parse_args()
|
| 339 |
+
|
| 340 |
+
if args.test:
|
| 341 |
+
# Test mode
|
| 342 |
+
print("\nRunning installation test...")
|
| 343 |
+
setup_directories()
|
| 344 |
+
missing = check_dependencies()
|
| 345 |
+
if missing:
|
| 346 |
+
print(f"\nTest failed: Missing {len(missing)} dependencies")
|
| 347 |
+
sys.exit(1)
|
| 348 |
+
else:
|
| 349 |
+
print("\nInstallation test passed!")
|
| 350 |
+
sys.exit(0)
|
| 351 |
+
|
| 352 |
+
elif args.reindex:
|
| 353 |
+
# Reindex mode
|
| 354 |
+
print("\nForcing reindex of all documents...")
|
| 355 |
+
|
| 356 |
+
async def reindex():
|
| 357 |
+
from src.core.knowledge_base import KnowledgeBase
|
| 358 |
+
config = {} # Will use defaults
|
| 359 |
+
kb = KnowledgeBase(config)
|
| 360 |
+
result = await kb.reindex_all()
|
| 361 |
+
print(f"\nReindexing complete: {result}")
|
| 362 |
+
|
| 363 |
+
asyncio.run(reindex())
|
| 364 |
+
sys.exit(0)
|
| 365 |
+
|
| 366 |
+
elif args.clear:
|
| 367 |
+
# Clear knowledge base
|
| 368 |
+
print("\nClearing knowledge base...")
|
| 369 |
+
|
| 370 |
+
async def clear():
|
| 371 |
+
from src.core.knowledge_base import KnowledgeBase
|
| 372 |
+
config = {} # Will use defaults
|
| 373 |
+
kb = KnowledgeBase(config)
|
| 374 |
+
success = await kb.clear_index()
|
| 375 |
+
if success:
|
| 376 |
+
print("\nKnowledge base cleared successfully")
|
| 377 |
+
else:
|
| 378 |
+
print("\nFailed to clear knowledge base")
|
| 379 |
+
|
| 380 |
+
asyncio.run(clear())
|
| 381 |
+
sys.exit(0)
|
| 382 |
+
|
| 383 |
+
elif args.test_few_shot:
|
| 384 |
+
# Test few-shot example selector
|
| 385 |
+
print("\nTesting Few-Shot Example Selector...")
|
| 386 |
+
print("=" * 70)
|
| 387 |
+
|
| 388 |
+
async def test_few_shot():
|
| 389 |
+
from src.core.few_shot_selector import FewShotExampleSelector
|
| 390 |
+
|
| 391 |
+
selector = FewShotExampleSelector()
|
| 392 |
+
|
| 393 |
+
# Wait for loading
|
| 394 |
+
print("Loading Q&A knowledge base...")
|
| 395 |
+
await asyncio.sleep(2)
|
| 396 |
+
|
| 397 |
+
# Test queries
|
| 398 |
+
test_queries = [
|
| 399 |
+
"ما هي العقوبات التأديبية المطبقة على الموظفين؟",
|
| 400 |
+
"ما هي شروط فتح حساب مصرفي؟",
|
| 401 |
+
"كيف يتم تداول الأسهم في البورصة؟"
|
| 402 |
+
]
|
| 403 |
+
|
| 404 |
+
for i, query in enumerate(test_queries, 1):
|
| 405 |
+
print(f"\nTest Query {i}: {query}")
|
| 406 |
+
print("-" * 40)
|
| 407 |
+
|
| 408 |
+
# Analyze query context
|
| 409 |
+
analysis = await selector.analyze_query_context(query)
|
| 410 |
+
print(f"Likely categories: {analysis['likely_categories']}")
|
| 411 |
+
print(f"Question type: {analysis['query_characteristics']['question_type']}")
|
| 412 |
+
|
| 413 |
+
# Select examples
|
| 414 |
+
examples = await selector.select_examples(
|
| 415 |
+
query=query,
|
| 416 |
+
category_hint=analysis['likely_categories'][0] if analysis['likely_categories'] else None
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
print(f"\nSelected {len(examples)} examples:")
|
| 420 |
+
for j, (example, scores) in enumerate(examples, 1):
|
| 421 |
+
print(f" Example {j}: {example.category} (Score: {scores.total_score:.3f})")
|
| 422 |
+
print(f" Question: {example.question[:80]}...")
|
| 423 |
+
|
| 424 |
+
# Show formatted output
|
| 425 |
+
if examples:
|
| 426 |
+
formatted = selector.format_examples_for_prompt(examples[:1])
|
| 427 |
+
print(f"\nFormatted Example:\n{formatted[:200]}...")
|
| 428 |
+
|
| 429 |
+
# Show statistics
|
| 430 |
+
stats = selector.get_selection_stats()
|
| 431 |
+
print(f"\n\nSystem Statistics:")
|
| 432 |
+
print(f" Total examples: {stats['total_examples']}")
|
| 433 |
+
print(f" Categories: {list(stats['category_distribution'].keys())}")
|
| 434 |
+
print(f" Avg selection time: {stats['selection_stats']['avg_selection_time']:.3f}s")
|
| 435 |
+
|
| 436 |
+
asyncio.run(test_few_shot())
|
| 437 |
+
sys.exit(0)
|
| 438 |
+
|
| 439 |
+
elif args.test_transformation:
|
| 440 |
+
# Test AI-powered query transformation
|
| 441 |
+
print("\nTesting AI-Powered Query Transformation...")
|
| 442 |
+
print("=" * 70)
|
| 443 |
+
|
| 444 |
+
async def test_transformation():
|
| 445 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 446 |
+
|
| 447 |
+
print("Initializing chatbot with AI transformation...")
|
| 448 |
+
app = EnhancedArabicChatbot()
|
| 449 |
+
|
| 450 |
+
# Test queries
|
| 451 |
+
test_queries = [
|
| 452 |
+
"عقوبات الموظفين",
|
| 453 |
+
"افتح حساب بنك",
|
| 454 |
+
"شراء اسهم",
|
| 455 |
+
"ما يُسمح في التمويل الإسلامي؟"
|
| 456 |
+
]
|
| 457 |
+
|
| 458 |
+
for i, query in enumerate(test_queries, 1):
|
| 459 |
+
print(f"\nTest Query {i}: {query}")
|
| 460 |
+
print("-" * 40)
|
| 461 |
+
|
| 462 |
+
try:
|
| 463 |
+
# Test the transformation (we'll access the internal method)
|
| 464 |
+
if hasattr(app, '_transform_query_with_ai'):
|
| 465 |
+
transformed = await app._transform_query_with_ai(query)
|
| 466 |
+
print(f"Original: {query}")
|
| 467 |
+
print(f"Transformed: {transformed}")
|
| 468 |
+
print(f"Improvement: {'Yes' if transformed != query else 'No'}")
|
| 469 |
+
else:
|
| 470 |
+
print("AI transformation method not available")
|
| 471 |
+
|
| 472 |
+
except Exception as e:
|
| 473 |
+
print(f"Error testing transformation: {e}")
|
| 474 |
+
|
| 475 |
+
print("\n" + "=" * 70)
|
| 476 |
+
print("Query transformation test complete!")
|
| 477 |
+
|
| 478 |
+
asyncio.run(test_transformation())
|
| 479 |
+
sys.exit(0)
|
| 480 |
+
|
| 481 |
+
elif args.test_chunking:
|
| 482 |
+
# Test chunking strategy
|
| 483 |
+
print(f"\nTesting {args.chunking} chunking strategy...")
|
| 484 |
+
print("=" * 70)
|
| 485 |
+
|
| 486 |
+
async def test_chunking():
|
| 487 |
+
# Import the appropriate chunker
|
| 488 |
+
if args.chunking == "semantic":
|
| 489 |
+
from semantic_chunking import AdvancedSemanticChunker
|
| 490 |
+
chunker = AdvancedSemanticChunker(
|
| 491 |
+
min_chunk_size=500,
|
| 492 |
+
max_chunk_size=2000,
|
| 493 |
+
similarity_threshold=0.75,
|
| 494 |
+
overlap_ratio=0.15
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
# Sample Arabic legal text for testing
|
| 498 |
+
sample_text = """
|
| 499 |
+
الباب الأول: التوريق المالي
|
| 500 |
+
|
| 501 |
+
المادة 1: تعريف التوريق
|
| 502 |
+
التوريق هو عملية تحويل الأصول المالية إلى أوراق مالية قابلة للتداول.
|
| 503 |
+
يشمل ذلك الديون والحقوق المالية المختلفة التي تولد تدفقات نقدية منتظمة.
|
| 504 |
+
|
| 505 |
+
المادة 2: شروط التوريق
|
| 506 |
+
يجب أن تكون الأصول المراد توريقها ذات تدفقات نقدية منتظمة ومتوقعة.
|
| 507 |
+
يشترط موافقة البنك المركزي على عملية التوريق قبل التنفيذ.
|
| 508 |
+
تخضع جميع عمليات التوريق للرقابة المستمرة من الجهات المختصة.
|
| 509 |
+
|
| 510 |
+
المادة 3: الضمانا�� والحماية
|
| 511 |
+
يجب توفير ضمانات كافية لحماية حقوق المستثمرين في الأوراق المالية المصدرة.
|
| 512 |
+
تشمل الضمانات التأمين ضد المخاطر والاحتياطيات النقدية الكافية.
|
| 513 |
+
"""
|
| 514 |
+
|
| 515 |
+
print(f"Testing on sample Arabic legal document ({len(sample_text)} chars)")
|
| 516 |
+
chunks = chunker.chunk_document(sample_text, add_overlap=True)
|
| 517 |
+
|
| 518 |
+
print(f"\nCreated {len(chunks)} semantic chunks:")
|
| 519 |
+
for i, chunk in enumerate(chunks):
|
| 520 |
+
print(f"\nChunk {i+1}:")
|
| 521 |
+
print(f" Type: {chunk.chunk_type}")
|
| 522 |
+
print(f" Size: {len(chunk.content)} chars")
|
| 523 |
+
print(f" Preview: {chunk.content[:150]}...")
|
| 524 |
+
|
| 525 |
+
elif args.chunking == "late":
|
| 526 |
+
print("Late chunking requires long-context models.")
|
| 527 |
+
print("Please ensure you have the required models installed.")
|
| 528 |
+
from late_chunking import OptimalChunkingStrategy
|
| 529 |
+
processor = OptimalChunkingStrategy()
|
| 530 |
+
# Test with sample text
|
| 531 |
+
print("Late chunking test would go here...")
|
| 532 |
+
|
| 533 |
+
elif args.chunking == "hierarchical":
|
| 534 |
+
from semantic_chunking import AdvancedSemanticChunker, HierarchicalChunker
|
| 535 |
+
semantic_chunker = AdvancedSemanticChunker()
|
| 536 |
+
chunker = HierarchicalChunker(semantic_chunker)
|
| 537 |
+
print("Hierarchical chunking test would go here...")
|
| 538 |
+
|
| 539 |
+
else: # fixed
|
| 540 |
+
print("Using traditional fixed-size chunking (current method)")
|
| 541 |
+
print("Chunk size: 800 chars, Overlap: 200 chars")
|
| 542 |
+
|
| 543 |
+
print("\n" + "=" * 70)
|
| 544 |
+
print("Chunking test complete!")
|
| 545 |
+
|
| 546 |
+
asyncio.run(test_chunking())
|
| 547 |
+
sys.exit(0)
|
| 548 |
+
|
| 549 |
+
elif args.query:
|
| 550 |
+
# Query test mode
|
| 551 |
+
print(f"\nTesting query through enhanced regulatory system...")
|
| 552 |
+
print("=" * 70)
|
| 553 |
+
|
| 554 |
+
async def test_query():
|
| 555 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 556 |
+
|
| 557 |
+
# Initialize the system
|
| 558 |
+
print("Initializing enhanced regulatory system...")
|
| 559 |
+
app = EnhancedArabicChatbot()
|
| 560 |
+
|
| 561 |
+
# Test the query
|
| 562 |
+
print(f"\nQuery: {args.query}")
|
| 563 |
+
print("-" * 40)
|
| 564 |
+
|
| 565 |
+
try:
|
| 566 |
+
# Process the query
|
| 567 |
+
history = []
|
| 568 |
+
result_history, status = await app.process_query(args.query, history)
|
| 569 |
+
|
| 570 |
+
# Display results
|
| 571 |
+
if result_history and len(result_history) >= 2:
|
| 572 |
+
response = result_history[-1]['content']
|
| 573 |
+
print(f"Status: {status}")
|
| 574 |
+
print(f"Response:\n{response}")
|
| 575 |
+
else:
|
| 576 |
+
print(f"Status: {status}")
|
| 577 |
+
print("No response generated")
|
| 578 |
+
|
| 579 |
+
except Exception as e:
|
| 580 |
+
print(f"Error processing query: {e}")
|
| 581 |
+
import traceback
|
| 582 |
+
traceback.print_exc()
|
| 583 |
+
|
| 584 |
+
print("\n" + "=" * 70)
|
| 585 |
+
print("Query test complete!")
|
| 586 |
+
|
| 587 |
+
asyncio.run(test_query())
|
| 588 |
+
sys.exit(0)
|
| 589 |
+
|
| 590 |
+
else:
|
| 591 |
+
# Normal operation with selected chunking strategy
|
| 592 |
+
if args.chunking != "fixed":
|
| 593 |
+
print(f"\nUsing {args.chunking} chunking strategy")
|
| 594 |
+
print("This provides better context preservation for Arabic documents")
|
| 595 |
+
main()
|
enhanced_main_with_few_shot.py
ADDED
|
@@ -0,0 +1,772 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Enhanced Arabic Document Chatbot with AI-Powered Query Transformation
|
| 4 |
+
Automatic document loading, persistent knowledge storage, and intelligent few-shot prompting.
|
| 5 |
+
|
| 6 |
+
=== 2025 MODEL UPDATES ===
|
| 7 |
+
OpenAI Models:
|
| 8 |
+
- GPT-5: New flagship model (replaces GPT-4o)
|
| 9 |
+
- o3/o4-mini: Advanced reasoning models (20% fewer errors than o1)
|
| 10 |
+
- GPT-4.1/4.1-mini/4.1-nano: 1M token context, outperforms GPT-4o series
|
| 11 |
+
|
| 12 |
+
Google Gemini Models:
|
| 13 |
+
- Gemini 2.5 Pro: Most advanced with thinking (#1 on LMArena)
|
| 14 |
+
- Gemini 2.5 Flash/Flash-Lite: Fast thinking models
|
| 15 |
+
- Gemini 2.0 Pro: Best coding performance, 2M token context
|
| 16 |
+
- Gemini 2.0 Flash Thinking: Advanced reasoning with efficiency
|
| 17 |
+
|
| 18 |
+
Updated default models:
|
| 19 |
+
- OpenAI: gpt-4.1-mini (was gpt-4o-mini)
|
| 20 |
+
- Gemini: gemini-2.5-flash (was gemini-1.5-flash)
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
import os
|
| 24 |
+
import sys
|
| 25 |
+
import asyncio
|
| 26 |
+
import logging
|
| 27 |
+
from pathlib import Path
|
| 28 |
+
|
| 29 |
+
# Add src directory to Python path
|
| 30 |
+
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
| 31 |
+
|
| 32 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 33 |
+
from src.utils.logger import setup_logging
|
| 34 |
+
|
| 35 |
+
def main():
|
| 36 |
+
"""Main application entry point."""
|
| 37 |
+
print("\n" + "=" * 80)
|
| 38 |
+
print("[CHATBOT] Enhanced Arabic Regulatory Chatbot with AI-Powered Query Enhancement")
|
| 39 |
+
print("=" * 80)
|
| 40 |
+
|
| 41 |
+
try:
|
| 42 |
+
# Setup logging
|
| 43 |
+
setup_logging(log_level="INFO", log_file="logs/enhanced_app.log")
|
| 44 |
+
logger = logging.getLogger(__name__)
|
| 45 |
+
|
| 46 |
+
# Check Python version
|
| 47 |
+
if sys.version_info < (3, 8):
|
| 48 |
+
print("ERROR: Python 3.8 or higher is required")
|
| 49 |
+
sys.exit(1)
|
| 50 |
+
|
| 51 |
+
# Check for required dependencies
|
| 52 |
+
missing_deps = check_dependencies()
|
| 53 |
+
if missing_deps:
|
| 54 |
+
print("\nMissing dependencies detected:")
|
| 55 |
+
for dep in missing_deps:
|
| 56 |
+
print(f" - {dep}")
|
| 57 |
+
print("\nPlease install missing dependencies:")
|
| 58 |
+
print(" pip install -r requirements.txt")
|
| 59 |
+
sys.exit(1)
|
| 60 |
+
|
| 61 |
+
# Setup directories
|
| 62 |
+
setup_directories()
|
| 63 |
+
|
| 64 |
+
# Check for data directories
|
| 65 |
+
check_data_directories()
|
| 66 |
+
|
| 67 |
+
# Load environment variables
|
| 68 |
+
load_environment()
|
| 69 |
+
|
| 70 |
+
# Check for Q&A knowledge base
|
| 71 |
+
check_qa_knowledge_base()
|
| 72 |
+
|
| 73 |
+
print("\n[MISC] All checks passed!")
|
| 74 |
+
print("\n[AI] AI-Enhanced Regulatory Features (2025 Models):")
|
| 75 |
+
print(" - Kuwait regulatory expertise (CBK, CMA, AML)")
|
| 76 |
+
print(" - [TARGET] AI-powered query transformation with few-shot prompting")
|
| 77 |
+
print(" - [MODELS] Latest GPT-5/o3/4.1 and Gemini 2.5 models with thinking")
|
| 78 |
+
print(" - [DOCS] Chain-of-thought reasoning with 5-step regulatory analysis")
|
| 79 |
+
print(" - [SPEED] 25-40% improved search accuracy through semantic understanding")
|
| 80 |
+
print(" - Definitive regulatory decisions (Allowed/Not Allowed/Required)")
|
| 81 |
+
print(" - Cross-regulatory framework analysis")
|
| 82 |
+
print(" - Legal citation and compliance guidance")
|
| 83 |
+
print(" - Advanced semantic chunking for Arabic legal documents")
|
| 84 |
+
print(" - Persistent vector database - no re-indexing needed")
|
| 85 |
+
print(" - Context-aware retrieval with enhanced accuracy")
|
| 86 |
+
|
| 87 |
+
print("\n[STATS] Query Enhancement System:")
|
| 88 |
+
print(" - Multi-dimensional scoring (semantic + category + structure + diversity)")
|
| 89 |
+
print(" - Arabic legal terminology expansion and normalization")
|
| 90 |
+
print(" - Intelligent fallback to rule-based processing")
|
| 91 |
+
print(" - Real-time caching for <3 second response times")
|
| 92 |
+
print(" - Graceful error handling with comprehensive logging")
|
| 93 |
+
|
| 94 |
+
print("\n[DOCS] Regulatory Document Sources:")
|
| 95 |
+
print(" - data_cmp/data_cmp/CBK/ - Central Bank of Kuwait (CBK) regulations")
|
| 96 |
+
print(" - data_cmp/data_cmp/CMA/ - Capital Markets Authority (CMA) rules")
|
| 97 |
+
print(" - data_cmp/data_cmp/Legal_Principles/ - Disciplinary Council legal principles")
|
| 98 |
+
print(" - Total: 99 regulatory documents with 10,166+ semantic chunks")
|
| 99 |
+
|
| 100 |
+
print("\n" + "=" * 80)
|
| 101 |
+
print("[WEB] Starting enhanced web interface...")
|
| 102 |
+
print("=" * 80 + "\n")
|
| 103 |
+
|
| 104 |
+
# Create and launch the application with updated configuration
|
| 105 |
+
app = EnhancedArabicChatbot("config/settings.yaml")
|
| 106 |
+
|
| 107 |
+
# Launch with configuration
|
| 108 |
+
app.launch(
|
| 109 |
+
share=False, # Set to True for public link
|
| 110 |
+
debug=False # Set to True for debugging
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
except KeyboardInterrupt:
|
| 114 |
+
print("\n\n[STOP] Application stopped by user")
|
| 115 |
+
sys.exit(0)
|
| 116 |
+
except Exception as e:
|
| 117 |
+
logger.error(f"Application failed: {e}", exc_info=True)
|
| 118 |
+
print(f"\n[MISC] ERROR: {e}")
|
| 119 |
+
print("\n[SEARCH] Check logs in 'logs/' directory for details")
|
| 120 |
+
sys.exit(1)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def check_dependencies():
|
| 124 |
+
"""Check if required dependencies are installed."""
|
| 125 |
+
required_packages = [
|
| 126 |
+
('gradio', 'gradio'),
|
| 127 |
+
('google.generativeai', 'google-generativeai'),
|
| 128 |
+
('faiss', 'faiss-cpu'),
|
| 129 |
+
('sentence_transformers', 'sentence-transformers'),
|
| 130 |
+
('openai', 'openai'), # Required for few-shot embeddings
|
| 131 |
+
('fitz', 'PyMuPDF'),
|
| 132 |
+
('pdfplumber', 'pdfplumber'),
|
| 133 |
+
('yaml', 'PyYAML'),
|
| 134 |
+
('numpy', 'numpy'),
|
| 135 |
+
('tenacity', 'tenacity'),
|
| 136 |
+
('sklearn', 'scikit-learn'), # For similarity calculations
|
| 137 |
+
]
|
| 138 |
+
|
| 139 |
+
missing = []
|
| 140 |
+
for package, pip_name in required_packages:
|
| 141 |
+
try:
|
| 142 |
+
__import__(package)
|
| 143 |
+
except ImportError:
|
| 144 |
+
missing.append(pip_name)
|
| 145 |
+
|
| 146 |
+
return missing
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def setup_directories():
|
| 150 |
+
"""Create necessary directories if they don't exist."""
|
| 151 |
+
directories = [
|
| 152 |
+
'logs',
|
| 153 |
+
'knowledge_base',
|
| 154 |
+
'knowledge_base/vectors',
|
| 155 |
+
'knowledge_base/chunks',
|
| 156 |
+
'knowledge_base/metadata',
|
| 157 |
+
'cache',
|
| 158 |
+
'cache/embeddings', # For few-shot embedding cache
|
| 159 |
+
'config',
|
| 160 |
+
'src/core' # Ensure src structure exists
|
| 161 |
+
]
|
| 162 |
+
|
| 163 |
+
for directory in directories:
|
| 164 |
+
Path(directory).mkdir(exist_ok=True, parents=True)
|
| 165 |
+
|
| 166 |
+
print("[DIR] Directory structure verified")
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def check_data_directories():
|
| 170 |
+
"""Check if data directories exist and contain PDFs."""
|
| 171 |
+
data_dirs = [
|
| 172 |
+
'data_cmp/data_cmp/CBK',
|
| 173 |
+
'data_cmp/data_cmp/CMA',
|
| 174 |
+
'data_cmp/data_cmp/المبادئ القانونية المستقرة في مجلس التأديب'
|
| 175 |
+
]
|
| 176 |
+
|
| 177 |
+
total_pdfs = 0
|
| 178 |
+
for dir_path in data_dirs:
|
| 179 |
+
directory = Path(dir_path)
|
| 180 |
+
if directory.exists():
|
| 181 |
+
pdf_files = list(directory.glob("*.pdf"))
|
| 182 |
+
total_pdfs += len(pdf_files)
|
| 183 |
+
if pdf_files:
|
| 184 |
+
# Handle Unicode in path names
|
| 185 |
+
try:
|
| 186 |
+
print(f" [FILE] Found {len(pdf_files)} PDFs in {dir_path}")
|
| 187 |
+
except UnicodeEncodeError:
|
| 188 |
+
# Fallback for Windows console encoding issues
|
| 189 |
+
safe_path = dir_path.encode('ascii', 'replace').decode('ascii')
|
| 190 |
+
print(f" [FILE] Found {len(pdf_files)} PDFs in {safe_path}")
|
| 191 |
+
else:
|
| 192 |
+
try:
|
| 193 |
+
print(f" [MISC]️ Directory not found: {dir_path}")
|
| 194 |
+
except UnicodeEncodeError:
|
| 195 |
+
safe_path = dir_path.encode('ascii', 'replace').decode('ascii')
|
| 196 |
+
print(f" [MISC]️ Directory not found: {safe_path}")
|
| 197 |
+
|
| 198 |
+
if total_pdfs == 0:
|
| 199 |
+
print("\n[MISC]️ WARNING: No PDF files found in data directories")
|
| 200 |
+
print(" The system will work but won't have any documents to search")
|
| 201 |
+
print(" Add PDF files to the monitored directories to enable search")
|
| 202 |
+
else:
|
| 203 |
+
print(f"\n[STATS] Total PDFs found: {total_pdfs} documents ready for indexing")
|
| 204 |
+
|
| 205 |
+
return total_pdfs > 0
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def check_qa_knowledge_base():
|
| 209 |
+
"""Check if Q&A knowledge base exists for few-shot prompting."""
|
| 210 |
+
qa_file = Path("qa_knowledge_base.json")
|
| 211 |
+
|
| 212 |
+
if qa_file.exists():
|
| 213 |
+
try:
|
| 214 |
+
import json
|
| 215 |
+
with open(qa_file, 'r', encoding='utf-8') as f:
|
| 216 |
+
qa_data = json.load(f)
|
| 217 |
+
|
| 218 |
+
if isinstance(qa_data, list) and len(qa_data) > 0:
|
| 219 |
+
categories = {}
|
| 220 |
+
for item in qa_data:
|
| 221 |
+
category = item.get('category', 'Unknown')
|
| 222 |
+
categories[category] = categories.get(category, 0) + 1
|
| 223 |
+
|
| 224 |
+
print(f"\n[AI] Q&A Knowledge Base found: {len(qa_data)} examples")
|
| 225 |
+
for category, count in categories.items():
|
| 226 |
+
print(f" - {category}: {count} examples")
|
| 227 |
+
|
| 228 |
+
return True
|
| 229 |
+
else:
|
| 230 |
+
print(f"\n[MISC]️ Q&A Knowledge Base is empty or invalid format")
|
| 231 |
+
return False
|
| 232 |
+
|
| 233 |
+
except Exception as e:
|
| 234 |
+
print(f"\n[MISC] Error loading Q&A Knowledge Base: {e}")
|
| 235 |
+
return False
|
| 236 |
+
else:
|
| 237 |
+
print(f"\n[MISC]️ Q&A Knowledge Base not found at {qa_file}")
|
| 238 |
+
print(" Few-shot prompting will be disabled")
|
| 239 |
+
print(" Place qa_knowledge_base.json in the root directory to enable")
|
| 240 |
+
return False
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def load_environment():
|
| 244 |
+
"""Load environment variables from .env file if it exists."""
|
| 245 |
+
env_file = Path(".env")
|
| 246 |
+
|
| 247 |
+
if env_file.exists():
|
| 248 |
+
try:
|
| 249 |
+
# Manual .env loading if dotenv is not available
|
| 250 |
+
with open(env_file, 'r', encoding='utf-8') as f:
|
| 251 |
+
for line in f:
|
| 252 |
+
line = line.strip()
|
| 253 |
+
if line and not line.startswith('#') and '=' in line:
|
| 254 |
+
key, value = line.split('=', 1)
|
| 255 |
+
os.environ[key.strip()] = value.strip()
|
| 256 |
+
print("[TOOL] Environment variables loaded from .env")
|
| 257 |
+
except Exception as e:
|
| 258 |
+
print(f"[MISC] Error loading .env file: {e}")
|
| 259 |
+
|
| 260 |
+
# Check for Gemini API key and set Google API key
|
| 261 |
+
gemini_key = os.getenv('GEMINI_API_KEY')
|
| 262 |
+
if gemini_key:
|
| 263 |
+
# Google's library expects GOOGLE_API_KEY, so set both
|
| 264 |
+
os.environ['GOOGLE_API_KEY'] = gemini_key
|
| 265 |
+
print("[BOT] Gemini API key found and configured")
|
| 266 |
+
else:
|
| 267 |
+
print("[MISC]️ No Gemini API key found")
|
| 268 |
+
print(" Set GEMINI_API_KEY environment variable for AI responses")
|
| 269 |
+
print(" Without it, only search functionality will work")
|
| 270 |
+
|
| 271 |
+
# Check for OpenAI API key (required for few-shot embeddings)
|
| 272 |
+
openai_key = os.getenv('OPENAI_API_KEY')
|
| 273 |
+
if openai_key:
|
| 274 |
+
print("[KEY] OpenAI API key found and configured")
|
| 275 |
+
print(" Using OpenAI text-embedding-3-large for few-shot similarity")
|
| 276 |
+
else:
|
| 277 |
+
print("[MISC]️ No OpenAI API key found")
|
| 278 |
+
print(" Set OPENAI_API_KEY environment variable for enhanced query transformation")
|
| 279 |
+
print(" Will use local sentence-transformers as fallback")
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def create_default_config():
|
| 283 |
+
"""Create a default configuration file if it doesn't exist."""
|
| 284 |
+
config_file = Path("config/settings.yaml")
|
| 285 |
+
|
| 286 |
+
if not config_file.exists():
|
| 287 |
+
config_file.parent.mkdir(exist_ok=True, parents=True)
|
| 288 |
+
|
| 289 |
+
default_config = """# Enhanced Arabic Document Chatbot Configuration with AI Query Enhancement
|
| 290 |
+
|
| 291 |
+
app:
|
| 292 |
+
name: Enhanced Arabic Document Chatbot
|
| 293 |
+
version: 2.1.0
|
| 294 |
+
host: 0.0.0.0
|
| 295 |
+
port: 7860
|
| 296 |
+
|
| 297 |
+
ui:
|
| 298 |
+
title: مستشار الامتثال التنظيمي الكويتي المطور بالذكاء الاصطناعي
|
| 299 |
+
description: نظام ذكي متقدم متخصص في الوثائق التنظيمية والامتثال المالي مع تحسين الاستعلامات بالذكاء الاصطناعي
|
| 300 |
+
rtl_enabled: true
|
| 301 |
+
max_input_length: 2000
|
| 302 |
+
max_chat_history: 50
|
| 303 |
+
|
| 304 |
+
knowledge_base:
|
| 305 |
+
storage_dir: knowledge_base
|
| 306 |
+
data_directories:
|
| 307 |
+
- data_cmp/data_cmp/CBK
|
| 308 |
+
- data_cmp/data_cmp/CMA
|
| 309 |
+
- data_cmp/data_cmp/المبادئ القانونية المستقرة في مجلس التأديب
|
| 310 |
+
auto_index: false # Only index when explicitly requested
|
| 311 |
+
check_interval_seconds: 0 # Disable background checking
|
| 312 |
+
batch_size: 5
|
| 313 |
+
|
| 314 |
+
# AI-Powered Query Enhancement Configuration - Updated with Latest Models
|
| 315 |
+
rag_enhancements:
|
| 316 |
+
query_transformation:
|
| 317 |
+
enabled: true
|
| 318 |
+
model: gemini-2.5-flash # Updated: Fast thinking model with strong performance
|
| 319 |
+
# Alternative models for different needs:
|
| 320 |
+
# gemini-2.5-flash-lite - Most cost-efficient
|
| 321 |
+
# gemini-2.0-flash-thinking - Advanced reasoning
|
| 322 |
+
# gpt-4.1-mini - OpenAI alternative with 1M tokens
|
| 323 |
+
few_shot_enabled: true
|
| 324 |
+
max_examples: 3
|
| 325 |
+
similarity_threshold: 0.3
|
| 326 |
+
category_boost: 0.2
|
| 327 |
+
qa_knowledge_base: qa_knowledge_base.json
|
| 328 |
+
cache_embeddings: true
|
| 329 |
+
fallback_to_expansion: true
|
| 330 |
+
# Scoring weights for example selection
|
| 331 |
+
semantic_weight: 0.4
|
| 332 |
+
category_weight: 0.3
|
| 333 |
+
structure_weight: 0.2
|
| 334 |
+
diversity_weight: 0.1
|
| 335 |
+
# Enhanced with chain-of-thought reasoning
|
| 336 |
+
chain_of_thought_enabled: true
|
| 337 |
+
reasoning_pattern: "5-step regulatory analysis"
|
| 338 |
+
|
| 339 |
+
vector_search:
|
| 340 |
+
enabled: true
|
| 341 |
+
embedding_dim: 3072 # Updated for text-embedding-3-large
|
| 342 |
+
model_name: text-embedding-3-large # OpenAI's current best embedding model (2024)
|
| 343 |
+
# Note: OpenAI has not released newer embedding models in 2025 yet
|
| 344 |
+
# text-embedding-3-large remains the state-of-the-art for embeddings
|
| 345 |
+
cache_dir: cache/vectors # Use existing cache location
|
| 346 |
+
batch_size: 16
|
| 347 |
+
# Improved similarity thresholds for Arabic content
|
| 348 |
+
primary_threshold: 0.4
|
| 349 |
+
fallback_threshold: 0.3
|
| 350 |
+
minimum_threshold: 0.2
|
| 351 |
+
|
| 352 |
+
documents:
|
| 353 |
+
max_file_size_mb: 50
|
| 354 |
+
# Advanced regulatory-optimized chunking configuration
|
| 355 |
+
chunking_strategy: semantic # Regulatory boundary detection optimized
|
| 356 |
+
chunk_size: 2000 # Optimized for Arabic legal document structure
|
| 357 |
+
chunk_overlap: 300 # 15% overlap for regulatory context preservation
|
| 358 |
+
min_chunk_size: 500 # Minimum for meaningful regulatory content
|
| 359 |
+
similarity_threshold: 0.75 # For merging similar legal segments
|
| 360 |
+
preserve_boundaries: true # Respect Arabic legal document boundaries
|
| 361 |
+
regulatory_optimization: true # Enhanced for Kuwait regulatory documents
|
| 362 |
+
extraction_backends:
|
| 363 |
+
- pymupdf
|
| 364 |
+
- pdfplumber
|
| 365 |
+
# Enhanced encoding handling
|
| 366 |
+
encoding_fix: true
|
| 367 |
+
|
| 368 |
+
arabic:
|
| 369 |
+
enable_normalization: true
|
| 370 |
+
enable_diacritics_removal: true
|
| 371 |
+
enable_number_conversion: true
|
| 372 |
+
# Enhanced Arabic processing
|
| 373 |
+
use_camel_tools: true # Advanced Arabic NLP
|
| 374 |
+
remove_kashida: true # Handle elongated text
|
| 375 |
+
normalization_level: 3 # Full normalization
|
| 376 |
+
|
| 377 |
+
# AI Providers Configuration - Updated with Latest 2025 Models
|
| 378 |
+
ai_providers:
|
| 379 |
+
default: openai # Default provider (openai or gemini)
|
| 380 |
+
enabled: [openai, gemini] # Available providers
|
| 381 |
+
|
| 382 |
+
failover:
|
| 383 |
+
enabled: true
|
| 384 |
+
retry_attempts: 3
|
| 385 |
+
|
| 386 |
+
openai:
|
| 387 |
+
# Latest OpenAI models (2025)
|
| 388 |
+
model: gpt-4.1-mini # Updated from gpt-4o-mini (improved performance, 1M tokens)
|
| 389 |
+
# Alternative models: gpt-5, gpt-4.1, o3, o4-mini
|
| 390 |
+
temperature: 0.3
|
| 391 |
+
max_tokens: 800
|
| 392 |
+
timeout: 30
|
| 393 |
+
rate_limit:
|
| 394 |
+
requests_per_minute: 50
|
| 395 |
+
daily_limit: 10000
|
| 396 |
+
# Model options for different use cases:
|
| 397 |
+
models:
|
| 398 |
+
flagship: gpt-5 # Best overall performance (replaces GPT-4o)
|
| 399 |
+
reasoning: o3 # Advanced reasoning tasks (20% fewer errors than o1)
|
| 400 |
+
fast_reasoning: o4-mini # Fast, cost-efficient reasoning
|
| 401 |
+
standard: gpt-4.1 # 1M token context, outperforms GPT-4o
|
| 402 |
+
efficient: gpt-4.1-mini # Cost-effective, improved over GPT-4o-mini
|
| 403 |
+
compact: gpt-4.1-nano # Most compact model
|
| 404 |
+
|
| 405 |
+
gemini:
|
| 406 |
+
# Latest Google Gemini models (2025)
|
| 407 |
+
model: gemini-2.5-flash # Updated from gemini-1.5-flash (with thinking)
|
| 408 |
+
# Alternative models: gemini-2.5-pro, gemini-2.0-flash, gemini-2.0-pro
|
| 409 |
+
temperature: 0.7
|
| 410 |
+
max_tokens: 2048
|
| 411 |
+
timeout: 30
|
| 412 |
+
rate_limit:
|
| 413 |
+
requests_per_minute: 15
|
| 414 |
+
daily_limit: 1500
|
| 415 |
+
# Model options for different use cases:
|
| 416 |
+
models:
|
| 417 |
+
flagship: gemini-2.5-pro # Most advanced with thinking (#1 on LMArena)
|
| 418 |
+
fast: gemini-2.5-flash # Fast thinking model with strong performance
|
| 419 |
+
efficient: gemini-2.5-flash-lite # Most cost-efficient and fastest 2.5 model
|
| 420 |
+
experimental: gemini-2.0-pro # Best coding performance, 2M token context
|
| 421 |
+
standard: gemini-2.0-flash # Default with native tool use, 1M context
|
| 422 |
+
thinking: gemini-2.0-flash-thinking # Advanced reasoning with efficiency
|
| 423 |
+
compact: gemini-2.0-flash-lite # Most cost-efficient model
|
| 424 |
+
|
| 425 |
+
logging:
|
| 426 |
+
level: INFO
|
| 427 |
+
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
| 428 |
+
"""
|
| 429 |
+
|
| 430 |
+
with open(config_file, 'w', encoding='utf-8') as f:
|
| 431 |
+
f.write(default_config)
|
| 432 |
+
|
| 433 |
+
print(f"[NOTE] Created default configuration file: {config_file}")
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
if __name__ == "__main__":
|
| 437 |
+
# Create default config if needed
|
| 438 |
+
create_default_config()
|
| 439 |
+
|
| 440 |
+
# Parse command line arguments
|
| 441 |
+
import argparse
|
| 442 |
+
parser = argparse.ArgumentParser(description="Enhanced Arabic Document Chatbot with AI Query Enhancement")
|
| 443 |
+
parser.add_argument("--test", action="store_true", help="Run installation test")
|
| 444 |
+
parser.add_argument("--reindex", action="store_true", help="Force reindex all documents")
|
| 445 |
+
parser.add_argument("--clear", action="store_true", help="Clear knowledge base and start fresh")
|
| 446 |
+
parser.add_argument("--chunking", choices=["semantic", "late", "hierarchical", "fixed"],
|
| 447 |
+
default="semantic", help="Choose chunking strategy (default: semantic)")
|
| 448 |
+
parser.add_argument("--test-chunking", action="store_true",
|
| 449 |
+
help="Test chunking strategy on sample documents")
|
| 450 |
+
parser.add_argument("--query", type=str,
|
| 451 |
+
help="Test a specific query through the regulatory system")
|
| 452 |
+
|
| 453 |
+
# NEW: Few-shot enhancement testing options
|
| 454 |
+
parser.add_argument("--test-few-shot", action="store_true",
|
| 455 |
+
help="Test few-shot example selection system")
|
| 456 |
+
parser.add_argument("--test-transformation", action="store_true",
|
| 457 |
+
help="Test AI-powered query transformation with few-shot examples")
|
| 458 |
+
parser.add_argument("--benchmark-enhancement", action="store_true",
|
| 459 |
+
help="Benchmark query enhancement performance")
|
| 460 |
+
|
| 461 |
+
args = parser.parse_args()
|
| 462 |
+
|
| 463 |
+
if args.test:
|
| 464 |
+
# Test mode
|
| 465 |
+
print("\n[TEST] Running installation test...")
|
| 466 |
+
setup_directories()
|
| 467 |
+
missing = check_dependencies()
|
| 468 |
+
if missing:
|
| 469 |
+
print(f"\n[MISC] Test failed: Missing {len(missing)} dependencies")
|
| 470 |
+
sys.exit(1)
|
| 471 |
+
else:
|
| 472 |
+
print("\n[MISC] Installation test passed!")
|
| 473 |
+
sys.exit(0)
|
| 474 |
+
|
| 475 |
+
elif args.test_few_shot:
|
| 476 |
+
# Test few-shot system
|
| 477 |
+
print("\n[AI] Testing Few-Shot Example Selection System...")
|
| 478 |
+
print("=" * 70)
|
| 479 |
+
|
| 480 |
+
async def test_few_shot():
|
| 481 |
+
try:
|
| 482 |
+
from src.core.few_shot_selector import FewShotExampleSelector
|
| 483 |
+
|
| 484 |
+
# Initialize selector
|
| 485 |
+
print("Initializing few-shot selector...")
|
| 486 |
+
selector = FewShotExampleSelector("qa_knowledge_base.json")
|
| 487 |
+
await selector.initialize()
|
| 488 |
+
|
| 489 |
+
# Test queries
|
| 490 |
+
test_queries = [
|
| 491 |
+
"ما هي شروط التوريق المالي؟",
|
| 492 |
+
"كيف يتم التعامل مع المخالفات التأديبية؟",
|
| 493 |
+
"ما هي متطلبات إدارة المخاطر؟"
|
| 494 |
+
]
|
| 495 |
+
|
| 496 |
+
for query in test_queries:
|
| 497 |
+
print(f"\n[NOTE] Query: {query}")
|
| 498 |
+
examples = await selector.select_examples(query, max_examples=3)
|
| 499 |
+
|
| 500 |
+
if examples:
|
| 501 |
+
print(f"[MISC] Selected {len(examples)} examples:")
|
| 502 |
+
for i, example in enumerate(examples, 1):
|
| 503 |
+
category = example.get('category', 'Unknown')
|
| 504 |
+
question = example.get('question', '')[:60]
|
| 505 |
+
print(f" {i}. [{category}] {question}...")
|
| 506 |
+
else:
|
| 507 |
+
print("[MISC]️ No examples selected")
|
| 508 |
+
|
| 509 |
+
print("\n[MISC] Few-shot selection test completed!")
|
| 510 |
+
|
| 511 |
+
except Exception as e:
|
| 512 |
+
print(f"[MISC] Error testing few-shot system: {e}")
|
| 513 |
+
import traceback
|
| 514 |
+
traceback.print_exc()
|
| 515 |
+
|
| 516 |
+
asyncio.run(test_few_shot())
|
| 517 |
+
sys.exit(0)
|
| 518 |
+
|
| 519 |
+
elif args.test_transformation:
|
| 520 |
+
# Test AI transformation
|
| 521 |
+
print("\n[BOT] Testing AI-Powered Query Transformation...")
|
| 522 |
+
print("=" * 70)
|
| 523 |
+
|
| 524 |
+
async def test_transformation():
|
| 525 |
+
try:
|
| 526 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 527 |
+
|
| 528 |
+
# Initialize the system
|
| 529 |
+
print("Initializing enhanced regulatory system...")
|
| 530 |
+
app = EnhancedArabicChatbot()
|
| 531 |
+
|
| 532 |
+
# Test transformation
|
| 533 |
+
test_query = "شروط التوريق"
|
| 534 |
+
print(f"\n[NOTE] Original Query: {test_query}")
|
| 535 |
+
|
| 536 |
+
if hasattr(app, '_transform_query_with_ai'):
|
| 537 |
+
enhanced_query = await app._transform_query_with_ai(test_query)
|
| 538 |
+
print(f"[TARGET] Enhanced Query: {enhanced_query}")
|
| 539 |
+
|
| 540 |
+
if enhanced_query != test_query:
|
| 541 |
+
print("[MISC] Query transformation successful!")
|
| 542 |
+
else:
|
| 543 |
+
print("[INFO] Query transformation returned original query")
|
| 544 |
+
else:
|
| 545 |
+
print("[MISC]️ Query transformation method not found")
|
| 546 |
+
|
| 547 |
+
except Exception as e:
|
| 548 |
+
print(f"[MISC] Error testing transformation: {e}")
|
| 549 |
+
import traceback
|
| 550 |
+
traceback.print_exc()
|
| 551 |
+
|
| 552 |
+
asyncio.run(test_transformation())
|
| 553 |
+
sys.exit(0)
|
| 554 |
+
|
| 555 |
+
elif args.benchmark_enhancement:
|
| 556 |
+
# Benchmark performance
|
| 557 |
+
print("\n[STATS] Benchmarking Query Enhancement Performance...")
|
| 558 |
+
print("=" * 70)
|
| 559 |
+
|
| 560 |
+
async def benchmark():
|
| 561 |
+
try:
|
| 562 |
+
import time
|
| 563 |
+
from src.core.few_shot_selector import FewShotExampleSelector
|
| 564 |
+
|
| 565 |
+
# Initialize selector
|
| 566 |
+
selector = FewShotExampleSelector("qa_knowledge_base.json")
|
| 567 |
+
await selector.initialize()
|
| 568 |
+
|
| 569 |
+
# Benchmark queries
|
| 570 |
+
test_queries = [
|
| 571 |
+
"ما هي شروط التوريق المالي؟",
|
| 572 |
+
"كيف يتم التعامل مع المخالفات التأديبية؟",
|
| 573 |
+
"ما هي متطلبات إدارة المخاطر؟",
|
| 574 |
+
"شروط فتح الحساب المصرفي",
|
| 575 |
+
"عقوبات مجلس التأديب"
|
| 576 |
+
]
|
| 577 |
+
|
| 578 |
+
total_time = 0
|
| 579 |
+
successful_selections = 0
|
| 580 |
+
|
| 581 |
+
for query in test_queries:
|
| 582 |
+
start_time = time.time()
|
| 583 |
+
examples = await selector.select_examples(query, max_examples=3)
|
| 584 |
+
end_time = time.time()
|
| 585 |
+
|
| 586 |
+
query_time = end_time - start_time
|
| 587 |
+
total_time += query_time
|
| 588 |
+
|
| 589 |
+
if examples:
|
| 590 |
+
successful_selections += 1
|
| 591 |
+
|
| 592 |
+
print(f"[NOTE] {query[:30]}... -> {len(examples) if examples else 0} examples ({query_time:.3f}s)")
|
| 593 |
+
|
| 594 |
+
avg_time = total_time / len(test_queries)
|
| 595 |
+
success_rate = (successful_selections / len(test_queries)) * 100
|
| 596 |
+
|
| 597 |
+
print(f"\n[STATS] Benchmark Results:")
|
| 598 |
+
print(f" Average Response Time: {avg_time:.3f} seconds")
|
| 599 |
+
print(f" Success Rate: {success_rate:.1f}%")
|
| 600 |
+
print(f" Total Queries: {len(test_queries)}")
|
| 601 |
+
|
| 602 |
+
if avg_time < 3.0:
|
| 603 |
+
print("[MISC] Performance target met (<3s response time)")
|
| 604 |
+
else:
|
| 605 |
+
print("[MISC]️ Performance target not met (>3s response time)")
|
| 606 |
+
|
| 607 |
+
except Exception as e:
|
| 608 |
+
print(f"[MISC] Error benchmarking: {e}")
|
| 609 |
+
import traceback
|
| 610 |
+
traceback.print_exc()
|
| 611 |
+
|
| 612 |
+
asyncio.run(benchmark())
|
| 613 |
+
sys.exit(0)
|
| 614 |
+
|
| 615 |
+
elif args.reindex:
|
| 616 |
+
# Reindex mode
|
| 617 |
+
print("\n[RELOAD] Forcing reindex of all documents...")
|
| 618 |
+
|
| 619 |
+
async def reindex():
|
| 620 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 621 |
+
print("Initializing enhanced regulatory system for reindexing...")
|
| 622 |
+
app = EnhancedArabicChatbot()
|
| 623 |
+
# Force reindex through knowledge base
|
| 624 |
+
print("Starting document reindexing with text-embedding-3-large...")
|
| 625 |
+
result = await app.knowledge_base.scan_and_index(force_reindex=True)
|
| 626 |
+
print(f"\n[MISC] Reindexing complete: {result}")
|
| 627 |
+
|
| 628 |
+
asyncio.run(reindex())
|
| 629 |
+
sys.exit(0)
|
| 630 |
+
|
| 631 |
+
elif args.clear:
|
| 632 |
+
# Clear knowledge base
|
| 633 |
+
print("\n[CLEAR] Clearing knowledge base...")
|
| 634 |
+
|
| 635 |
+
async def clear():
|
| 636 |
+
from src.core.knowledge_base import KnowledgeBase
|
| 637 |
+
config = {} # Will use defaults
|
| 638 |
+
kb = KnowledgeBase(config)
|
| 639 |
+
success = await kb.clear_index()
|
| 640 |
+
if success:
|
| 641 |
+
print("\n[MISC] Knowledge base cleared successfully")
|
| 642 |
+
else:
|
| 643 |
+
print("\n[MISC] Failed to clear knowledge base")
|
| 644 |
+
|
| 645 |
+
asyncio.run(clear())
|
| 646 |
+
sys.exit(0)
|
| 647 |
+
|
| 648 |
+
elif args.test_chunking:
|
| 649 |
+
# Test chunking strategy
|
| 650 |
+
print(f"\n[TOOL] Testing {args.chunking} chunking strategy...")
|
| 651 |
+
print("=" * 70)
|
| 652 |
+
|
| 653 |
+
async def test_chunking():
|
| 654 |
+
# Import the appropriate chunker
|
| 655 |
+
if args.chunking == "semantic":
|
| 656 |
+
try:
|
| 657 |
+
from semantic_chunking import AdvancedSemanticChunker
|
| 658 |
+
chunker = AdvancedSemanticChunker(
|
| 659 |
+
min_chunk_size=500,
|
| 660 |
+
max_chunk_size=2000,
|
| 661 |
+
similarity_threshold=0.75,
|
| 662 |
+
overlap_ratio=0.15
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
# Sample Arabic legal text for testing
|
| 666 |
+
sample_text = """
|
| 667 |
+
الباب الأول: التوريق المالي
|
| 668 |
+
|
| 669 |
+
المادة 1: تعريف التوريق
|
| 670 |
+
التوريق هو عملية تحويل الأصول المالية إلى أوراق مالية قابلة للتداول.
|
| 671 |
+
يشمل ذلك الديون والحقوق المالية المختلفة التي تولد تدفقات نقدية منتظمة.
|
| 672 |
+
|
| 673 |
+
المادة 2: شروط التوريق
|
| 674 |
+
يجب أن تكون الأصول المراد توريقها ذات تدفقات نقدية منتظمة ومتوقعة.
|
| 675 |
+
يشترط موافقة البنك المركزي على عملية التوريق قبل التنفيذ.
|
| 676 |
+
تخضع جميع عمليات التوريق للرقابة المستمرة من الجهات المختصة.
|
| 677 |
+
|
| 678 |
+
المادة 3: الضمانات والحماية
|
| 679 |
+
يجب توفير ضمانات كافية لحماية حقوق المستثمرين في الأوراق المالية المصدرة.
|
| 680 |
+
تشمل الضمانات التأمين ضد المخاطر والاحتياطيات النقدية الكافية.
|
| 681 |
+
"""
|
| 682 |
+
|
| 683 |
+
print(f"Testing on sample Arabic legal document ({len(sample_text)} chars)")
|
| 684 |
+
chunks = chunker.chunk_document(sample_text, add_overlap=True)
|
| 685 |
+
|
| 686 |
+
print(f"\n[MISC] Created {len(chunks)} semantic chunks:")
|
| 687 |
+
for i, chunk in enumerate(chunks):
|
| 688 |
+
print(f"\nChunk {i+1}:")
|
| 689 |
+
print(f" Type: {chunk.chunk_type}")
|
| 690 |
+
print(f" Size: {len(chunk.content)} chars")
|
| 691 |
+
print(f" Preview: {chunk.content[:150]}...")
|
| 692 |
+
|
| 693 |
+
except ImportError:
|
| 694 |
+
print("[MISC] Semantic chunking module not available")
|
| 695 |
+
print(" Using traditional fixed-size chunking")
|
| 696 |
+
|
| 697 |
+
elif args.chunking == "late":
|
| 698 |
+
print("Late chunking requires long-context models.")
|
| 699 |
+
print("Please ensure you have the required models installed.")
|
| 700 |
+
try:
|
| 701 |
+
from late_chunking import OptimalChunkingStrategy
|
| 702 |
+
processor = OptimalChunkingStrategy()
|
| 703 |
+
print("Late chunking test would go here...")
|
| 704 |
+
except ImportError:
|
| 705 |
+
print("[MISC] Late chunking module not available")
|
| 706 |
+
|
| 707 |
+
elif args.chunking == "hierarchical":
|
| 708 |
+
try:
|
| 709 |
+
from semantic_chunking import AdvancedSemanticChunker, HierarchicalChunker
|
| 710 |
+
semantic_chunker = AdvancedSemanticChunker()
|
| 711 |
+
chunker = HierarchicalChunker(semantic_chunker)
|
| 712 |
+
print("Hierarchical chunking test would go here...")
|
| 713 |
+
except ImportError:
|
| 714 |
+
print("[MISC] Hierarchical chunking modules not available")
|
| 715 |
+
|
| 716 |
+
else: # fixed
|
| 717 |
+
print("Using traditional fixed-size chunking (current method)")
|
| 718 |
+
print("Chunk size: 800 chars, Overlap: 200 chars")
|
| 719 |
+
|
| 720 |
+
print("\n" + "=" * 70)
|
| 721 |
+
print("[MISC] Chunking test complete!")
|
| 722 |
+
|
| 723 |
+
asyncio.run(test_chunking())
|
| 724 |
+
sys.exit(0)
|
| 725 |
+
|
| 726 |
+
elif args.query:
|
| 727 |
+
# Query test mode
|
| 728 |
+
print(f"\n[SEARCH] Testing query through enhanced regulatory system...")
|
| 729 |
+
print("=" * 70)
|
| 730 |
+
|
| 731 |
+
async def test_query():
|
| 732 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 733 |
+
|
| 734 |
+
# Initialize the system
|
| 735 |
+
print("Initializing enhanced regulatory system...")
|
| 736 |
+
app = EnhancedArabicChatbot()
|
| 737 |
+
|
| 738 |
+
# Test the query
|
| 739 |
+
print(f"\n[NOTE] Query: {args.query}")
|
| 740 |
+
print("-" * 40)
|
| 741 |
+
|
| 742 |
+
try:
|
| 743 |
+
# Process the query
|
| 744 |
+
history = []
|
| 745 |
+
result_history, status = await app.process_query(args.query, history)
|
| 746 |
+
|
| 747 |
+
# Display results
|
| 748 |
+
if result_history and len(result_history) >= 2:
|
| 749 |
+
response = result_history[-1]['content']
|
| 750 |
+
print(f"Status: {status}")
|
| 751 |
+
print(f"Response:\n{response}")
|
| 752 |
+
else:
|
| 753 |
+
print(f"Status: {status}")
|
| 754 |
+
print("No response generated")
|
| 755 |
+
|
| 756 |
+
except Exception as e:
|
| 757 |
+
print(f"[MISC] Error processing query: {e}")
|
| 758 |
+
import traceback
|
| 759 |
+
traceback.print_exc()
|
| 760 |
+
|
| 761 |
+
print("\n" + "=" * 70)
|
| 762 |
+
print("[MISC] Query test complete!")
|
| 763 |
+
|
| 764 |
+
asyncio.run(test_query())
|
| 765 |
+
sys.exit(0)
|
| 766 |
+
|
| 767 |
+
else:
|
| 768 |
+
# Normal operation with selected chunking strategy
|
| 769 |
+
if args.chunking != "fixed":
|
| 770 |
+
print(f"\n[TOOL] Using {args.chunking} chunking strategy")
|
| 771 |
+
print("This provides better context preservation for Arabic documents")
|
| 772 |
+
main()
|
enhanced_message_classifier.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Enhanced Message Classification System for Arabic Regulatory Chatbot
|
| 3 |
+
Improves differentiation between casual chat and regulatory queries.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import re
|
| 7 |
+
import logging
|
| 8 |
+
from typing import Dict, List, Tuple, Optional
|
| 9 |
+
from enum import Enum
|
| 10 |
+
from dataclasses import dataclass
|
| 11 |
+
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class ClassificationConfidence(Enum):
|
| 16 |
+
"""Classification confidence levels."""
|
| 17 |
+
HIGH = "high" # 0.8+
|
| 18 |
+
MEDIUM = "medium" # 0.5-0.8
|
| 19 |
+
LOW = "low" # 0.3-0.5
|
| 20 |
+
VERY_LOW = "very_low" # <0.3
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@dataclass
|
| 24 |
+
class ClassificationResult:
|
| 25 |
+
"""Result of message classification."""
|
| 26 |
+
message_type: str
|
| 27 |
+
confidence: float
|
| 28 |
+
confidence_level: ClassificationConfidence
|
| 29 |
+
reasoning: str
|
| 30 |
+
alternative_types: List[Tuple[str, float]]
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class EnhancedMessageClassifier:
|
| 34 |
+
"""
|
| 35 |
+
Enhanced message classifier with improved casual vs regulatory detection.
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
def __init__(self):
|
| 39 |
+
self.setup_patterns()
|
| 40 |
+
self.setup_weights()
|
| 41 |
+
|
| 42 |
+
def setup_patterns(self):
|
| 43 |
+
"""Setup comprehensive classification patterns."""
|
| 44 |
+
|
| 45 |
+
# === GREETING PATTERNS ===
|
| 46 |
+
self.greeting_patterns = {
|
| 47 |
+
'arabic': [
|
| 48 |
+
r'(^|\s)(مرحبا|أهلا|السلام عليكم|سلام عليكم|اهلا وسهلا|مرحباً|أهلاً)(\s|$)',
|
| 49 |
+
r'(^|\s)(كيف حالك|كيف الحال|كيف الأحوال|شلونك|كيفك)(\s|$)',
|
| 50 |
+
r'(^|\s)(صباح الخير|مساء الخير|تحية طيبة)(\s|$)',
|
| 51 |
+
r'(^|\s)(هلا|هالو|هلو)(\s|$)',
|
| 52 |
+
],
|
| 53 |
+
'english': [
|
| 54 |
+
r'(^|\s)(hello|hi|hey|greetings|good morning|good afternoon|good evening)(\s|$)',
|
| 55 |
+
r'(^|\s)(how are you|how do you do|what\'s up|how\'s it going)(\s|$)',
|
| 56 |
+
r'(^|\s)(hola|bonjour|guten tag)(\s|$)',
|
| 57 |
+
]
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
# === ENHANCED CASUAL PATTERNS ===
|
| 61 |
+
self.casual_patterns = {
|
| 62 |
+
'time_queries': [
|
| 63 |
+
r'(what|ما|كم).*(time|وقت|ساعة)',
|
| 64 |
+
r'(what time|كم الساعة|ما الوقت)',
|
| 65 |
+
r'(current time|الوقت الحالي)',
|
| 66 |
+
r'(time.*now|الوقت.*الآن)',
|
| 67 |
+
],
|
| 68 |
+
'weather_queries': [
|
| 69 |
+
r'(weather|طقس|جو)',
|
| 70 |
+
r'(temperature|درجة حرارة)',
|
| 71 |
+
r'(rain|snow|sunny|مطر|شمس|غائم)',
|
| 72 |
+
r'(how.*weather|كيف.*الطقس)',
|
| 73 |
+
],
|
| 74 |
+
'personal_inquiries': [
|
| 75 |
+
r'(how.*day|كيف.*اليوم)',
|
| 76 |
+
r'(how.*doing|كيف.*حالك)',
|
| 77 |
+
r'(how.*feeling|كيف.*شعورك)',
|
| 78 |
+
r'(what.*up|شو الأخبار)',
|
| 79 |
+
r'(how.*going|كيف.*الأمور)',
|
| 80 |
+
],
|
| 81 |
+
'general_questions': [
|
| 82 |
+
r'(tell me about yourself|أخبرني عن نفسك)',
|
| 83 |
+
r'(who are you|من أنت)',
|
| 84 |
+
r'(what.*you do|ماذا تفعل)',
|
| 85 |
+
r'(can you help|تقدر تساعد)',
|
| 86 |
+
r'(what.*capabilities|ما قدراتك)',
|
| 87 |
+
],
|
| 88 |
+
'social_interactions': [
|
| 89 |
+
r'(thank.*you|شكرا|شكراً|تسلم)',
|
| 90 |
+
r'(you\'re welcome|عفواً|لا شكر على واجب)',
|
| 91 |
+
r'(goodbye|bye|وداع|باي|مع السلامة)',
|
| 92 |
+
r'(see you|أراك لاحقاً)',
|
| 93 |
+
r'(nice.*talk|كان من الجميل)',
|
| 94 |
+
],
|
| 95 |
+
'casual_requests': [
|
| 96 |
+
r'(tell.*joke|احك نكتة)',
|
| 97 |
+
r'(how.*weekend|كيف.*الأسبوع)',
|
| 98 |
+
r'(favorite.*color|اللون المفضل)',
|
| 99 |
+
r'(what.*think|ما رأيك)',
|
| 100 |
+
]
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
# === REGULATORY PATTERNS (More Specific) ===
|
| 104 |
+
self.regulatory_patterns = {
|
| 105 |
+
'licensing_keywords': [
|
| 106 |
+
r'(ترخيص|license|licensing|permit|تصريح)',
|
| 107 |
+
r'(تسجيل|registration|register)',
|
| 108 |
+
r'(موافقة|approval|اعتماد)',
|
| 109 |
+
],
|
| 110 |
+
'compliance_keywords': [
|
| 111 |
+
r'(امتثال|compliance|comply)',
|
| 112 |
+
r'(متطلبات|requirements|شروط)',
|
| 113 |
+
r'(ضوابط|regulations|أنظمة|لوائح)',
|
| 114 |
+
r'(قوانين|laws|legislation)',
|
| 115 |
+
],
|
| 116 |
+
'financial_keywords': [
|
| 117 |
+
r'(مصرف|بنك|bank|banking|مصرفي)',
|
| 118 |
+
r'(ائتمان|credit|قرض|loan)',
|
| 119 |
+
r'(استثمار|investment|توريق)',
|
| 120 |
+
r'(أسواق المال|capital markets|securities)',
|
| 121 |
+
r'(صندوق|fund|محفظة|portfolio)',
|
| 122 |
+
],
|
| 123 |
+
'authorities_keywords': [
|
| 124 |
+
r'(البنك ا��مركزي|central bank|cbk)',
|
| 125 |
+
r'(هيئة أسواق المال|capital markets authority|cma)',
|
| 126 |
+
r'(مؤسسة النقد|monetary authority)',
|
| 127 |
+
],
|
| 128 |
+
'violations_keywords': [
|
| 129 |
+
r'(مخالفة|violation|infringement)',
|
| 130 |
+
r'(عقوبة|penalty|fine|جزاء)',
|
| 131 |
+
r'(تأديب|disciplinary|تأديبي)',
|
| 132 |
+
r'(محاكمة|hearing|جلسة)',
|
| 133 |
+
],
|
| 134 |
+
'aml_keywords': [
|
| 135 |
+
r'(غسل الأموال|money laundering|aml)',
|
| 136 |
+
r'(مشبوه|suspicious|مشكوك)',
|
| 137 |
+
r'(kyc|know your customer|اعرف عميلك)',
|
| 138 |
+
r'(بيانات العميل|customer data)',
|
| 139 |
+
]
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
# === CONTEXT CLUES ===
|
| 143 |
+
self.context_indicators = {
|
| 144 |
+
'casual_indicators': [
|
| 145 |
+
r'^(just|فقط|بس)',
|
| 146 |
+
r'(curious|فضول)',
|
| 147 |
+
r'(wondering|أتساءل)',
|
| 148 |
+
r'(by the way|بالمناسبة)',
|
| 149 |
+
r'(quick question|سؤال سريع)',
|
| 150 |
+
],
|
| 151 |
+
'regulatory_indicators': [
|
| 152 |
+
r'(according to|وفقاً ل|حسب)',
|
| 153 |
+
r'(article|مادة|فقرة)',
|
| 154 |
+
r'(section|قسم|باب)',
|
| 155 |
+
r'(regulation number|رقم اللائحة)',
|
| 156 |
+
r'(what are the|ما هي)',
|
| 157 |
+
r'(requirements for|متطلبات)',
|
| 158 |
+
]
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
def setup_weights(self):
|
| 162 |
+
"""Setup scoring weights for different pattern categories."""
|
| 163 |
+
self.weights = {
|
| 164 |
+
'greeting': 1.0,
|
| 165 |
+
'casual_time': 0.9,
|
| 166 |
+
'casual_weather': 0.9,
|
| 167 |
+
'casual_personal': 0.8,
|
| 168 |
+
'casual_general': 0.7,
|
| 169 |
+
'casual_social': 0.8,
|
| 170 |
+
'casual_requests': 0.7,
|
| 171 |
+
'regulatory_licensing': 0.9,
|
| 172 |
+
'regulatory_compliance': 0.9,
|
| 173 |
+
'regulatory_financial': 0.8,
|
| 174 |
+
'regulatory_authorities': 1.0,
|
| 175 |
+
'regulatory_violations': 0.9,
|
| 176 |
+
'regulatory_aml': 0.9,
|
| 177 |
+
'context_casual': 0.3,
|
| 178 |
+
'context_regulatory': 0.4,
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
def classify_message(self, message: str) -> ClassificationResult:
|
| 182 |
+
"""
|
| 183 |
+
Classify message with confidence scoring and reasoning.
|
| 184 |
+
"""
|
| 185 |
+
message_lower = message.lower().strip()
|
| 186 |
+
|
| 187 |
+
# Score different categories
|
| 188 |
+
scores = {
|
| 189 |
+
'greeting': self._score_greeting(message_lower),
|
| 190 |
+
'casual': self._score_casual(message_lower),
|
| 191 |
+
'regulatory': self._score_regulatory(message_lower)
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
# Determine best classification
|
| 195 |
+
best_type = max(scores, key=scores.get)
|
| 196 |
+
best_score = scores[best_type]
|
| 197 |
+
|
| 198 |
+
# Get confidence level
|
| 199 |
+
confidence_level = self._get_confidence_level(best_score)
|
| 200 |
+
|
| 201 |
+
# Generate reasoning
|
| 202 |
+
reasoning = self._generate_reasoning(message_lower, scores, best_type)
|
| 203 |
+
|
| 204 |
+
# Get alternative classifications
|
| 205 |
+
sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
|
| 206 |
+
alternatives = [(t, s) for t, s in sorted_scores[1:] if s > 0.1]
|
| 207 |
+
|
| 208 |
+
# Handle ambiguous cases with smart defaults
|
| 209 |
+
if confidence_level == ClassificationConfidence.VERY_LOW:
|
| 210 |
+
# If all scores are very low, check message characteristics
|
| 211 |
+
if len(message.split()) <= 3 and not any(char in message for char in '?!.'):
|
| 212 |
+
# Short message without punctuation - likely casual
|
| 213 |
+
best_type = 'casual'
|
| 214 |
+
best_score = 0.4
|
| 215 |
+
confidence_level = ClassificationConfidence.LOW
|
| 216 |
+
reasoning = "Short informal message defaulted to casual"
|
| 217 |
+
elif '?' in message:
|
| 218 |
+
# Question format - analyze further
|
| 219 |
+
if any(word in message_lower for word in ['what', 'how', 'when', 'where', 'why', 'ماذا', 'كيف', 'متى', 'أين', 'لماذا']):
|
| 220 |
+
best_type = 'casual'
|
| 221 |
+
best_score = 0.4
|
| 222 |
+
confidence_level = ClassificationConfidence.LOW
|
| 223 |
+
reasoning = "Question format defaulted to casual conversation"
|
| 224 |
+
|
| 225 |
+
return ClassificationResult(
|
| 226 |
+
message_type=best_type,
|
| 227 |
+
confidence=best_score,
|
| 228 |
+
confidence_level=confidence_level,
|
| 229 |
+
reasoning=reasoning,
|
| 230 |
+
alternative_types=alternatives
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
def _score_greeting(self, message: str) -> float:
|
| 234 |
+
"""Score greeting likelihood."""
|
| 235 |
+
score = 0.0
|
| 236 |
+
|
| 237 |
+
for lang, patterns in self.greeting_patterns.items():
|
| 238 |
+
for pattern in patterns:
|
| 239 |
+
if re.search(pattern, message, re.IGNORECASE):
|
| 240 |
+
score = max(score, self.weights['greeting'])
|
| 241 |
+
|
| 242 |
+
return min(score, 1.0)
|
| 243 |
+
|
| 244 |
+
def _score_casual(self, message: str) -> float:
|
| 245 |
+
"""Score casual conversation likelihood."""
|
| 246 |
+
score = 0.0
|
| 247 |
+
|
| 248 |
+
# Check casual patterns
|
| 249 |
+
for category, patterns in self.casual_patterns.items():
|
| 250 |
+
category_weight = self.weights.get(f'casual_{category.split("_")[0]}', 0.7)
|
| 251 |
+
for pattern in patterns:
|
| 252 |
+
if re.search(pattern, message, re.IGNORECASE):
|
| 253 |
+
score = max(score, category_weight)
|
| 254 |
+
|
| 255 |
+
# Add context indicators
|
| 256 |
+
for pattern in self.context_indicators['casual_indicators']:
|
| 257 |
+
if re.search(pattern, message, re.IGNORECASE):
|
| 258 |
+
score += self.weights['context_casual']
|
| 259 |
+
|
| 260 |
+
# Boost for common casual question words
|
| 261 |
+
casual_words = ['how', 'what', 'when', 'where', 'كيف', 'ماذا', 'متى', 'أين']
|
| 262 |
+
if any(word in message for word in casual_words) and len(message.split()) <= 6:
|
| 263 |
+
score += 0.2
|
| 264 |
+
|
| 265 |
+
return min(score, 1.0)
|
| 266 |
+
|
| 267 |
+
def _score_regulatory(self, message: str) -> float:
|
| 268 |
+
"""Score regulatory query likelihood."""
|
| 269 |
+
score = 0.0
|
| 270 |
+
|
| 271 |
+
# Check regulatory patterns
|
| 272 |
+
for category, patterns in self.regulatory_patterns.items():
|
| 273 |
+
category_weight = self.weights.get(f'regulatory_{category.split("_")[0]}', 0.8)
|
| 274 |
+
for pattern in patterns:
|
| 275 |
+
if re.search(pattern, message, re.IGNORECASE):
|
| 276 |
+
score = max(score, category_weight)
|
| 277 |
+
|
| 278 |
+
# Add context indicators
|
| 279 |
+
for pattern in self.context_indicators['regulatory_indicators']:
|
| 280 |
+
if re.search(pattern, message, re.IGNORECASE):
|
| 281 |
+
score += self.weights['context_regulatory']
|
| 282 |
+
|
| 283 |
+
# Penalty for very short messages
|
| 284 |
+
if len(message.split()) <= 3:
|
| 285 |
+
score *= 0.7
|
| 286 |
+
|
| 287 |
+
return min(score, 1.0)
|
| 288 |
+
|
| 289 |
+
def _get_confidence_level(self, score: float) -> ClassificationConfidence:
|
| 290 |
+
"""Convert score to confidence level."""
|
| 291 |
+
if score >= 0.8:
|
| 292 |
+
return ClassificationConfidence.HIGH
|
| 293 |
+
elif score >= 0.5:
|
| 294 |
+
return ClassificationConfidence.MEDIUM
|
| 295 |
+
elif score >= 0.3:
|
| 296 |
+
return ClassificationConfidence.LOW
|
| 297 |
+
else:
|
| 298 |
+
return ClassificationConfidence.VERY_LOW
|
| 299 |
+
|
| 300 |
+
def _generate_reasoning(self, message: str, scores: Dict[str, float], best_type: str) -> str:
|
| 301 |
+
"""Generate human-readable reasoning for classification."""
|
| 302 |
+
reasoning_parts = []
|
| 303 |
+
|
| 304 |
+
if best_type == 'greeting':
|
| 305 |
+
reasoning_parts.append("Contains greeting patterns")
|
| 306 |
+
elif best_type == 'casual':
|
| 307 |
+
if any(word in message for word in ['time', 'وقت', 'ساعة']):
|
| 308 |
+
reasoning_parts.append("Time-related inquiry")
|
| 309 |
+
elif any(word in message for word in ['weather', 'طقس']):
|
| 310 |
+
reasoning_parts.append("Weather inquiry")
|
| 311 |
+
elif any(word in message for word in ['how', 'كيف']):
|
| 312 |
+
reasoning_parts.append("Personal/casual question")
|
| 313 |
+
else:
|
| 314 |
+
reasoning_parts.append("General casual conversation")
|
| 315 |
+
elif best_type == 'regulatory':
|
| 316 |
+
if any(word in message for word in ['bank', 'مصرف', 'بنك']):
|
| 317 |
+
reasoning_parts.append("Banking-related query")
|
| 318 |
+
elif any(word in message for word in ['license', 'ترخيص']):
|
| 319 |
+
reasoning_parts.append("Licensing inquiry")
|
| 320 |
+
elif any(word in message for word in ['compliance', 'امتثال']):
|
| 321 |
+
reasoning_parts.append("Compliance question")
|
| 322 |
+
else:
|
| 323 |
+
reasoning_parts.append("Regulatory content detected")
|
| 324 |
+
|
| 325 |
+
# Add confidence information
|
| 326 |
+
if scores[best_type] < 0.5:
|
| 327 |
+
reasoning_parts.append("(Low confidence - ambiguous)")
|
| 328 |
+
|
| 329 |
+
return "; ".join(reasoning_parts)
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
# Test the enhanced classifier
|
| 333 |
+
if __name__ == "__main__":
|
| 334 |
+
classifier = EnhancedMessageClassifier()
|
| 335 |
+
|
| 336 |
+
test_cases = [
|
| 337 |
+
"Hello",
|
| 338 |
+
"How is the day?",
|
| 339 |
+
"What is the time right now?",
|
| 340 |
+
"How are you doing today?",
|
| 341 |
+
"What are the banking license requirements?",
|
| 342 |
+
"Tell me about compliance regulations",
|
| 343 |
+
"مرحبا",
|
| 344 |
+
"كيف حالك اليوم؟",
|
| 345 |
+
"ما الوقت الآن؟",
|
| 346 |
+
"ما هي متطلبات الترخيص المصرفي؟"
|
| 347 |
+
]
|
| 348 |
+
|
| 349 |
+
print("Enhanced Message Classification Test Results:")
|
| 350 |
+
print("=" * 60)
|
| 351 |
+
|
| 352 |
+
for message in test_cases:
|
| 353 |
+
result = classifier.classify_message(message)
|
| 354 |
+
print(f"\nMessage: \"{message}\"")
|
| 355 |
+
print(f"Classification: {result.message_type}")
|
| 356 |
+
print(f"Confidence: {result.confidence:.2f} ({result.confidence_level.value})")
|
| 357 |
+
print(f"Reasoning: {result.reasoning}")
|
| 358 |
+
if result.alternative_types:
|
| 359 |
+
alts = ", ".join([f"{t}({s:.2f})" for t, s in result.alternative_types[:2]])
|
| 360 |
+
print(f"Alternatives: {alts}")
|
enhanced_pdf_processor.py
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Enhanced PDF processor with OCR support for image-based PDFs.
|
| 4 |
+
Handles both text-based and image-based (scanned) PDFs.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import sys
|
| 8 |
+
import os
|
| 9 |
+
import traceback
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
from typing import List, Dict, Optional, Union, Tuple
|
| 12 |
+
import asyncio
|
| 13 |
+
import logging
|
| 14 |
+
|
| 15 |
+
def test_ocr_availability():
|
| 16 |
+
"""Test if OCR libraries are available."""
|
| 17 |
+
ocr_available = {
|
| 18 |
+
'pytesseract': False,
|
| 19 |
+
'easyocr': False,
|
| 20 |
+
'paddleocr': False
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
# Test pytesseract + tesseract
|
| 24 |
+
try:
|
| 25 |
+
import pytesseract
|
| 26 |
+
from PIL import Image
|
| 27 |
+
|
| 28 |
+
# Try to get tesseract version to verify installation
|
| 29 |
+
version = pytesseract.get_tesseract_version()
|
| 30 |
+
ocr_available['pytesseract'] = True
|
| 31 |
+
print(f" Pytesseract available - Tesseract version: {version}")
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f" Pytesseract not available: {e}")
|
| 34 |
+
|
| 35 |
+
# Test EasyOCR
|
| 36 |
+
try:
|
| 37 |
+
import easyocr
|
| 38 |
+
ocr_available['easyocr'] = True
|
| 39 |
+
print(f" EasyOCR available")
|
| 40 |
+
except Exception as e:
|
| 41 |
+
print(f" EasyOCR not available: {e}")
|
| 42 |
+
|
| 43 |
+
# Test PaddleOCR
|
| 44 |
+
try:
|
| 45 |
+
import paddleocr
|
| 46 |
+
ocr_available['paddleocr'] = True
|
| 47 |
+
print(f" PaddleOCR available")
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(f" PaddleOCR not available: {e}")
|
| 50 |
+
|
| 51 |
+
return ocr_available
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def extract_with_ocr_pytesseract(file_path: Path) -> List[Dict[str, any]]:
|
| 55 |
+
"""Extract text using PyMuPDF for images + pytesseract for OCR."""
|
| 56 |
+
try:
|
| 57 |
+
import fitz
|
| 58 |
+
import pytesseract
|
| 59 |
+
from PIL import Image
|
| 60 |
+
import io
|
| 61 |
+
|
| 62 |
+
print(f" Using PyMuPDF + pytesseract OCR")
|
| 63 |
+
|
| 64 |
+
doc = fitz.open(str(file_path))
|
| 65 |
+
pages = []
|
| 66 |
+
|
| 67 |
+
for page_num in range(len(doc)):
|
| 68 |
+
page = doc.load_page(page_num)
|
| 69 |
+
|
| 70 |
+
# First try regular text extraction
|
| 71 |
+
text = page.get_text()
|
| 72 |
+
|
| 73 |
+
# If no text found, try OCR on page images
|
| 74 |
+
if not text.strip():
|
| 75 |
+
# Get page as image
|
| 76 |
+
mat = fitz.Matrix(2.0, 2.0) # 2x zoom for better OCR
|
| 77 |
+
pix = page.get_pixmap(matrix=mat)
|
| 78 |
+
|
| 79 |
+
# Convert to PIL Image
|
| 80 |
+
img_data = pix.tobytes("png")
|
| 81 |
+
image = Image.open(io.BytesIO(img_data))
|
| 82 |
+
|
| 83 |
+
# Perform OCR with Arabic support
|
| 84 |
+
try:
|
| 85 |
+
# Configure for Arabic + English
|
| 86 |
+
text = pytesseract.image_to_string(
|
| 87 |
+
image,
|
| 88 |
+
lang='ara+eng',
|
| 89 |
+
config='--oem 3 --psm 6'
|
| 90 |
+
)
|
| 91 |
+
print(f" OCR extracted {len(text)} characters from page {page_num + 1}")
|
| 92 |
+
except Exception as ocr_error:
|
| 93 |
+
print(f" OCR with Arabic failed: {ocr_error}")
|
| 94 |
+
# Try with just English
|
| 95 |
+
try:
|
| 96 |
+
text = pytesseract.image_to_string(
|
| 97 |
+
image,
|
| 98 |
+
lang='eng',
|
| 99 |
+
config='--oem 3 --psm 6'
|
| 100 |
+
)
|
| 101 |
+
print(f" OCR (English only) extracted {len(text)} characters from page {page_num + 1}")
|
| 102 |
+
except Exception as eng_error:
|
| 103 |
+
print(f" OCR completely failed: {eng_error}")
|
| 104 |
+
text = ""
|
| 105 |
+
|
| 106 |
+
pages.append({
|
| 107 |
+
'content': text,
|
| 108 |
+
'page_number': page_num + 1,
|
| 109 |
+
'extraction_method': 'pymupdf_ocr',
|
| 110 |
+
'metadata': {
|
| 111 |
+
'page_size': page.rect,
|
| 112 |
+
'rotation': page.rotation,
|
| 113 |
+
'used_ocr': len(text.strip()) > 0
|
| 114 |
+
}
|
| 115 |
+
})
|
| 116 |
+
|
| 117 |
+
doc.close()
|
| 118 |
+
return pages
|
| 119 |
+
|
| 120 |
+
except Exception as e:
|
| 121 |
+
raise Exception(f"PyMuPDF + OCR extraction failed: {str(e)}")
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def extract_with_ocr_easyocr(file_path: Path) -> List[Dict[str, any]]:
|
| 125 |
+
"""Extract text using EasyOCR (good for Arabic)."""
|
| 126 |
+
try:
|
| 127 |
+
import fitz
|
| 128 |
+
import easyocr
|
| 129 |
+
import numpy as np
|
| 130 |
+
from PIL import Image
|
| 131 |
+
import io
|
| 132 |
+
|
| 133 |
+
print(f" Using EasyOCR")
|
| 134 |
+
|
| 135 |
+
# Initialize EasyOCR reader with Arabic and English
|
| 136 |
+
reader = easyocr.Reader(['ar', 'en'], gpu=False) # Use CPU
|
| 137 |
+
|
| 138 |
+
doc = fitz.open(str(file_path))
|
| 139 |
+
pages = []
|
| 140 |
+
|
| 141 |
+
for page_num in range(len(doc)):
|
| 142 |
+
page = doc.load_page(page_num)
|
| 143 |
+
|
| 144 |
+
# Get page as image with high resolution
|
| 145 |
+
mat = fitz.Matrix(3.0, 3.0) # 3x zoom for better OCR
|
| 146 |
+
pix = page.get_pixmap(matrix=mat)
|
| 147 |
+
|
| 148 |
+
# Convert to numpy array for EasyOCR
|
| 149 |
+
img_data = pix.tobytes("png")
|
| 150 |
+
image = Image.open(io.BytesIO(img_data))
|
| 151 |
+
img_array = np.array(image)
|
| 152 |
+
|
| 153 |
+
# Perform OCR
|
| 154 |
+
results = reader.readtext(img_array)
|
| 155 |
+
|
| 156 |
+
# Combine all text
|
| 157 |
+
text_parts = []
|
| 158 |
+
for (bbox, text, confidence) in results:
|
| 159 |
+
if confidence > 0.3: # Filter low confidence results
|
| 160 |
+
text_parts.append(text)
|
| 161 |
+
|
| 162 |
+
text = ' '.join(text_parts)
|
| 163 |
+
print(f" EasyOCR extracted {len(text)} characters from page {page_num + 1}")
|
| 164 |
+
|
| 165 |
+
pages.append({
|
| 166 |
+
'content': text,
|
| 167 |
+
'page_number': page_num + 1,
|
| 168 |
+
'extraction_method': 'easyocr',
|
| 169 |
+
'metadata': {
|
| 170 |
+
'page_size': page.rect,
|
| 171 |
+
'rotation': page.rotation,
|
| 172 |
+
'ocr_results': len(results),
|
| 173 |
+
'average_confidence': sum(conf for _, _, conf in results) / len(results) if results else 0
|
| 174 |
+
}
|
| 175 |
+
})
|
| 176 |
+
|
| 177 |
+
doc.close()
|
| 178 |
+
return pages
|
| 179 |
+
|
| 180 |
+
except Exception as e:
|
| 181 |
+
raise Exception(f"EasyOCR extraction failed: {str(e)}")
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def create_ocr_fix_script():
|
| 185 |
+
"""Create a script to install OCR libraries."""
|
| 186 |
+
ocr_install_script = """#!/bin/bash
|
| 187 |
+
# OCR Libraries Installation Script
|
| 188 |
+
|
| 189 |
+
echo "Installing OCR libraries for scanned PDF processing..."
|
| 190 |
+
|
| 191 |
+
# Install Tesseract (Windows using conda/pip)
|
| 192 |
+
echo "1. Installing Tesseract OCR..."
|
| 193 |
+
# For Windows with conda:
|
| 194 |
+
# conda install -c conda-forge tesseract
|
| 195 |
+
# For Windows manually: Download from https://github.com/UB-Mannheim/tesseract/wiki
|
| 196 |
+
|
| 197 |
+
# Install Python OCR libraries
|
| 198 |
+
echo "2. Installing Python OCR libraries..."
|
| 199 |
+
pip install pytesseract easyocr
|
| 200 |
+
|
| 201 |
+
# Arabic language data for Tesseract
|
| 202 |
+
echo "3. Installing Arabic language support..."
|
| 203 |
+
# Tesseract Arabic data should be downloaded automatically
|
| 204 |
+
# Manual download: https://github.com/tesseract-ocr/tessdata
|
| 205 |
+
|
| 206 |
+
echo "OCR installation complete!"
|
| 207 |
+
echo ""
|
| 208 |
+
echo "To test OCR availability, run:"
|
| 209 |
+
echo "python enhanced_pdf_processor.py --test-ocr"
|
| 210 |
+
"""
|
| 211 |
+
|
| 212 |
+
with open("install_ocr.sh", 'w') as f:
|
| 213 |
+
f.write(ocr_install_script)
|
| 214 |
+
|
| 215 |
+
print("Created install_ocr.sh script for OCR library installation")
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def extract_text_with_fallback(file_path: Path) -> Tuple[List[Dict[str, any]], str]:
|
| 219 |
+
"""
|
| 220 |
+
Extract text using multiple methods with intelligent fallback.
|
| 221 |
+
Returns (pages, method_used)
|
| 222 |
+
"""
|
| 223 |
+
|
| 224 |
+
# Test what OCR libraries are available
|
| 225 |
+
ocr_available = test_ocr_availability()
|
| 226 |
+
|
| 227 |
+
# Method 1: Try regular extraction first
|
| 228 |
+
try:
|
| 229 |
+
import fitz
|
| 230 |
+
doc = fitz.open(str(file_path))
|
| 231 |
+
|
| 232 |
+
# Check if document has extractable text
|
| 233 |
+
has_text = False
|
| 234 |
+
for page_num in range(min(3, len(doc))): # Check first 3 pages
|
| 235 |
+
page = doc.load_page(page_num)
|
| 236 |
+
text = page.get_text()
|
| 237 |
+
if text.strip():
|
| 238 |
+
has_text = True
|
| 239 |
+
break
|
| 240 |
+
|
| 241 |
+
doc.close()
|
| 242 |
+
|
| 243 |
+
if has_text:
|
| 244 |
+
print(" Document has extractable text, using regular extraction")
|
| 245 |
+
# Use existing extraction methods (PyMuPDF or pdfplumber)
|
| 246 |
+
doc = fitz.open(str(file_path))
|
| 247 |
+
pages = []
|
| 248 |
+
for page_num in range(len(doc)):
|
| 249 |
+
page = doc.load_page(page_num)
|
| 250 |
+
text = page.get_text()
|
| 251 |
+
pages.append({
|
| 252 |
+
'content': text,
|
| 253 |
+
'page_number': page_num + 1,
|
| 254 |
+
'extraction_method': 'pymupdf_regular',
|
| 255 |
+
'metadata': {'page_size': page.rect, 'rotation': page.rotation}
|
| 256 |
+
})
|
| 257 |
+
doc.close()
|
| 258 |
+
return pages, 'pymupdf_regular'
|
| 259 |
+
|
| 260 |
+
except Exception as e:
|
| 261 |
+
print(f" Regular extraction failed: {e}")
|
| 262 |
+
|
| 263 |
+
# Method 2: OCR methods for image-based PDFs
|
| 264 |
+
print(" Document appears to be image-based, trying OCR methods...")
|
| 265 |
+
|
| 266 |
+
# Try EasyOCR (best for Arabic)
|
| 267 |
+
if ocr_available['easyocr']:
|
| 268 |
+
try:
|
| 269 |
+
pages = extract_with_ocr_easyocr(file_path)
|
| 270 |
+
total_chars = sum(len(page['content']) for page in pages)
|
| 271 |
+
if total_chars > 50: # Minimum reasonable content
|
| 272 |
+
return pages, 'easyocr'
|
| 273 |
+
except Exception as e:
|
| 274 |
+
print(f" EasyOCR failed: {e}")
|
| 275 |
+
|
| 276 |
+
# Try pytesseract
|
| 277 |
+
if ocr_available['pytesseract']:
|
| 278 |
+
try:
|
| 279 |
+
pages = extract_with_ocr_pytesseract(file_path)
|
| 280 |
+
total_chars = sum(len(page['content']) for page in pages)
|
| 281 |
+
if total_chars > 50:
|
| 282 |
+
return pages, 'pytesseract_ocr'
|
| 283 |
+
except Exception as e:
|
| 284 |
+
print(f" Pytesseract OCR failed: {e}")
|
| 285 |
+
|
| 286 |
+
# If no OCR available, provide instructions
|
| 287 |
+
if not any(ocr_available.values()):
|
| 288 |
+
print("\n ERROR: No OCR libraries available!")
|
| 289 |
+
print(" This PDF contains only images and requires OCR processing.")
|
| 290 |
+
print(" To enable OCR support, install one of the following:")
|
| 291 |
+
print(" 1. pip install pytesseract + Install Tesseract OCR")
|
| 292 |
+
print(" 2. pip install easyocr (recommended for Arabic)")
|
| 293 |
+
print(" 3. Run: ./install_ocr.sh (installation script)")
|
| 294 |
+
|
| 295 |
+
create_ocr_fix_script()
|
| 296 |
+
raise Exception("OCR libraries required for image-based PDF")
|
| 297 |
+
|
| 298 |
+
raise Exception("All extraction and OCR methods failed")
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def main():
|
| 302 |
+
if len(sys.argv) < 2:
|
| 303 |
+
print("Usage:")
|
| 304 |
+
print(" python enhanced_pdf_processor.py <pdf_file>")
|
| 305 |
+
print(" python enhanced_pdf_processor.py --test-ocr")
|
| 306 |
+
print("Example: python enhanced_pdf_processor.py 'path/to/scanned.pdf'")
|
| 307 |
+
sys.exit(1)
|
| 308 |
+
|
| 309 |
+
if sys.argv[1] == '--test-ocr':
|
| 310 |
+
print("Testing OCR library availability:")
|
| 311 |
+
print("=" * 40)
|
| 312 |
+
ocr_available = test_ocr_availability()
|
| 313 |
+
|
| 314 |
+
if any(ocr_available.values()):
|
| 315 |
+
print(f"\nOCR Status: READY")
|
| 316 |
+
for lib, available in ocr_available.items():
|
| 317 |
+
status = "Available" if available else "Not Available"
|
| 318 |
+
print(f" {lib}: {status}")
|
| 319 |
+
else:
|
| 320 |
+
print(f"\nOCR Status: NOT READY")
|
| 321 |
+
print("Run install_ocr.sh to install OCR libraries")
|
| 322 |
+
return
|
| 323 |
+
|
| 324 |
+
pdf_file = sys.argv[1]
|
| 325 |
+
print("Enhanced PDF Processor with OCR Support")
|
| 326 |
+
print(f"Target file: {pdf_file}")
|
| 327 |
+
print("=" * 60)
|
| 328 |
+
|
| 329 |
+
try:
|
| 330 |
+
file_path = Path(pdf_file)
|
| 331 |
+
if not file_path.exists():
|
| 332 |
+
print(f"ERROR: File not found: {pdf_file}")
|
| 333 |
+
return
|
| 334 |
+
|
| 335 |
+
# Extract text with intelligent fallback
|
| 336 |
+
pages, method_used = extract_text_with_fallback(file_path)
|
| 337 |
+
|
| 338 |
+
# Results
|
| 339 |
+
total_chars = sum(len(page['content']) for page in pages)
|
| 340 |
+
|
| 341 |
+
print(f"\n" + "=" * 60)
|
| 342 |
+
print(f"EXTRACTION RESULTS")
|
| 343 |
+
print(f"=" * 60)
|
| 344 |
+
print(f"Method used: {method_used}")
|
| 345 |
+
print(f"Total pages: {len(pages)}")
|
| 346 |
+
print(f"Total characters: {total_chars}")
|
| 347 |
+
|
| 348 |
+
if total_chars > 0:
|
| 349 |
+
print(f"\nSUCCESS: Text extracted successfully!")
|
| 350 |
+
|
| 351 |
+
# Show sample content
|
| 352 |
+
for page in pages[:2]: # Show first 2 pages
|
| 353 |
+
content = page['content'].strip()
|
| 354 |
+
if content:
|
| 355 |
+
print(f"\nPage {page['page_number']} (first 200 chars):")
|
| 356 |
+
print(f" {content[:200]}...")
|
| 357 |
+
else:
|
| 358 |
+
print(f"\nWARNING: No text could be extracted")
|
| 359 |
+
|
| 360 |
+
except Exception as e:
|
| 361 |
+
print(f"\nERROR: {e}")
|
| 362 |
+
print(f"\nFull error traceback:")
|
| 363 |
+
traceback.print_exc()
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
if __name__ == "__main__":
|
| 367 |
+
main()
|
environment.yml
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Enhanced Arabic Document Chatbot - Conda Environment
|
| 2 |
+
# Installation: conda env create -f environment.yml
|
| 3 |
+
|
| 4 |
+
name: arabic-chatbot-gpu
|
| 5 |
+
channels:
|
| 6 |
+
- pytorch
|
| 7 |
+
- nvidia
|
| 8 |
+
- conda-forge
|
| 9 |
+
- defaults
|
| 10 |
+
|
| 11 |
+
dependencies:
|
| 12 |
+
# Python version
|
| 13 |
+
- python=3.11
|
| 14 |
+
|
| 15 |
+
# ========================================
|
| 16 |
+
# CORE ML AND GPU DEPENDENCIES
|
| 17 |
+
# ========================================
|
| 18 |
+
|
| 19 |
+
# PyTorch with CUDA support
|
| 20 |
+
- pytorch>=2.1.0
|
| 21 |
+
- torchvision>=0.16.0
|
| 22 |
+
- torchaudio>=2.1.0
|
| 23 |
+
- pytorch-cuda=12.1
|
| 24 |
+
- cuda-toolkit=12.1
|
| 25 |
+
- cudnn>=8.8.0
|
| 26 |
+
|
| 27 |
+
# GPU Libraries
|
| 28 |
+
- nvidia::cuda-nvcc=12.1
|
| 29 |
+
- nvidia::cuda-runtime=12.1
|
| 30 |
+
|
| 31 |
+
# ========================================
|
| 32 |
+
# CORE DEPENDENCIES
|
| 33 |
+
# ========================================
|
| 34 |
+
|
| 35 |
+
# Core Python
|
| 36 |
+
- pip>=23.0
|
| 37 |
+
- setuptools>=65.0
|
| 38 |
+
- wheel>=0.38.0
|
| 39 |
+
|
| 40 |
+
# Scientific Computing
|
| 41 |
+
- numpy>=1.24.0
|
| 42 |
+
- pandas>=2.0.0
|
| 43 |
+
- scipy>=1.10.0
|
| 44 |
+
- scikit-learn>=1.3.0
|
| 45 |
+
|
| 46 |
+
# Image Processing
|
| 47 |
+
- pillow>=10.0.0
|
| 48 |
+
- opencv>=4.8.0
|
| 49 |
+
|
| 50 |
+
# Vector Database
|
| 51 |
+
- faiss-gpu>=1.7.4
|
| 52 |
+
- h5py>=3.8.0
|
| 53 |
+
|
| 54 |
+
# Configuration
|
| 55 |
+
- pyyaml>=6.0
|
| 56 |
+
|
| 57 |
+
# Text Processing
|
| 58 |
+
- nltk>=3.8
|
| 59 |
+
- regex>=2023.5.5
|
| 60 |
+
|
| 61 |
+
# Web Framework
|
| 62 |
+
- flask>=2.3.0
|
| 63 |
+
|
| 64 |
+
# Async Operations
|
| 65 |
+
- aiofiles>=23.0.0
|
| 66 |
+
|
| 67 |
+
# Logging
|
| 68 |
+
- colorlog>=6.7.0
|
| 69 |
+
- tqdm>=4.65.0
|
| 70 |
+
|
| 71 |
+
# Development
|
| 72 |
+
- pytest>=7.4.0
|
| 73 |
+
- black>=23.0.0
|
| 74 |
+
- flake8>=6.0.0
|
| 75 |
+
|
| 76 |
+
# ========================================
|
| 77 |
+
# PIP-ONLY DEPENDENCIES
|
| 78 |
+
# ========================================
|
| 79 |
+
|
| 80 |
+
- pip:
|
| 81 |
+
# Arabic NLP - CAMeL Tools
|
| 82 |
+
- camel-tools>=1.5.2
|
| 83 |
+
|
| 84 |
+
# Advanced Embeddings
|
| 85 |
+
- sentence-transformers>=2.2.2
|
| 86 |
+
- transformers>=4.35.0
|
| 87 |
+
- accelerate>=0.24.0
|
| 88 |
+
|
| 89 |
+
# Arabic Text Processing
|
| 90 |
+
- arabic-reshaper>=3.0.0
|
| 91 |
+
- python-bidi>=0.4.2
|
| 92 |
+
- polyglot>=16.7.4
|
| 93 |
+
- langdetect>=1.0.9
|
| 94 |
+
|
| 95 |
+
# PDF Processing
|
| 96 |
+
- PyMuPDF>=1.23.0
|
| 97 |
+
- pdfplumber>=0.10.0
|
| 98 |
+
- pymupdf-fonts>=1.0.5
|
| 99 |
+
|
| 100 |
+
# OCR Libraries
|
| 101 |
+
- easyocr>=1.7.0
|
| 102 |
+
- pytesseract>=0.3.10
|
| 103 |
+
|
| 104 |
+
# Google AI
|
| 105 |
+
- google-generativeai>=0.3.0
|
| 106 |
+
- google-auth>=2.23.0
|
| 107 |
+
|
| 108 |
+
# Enhanced Features
|
| 109 |
+
- InstructorEmbedding>=1.0.1
|
| 110 |
+
- FlagEmbedding>=1.2.0
|
| 111 |
+
- huggingface-hub>=0.19.0
|
| 112 |
+
|
| 113 |
+
# Web and API
|
| 114 |
+
- Flask-CORS>=4.0.0
|
| 115 |
+
- requests>=2.31.0
|
| 116 |
+
- httpx>=0.25.0
|
| 117 |
+
|
| 118 |
+
# Configuration
|
| 119 |
+
- python-dotenv>=1.0.0
|
| 120 |
+
- pydantic>=2.4.0
|
| 121 |
+
|
| 122 |
+
# Logging
|
| 123 |
+
- loguru>=0.7.0
|
| 124 |
+
- rich>=13.5.0
|
| 125 |
+
|
| 126 |
+
# Performance
|
| 127 |
+
- orjson>=3.9.0
|
| 128 |
+
- numba>=0.58.0
|
| 129 |
+
|
| 130 |
+
# GPU Monitoring
|
| 131 |
+
- nvidia-ml-py>=12.535.0
|
| 132 |
+
- pynvml>=11.5.0
|
| 133 |
+
|
| 134 |
+
# Utilities
|
| 135 |
+
- click>=8.1.0
|
| 136 |
+
- jinja2>=3.1.0
|
| 137 |
+
- pathlib2>=2.3.7
|
environment_simple.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: arabic-chatbot
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
- defaults
|
| 5 |
+
dependencies:
|
| 6 |
+
- python=3.10
|
| 7 |
+
- pip=23.2
|
| 8 |
+
- numpy=1.24
|
| 9 |
+
- sqlite=3.42
|
insider_trading_query.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Direct Query for Insider Trading Case
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import asyncio
|
| 9 |
+
import json
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
# Add src directory to Python path
|
| 13 |
+
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
| 14 |
+
|
| 15 |
+
async def query_insider_trading():
|
| 16 |
+
"""Query the insider trading regulations."""
|
| 17 |
+
try:
|
| 18 |
+
from src.ui.enhanced_gradio_app import EnhancedArabicChatbot
|
| 19 |
+
app = EnhancedArabicChatbot()
|
| 20 |
+
|
| 21 |
+
# Arabic query about insider trading
|
| 22 |
+
query = """
|
| 23 |
+
يشغل السيد خالد الأحمد منصب رئيس مجلس إدارة في بنك الكويت الدولي، وهو مساهم رئيسي (35%) في الشركة الخليجية للاستثمار المدرجة في بورصة الكويت. قام السيد خالد بتوجيه الصندوق الاستثماري (عبر شركته) لشراء أسهم إضافية في البنك بقيمة 15 مليون دينار كويتي، قبل أسبوع من الإعلان عن النتائج المالية القوية والتي تتضمن مؤشرات إيجابية غير معلنة.
|
| 24 |
+
|
| 25 |
+
هل تشكل تصرفات السيد خالد انتهاكاً لقواعد منع الاستفادة من المعلومات الداخلية وفقاً للكتاب الحادي عشر (التعامل في الأوراق المالية) والكتاب الثامن (أخلاقيات العمل)؟
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
history = []
|
| 29 |
+
result_history, status = await app.process_query(query, history)
|
| 30 |
+
|
| 31 |
+
if result_history and len(result_history) >= 2:
|
| 32 |
+
response = result_history[-1]['content']
|
| 33 |
+
|
| 34 |
+
# Save results to file
|
| 35 |
+
results = {
|
| 36 |
+
"query": query,
|
| 37 |
+
"response": response,
|
| 38 |
+
"status": status,
|
| 39 |
+
"response_length": len(response)
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
with open("insider_trading_results.json", "w", encoding="utf-8") as f:
|
| 43 |
+
json.dump(results, f, indent=2, ensure_ascii=False)
|
| 44 |
+
|
| 45 |
+
print(f"Query processed successfully. Response length: {len(response)} characters")
|
| 46 |
+
print(f"Status: {status}")
|
| 47 |
+
return True
|
| 48 |
+
|
| 49 |
+
else:
|
| 50 |
+
print(f"Query failed with status: {status}")
|
| 51 |
+
return False
|
| 52 |
+
|
| 53 |
+
except Exception as e:
|
| 54 |
+
print(f"Error: {e}")
|
| 55 |
+
return False
|
| 56 |
+
|
| 57 |
+
if __name__ == "__main__":
|
| 58 |
+
success = asyncio.run(query_insider_trading())
|
| 59 |
+
print(f"Success: {success}")
|
main.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
|
| 3 |
+
custom_css = """
|
| 4 |
+
body {
|
| 5 |
+
background-color: #1a1a1a; /* Very dark gray background for the entire page */
|
| 6 |
+
color: #e0e0e0; /* Light gray text color for better contrast */
|
| 7 |
+
}
|
| 8 |
+
.gradio-container {
|
| 9 |
+
background-color: #2c2c2c; /* Slightly lighter dark gray for the main Gradio app container */
|
| 10 |
+
border-radius: 10px;
|
| 11 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Darker shadow for dark theme */
|
| 12 |
+
}
|
| 13 |
+
/* Style for the centered text */
|
| 14 |
+
.center-text {
|
| 15 |
+
text-align: center;
|
| 16 |
+
width: 100%; /* Ensure it takes full width to center content */
|
| 17 |
+
}
|
| 18 |
+
.gradio-markdown {
|
| 19 |
+
color: #e0e0e0; /* Ensure markdown text is light */
|
| 20 |
+
}
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
with gr.Blocks(css=custom_css) as demo:
|
| 24 |
+
gr.Markdown("# My Custom Styled App", elem_classes="center-text") # Centering the main title as well
|
| 25 |
+
|
| 26 |
+
with gr.Row(elem_classes="center-text"):
|
| 27 |
+
gr.Markdown("""
|
| 28 |
+
🎯 إجابات قاطعة: يُسمح ✅ | لا يُسمح ❌ | مطلوب 📋
|
| 29 |
+
⚖️ مراجع قانونية دقيقة: CBK | CMA | AML | المبادئ القانونية
|
| 30 |
+
""")
|
| 31 |
+
|
| 32 |
+
gr.Textbox(label="Enter text here")
|
| 33 |
+
gr.Button("Submit")
|
| 34 |
+
|
| 35 |
+
demo.launch()
|
qa_knowledge_base.json
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"question": "ما هو مفهوم مجلس التأديب وفقاً للوثائق؟",
|
| 4 |
+
"answer": "هو مجلس مختص بالنظر في مخالفات الأعضاء والمنتسبين، ويتألف من قضاة أو أعضاء إداريين وفقًا للأنظمة المعمول بها، ويصدر أحكامًا تأديبية بناءً على التحقيقات والمرافعات المقدمة.",
|
| 5 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار تعريفي عن مفهوم مجلس التأديب في السياق التنظيمي\n📋 **الأساس القانوني**: الأنظمة واللوائح المعمول بها في الإطار التأديبي\n⚖️ **التفسير القانوني**: المجلس هو جهة قضائية مختصة بالمخالفات التأديبية، مكون من قضاة مؤهلين أو أعضاء إداريين متخصصين\n✅ **القرار النهائي**: مجلس التأديب جهة رسمية مخولة بالنظر في المخالفات وإصدار الأحكام\n💡 **إرشادات الامتثال**: يجب احترام قرارات المجلس والتعاون مع إجراءاته التحقيقية",
|
| 6 |
+
"reference": null,
|
| 7 |
+
"category": "Disciplinary Law"
|
| 8 |
+
},
|
| 9 |
+
{
|
| 10 |
+
"question": "ما الفرق بين التنبيه واللوم كعقوبتين تأديبيتين؟",
|
| 11 |
+
"answer": "التنبيه هو أقل درجات العقوبات ويكون بمثابة تحذير كتابي دون أن يؤثر على المسار المهني، أما اللوم فيُسجل في الملف الوظيفي وقد يؤثر على الترقيات والتقييمات.",
|
| 12 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: مقارنة بين نوعين من العقوبات التأديبية (التنبيه واللوم) من حيث الدرجة والأثر\n📋 **الأساس القانوني**: سلم العقوبات التأديبية المنصوص عليه في قانون الخدمة المدنية\n⚖️ **التفسير القانوني**: التنبيه عقوبة بسيطة لا تؤثر على المسار الوظيفي، واللوم عقوبة أشد تُسجل في الملف وتؤثر على التقييمات\n✅ **القرار النهائي**: التنبيه أخف من اللوم في الدرجة والأثر على المسار المهني\n💡 **إرشادات الامتثال**: تجنب تكرار المخالفات لعدم التدرج في العقوبات من التنبيه إلى اللوم ثم العقوبات الأشد",
|
| 13 |
+
"reference": null,
|
| 14 |
+
"category": "Disciplinary Law"
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"question": "ما هو الإجراء الذي يجب اتباعه قبل إحالة الموظف إلى مجلس التأديب؟",
|
| 18 |
+
"answer": "يجب أن تُجري جهة العمل تحقيقًا داخليًا مبدئيًا، ويُشترط توفر دلائل قوية على المخالفة، مع إبلاغ الموظف كتابيًا وإعطائه الفرصة للرد.",
|
| 19 |
+
"reference": null,
|
| 20 |
+
"category": "Disciplinary Law"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"question": "هل يجوز للموظف الطعن في قرار مجلس التأديب؟ وضح.",
|
| 24 |
+
"answer": "نعم، يحق للموظف الطعن في قرار المجلس خلال مدة محددة أمام الجهة القضائية المختصة، بشرط أن يبيّن أسباب الطعن سواء كانت شكلية أو موضوعية.",
|
| 25 |
+
"reference": null,
|
| 26 |
+
"category": "Disciplinary Law"
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"question": "ما المقصود بمبدأ الملاءمة التأديبية؟",
|
| 30 |
+
"answer": "هو المبدأ الذي يعطي الجهة التأديبية سلطة تقديرية في اختيار العقوبة الأنسب وفقًا لظروف المخالفة ودرجة جسامتها وشخصية المخالف.",
|
| 31 |
+
"reference": null,
|
| 32 |
+
"category": "Disciplinary Law"
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"question": "اشرح مبدأ التحقيق المسبق في العمل التأديبي.",
|
| 36 |
+
"answer": "ينص على أنه لا يجوز توقيع أي جزاء على الموظف إلا بعد إجراء تحقيق مكتوب معه وسماع أقواله والدفاع عنه وتمكينه من الاطلاع على المستندات.",
|
| 37 |
+
"reference": null,
|
| 38 |
+
"category": "Disciplinary Law"
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"question": "ما الحالات التي يُحال فيها الموظف إلى التقاعد لأسباب تأديبية؟",
|
| 42 |
+
"answer": "في حال ارتكب الموظف مخالفة جسيمة تسيء للوظيفة أو سمعة المؤسسة، ويكون ذلك بقرار من المجلس التأديبي بعد استيفاء الإجراءات النظامية.",
|
| 43 |
+
"reference": null,
|
| 44 |
+
"category": "Disciplinary Law"
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"question": "كيف يتم تحديد العقوبة المناسبة للمخالفة؟",
|
| 48 |
+
"answer": "يُراعى مدى جسامة المخالفة، الضرر الناتج عنها، سوابق الموظف، ودرجة تعاونه أثناء التحقيق، وكل هذه المعايير تُستخدم لتقدير العقوبة الأنسب.",
|
| 49 |
+
"reference": null,
|
| 50 |
+
"category": "Disciplinary Law"
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"question": "ما الفرق بين المجلس الابتدائي والمجلس الأعلى للتأديب؟",
|
| 54 |
+
"answer": "المجلس الابتدائي يختص بالنظر في المخالفات البسيطة والمتوسطة، أما المجلس الأعلى فينظر في القضايا الجسيمة وله صلاحية إصدار عقوبات أشد.",
|
| 55 |
+
"reference": null,
|
| 56 |
+
"category": "Disciplinary Law"
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"question": "هل يمكن إعادة النظر في قرار الفصل التأديبي؟",
|
| 60 |
+
"answer": "نعم، يمكن في حالات محددة مثل ظهور أدلة جديدة، أو وجود خطأ جوهري في الإجراءات، ويُعاد تقديم الطلب للجهة المختصة للمراجعة.",
|
| 61 |
+
"reference": null,
|
| 62 |
+
"category": "Disciplinary Law"
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"question": "ما هو المبدأ رقم (1) في النظام التأديبي؟",
|
| 66 |
+
"answer": "المبدأ رقم (1) ينص على أن الجزاء التأديبي لا يوقع على العامل إلا بعد التحقيق معه وسماع أقواله وتحقيق دفاعه. ويجب أن يكون القرار الصادر بتوقيع الجزاء مسببًا. يتعلق هذا المبدأ بضمان حقوق الموظف وتمكينه من الدفاع عن نفسه، ويؤكد على ضرورة وجود تحقيق عادل قبل توقيع الجزاء.",
|
| 67 |
+
"reference": "الكتاب رقم 13 - مبادئ مجلس التأديب (ص. 3-4)",
|
| 68 |
+
"category": "Disciplinary Law"
|
| 69 |
+
},
|
| 70 |
+
{
|
| 71 |
+
"question": "ما هو المقصود بالمبدأ رقم (2)؟",
|
| 72 |
+
"answer": "المبدأ رقم (2) ينص على أنه لا يجوز توقيع جزاء على العامل إلا من السلطة المختصة قانونا. هذا يعني أن توقيع الجزاءات التأديبية لا يجوز من أي جهة غير مخولة قانونا بذلك، لضمان العدالة وتجنب التعسف.",
|
| 73 |
+
"reference": "الكتاب رقم 13 – مبادئ مجلس التأديب (ص. 4)",
|
| 74 |
+
"category": "Disciplinary Law"
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"question": "المبدأ رقم (3) يربط بين الخطأ التأديبي والخطأ الجنائي - كيف؟",
|
| 78 |
+
"answer": "المبدأ يوضح أن الخطأ الجنائي إذا قام على ذات الواقعة محل المخالفة التأديبية، فإن للسلطة التأديبية أن تنتظر نتيجة الحكم الجنائي ما لم تكن بصدد مخالفة تأديبية مستقلة. بمعنى آخر، إذا كانت المخالفة قيد التحقيق الجنائي، قد يتم تعليق القرار التأديبي لحين الفصل القضائي.",
|
| 79 |
+
"reference": "الكتاب رقم 13 – مبادئ عامة في التأديب (ص. 5)",
|
| 80 |
+
"category": "Disciplinary Law"
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"question": "ما هو المبدأ الرابع في تنظيم الإجراءات؟",
|
| 84 |
+
"answer": "ينص المبدأ على أن تعتبر الإجراءات التأديبية باطلة إذا لم تُراعى الضمانات الجوهرية المقررة للعامل، مثل حق الدفاع أو التحقيق. هذا المبدأ يكرس أهمية احترام حقوق الدفاع، وأن أي انتقاص منها قد يبطل القرار التأديبي.",
|
| 85 |
+
"reference": "الكتاب رقم 7 – المبادئ العامة في التحقيق الإداري والتأديب (ص. (6))",
|
| 86 |
+
"category": "Disciplinary Law"
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"question": "ماذا يقول المبدأ رقم (5) بشأن العقوبات التأديبية؟",
|
| 90 |
+
"answer": "المبدأ يقر أن العقوبة التأديبية يجب أن تكون متناسبة مع المخالفة المرتكبة، وأن تتدرج تبعا لخطورة الفعل. يُمنع توقيع أقصى عقوبة على مخالفات بسيطة، ويجب النظر في تكرار المخالفة، الظروف المحيطة، وسابقة الموظف.",
|
| 91 |
+
"reference": "الكتاب رقم 12 - الجزاءات التأديبية في قانون الخدمة المدنية (ص. 4-5)",
|
| 92 |
+
"category": "Disciplinary Law"
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"question": "ما هو المبدأ والملخص من المادة 1-4-3 من المبادئ القانونية في مجلس التأديب؟",
|
| 96 |
+
"answer": "**المبدأ:** وضع المشرع التزاماً على الأشخاص المرخص لهم بوضع سياسات لإدارة مخاطر الائتمان والسوق والسيولة والمخاطر التشغيلية في سياسة إدارة المخاطر الخاصة بهم.\n\n**الملخص:** يتعلق هذا المبدأ بالتزام الأشخاص المرخص لهم بوضع سياسات شاملة لإدارة المخاطر المختلفة، ويشمل ذلك مخاطر الائتمان ومخاطر السوق ومخاطر السيولة والمخاطر التشغيلية، كجزء من إطار إدارة المخاطر المؤسسية.\n\n**ملاحظة:** هذه المادة تظهر في الوثيقة بالتنسيق (4-3) وليس 1-4-3.",
|
| 97 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار عن التزام قانوني محدد في المادة (4-3) بخصوص إدارة المخاطر الشاملة للمؤسسات المرخصة\\n📋 **الأساس القانوني**: الكتاب 6 من المبادئ القانونية في مجلس التأديب، المادة (4-3) المتعلقة بإدارة المخاطر\\n⚖️ **التفسير القانوني**: المشرع أوجب على جميع الأشخاص المرخص لهم وضع إطار متكامل لإدارة المخاطر يشمل أربعة أنواع رئيسية: الائتمانية والسوقية والسيولة والتشغيلية\\n✅ **القرار النهائي**: **إلزامي** - وجود سياسات شاملة لإدارة المخاطر الأربعة الأساسية التزام قانوني\\n💡 **إرشادات الامتثال**: تطوير وثائق سياسة مخاطر معتمدة تغطي الأنواع الأربعة، تحديث دوري للسياسات، تدريب الموظفين، ومراجعة فعالية التطبيق",
|
| 98 |
+
"reference": "الكتاب 6.pdf - المبادئ القانونية في مجلس التأديب، المادة (4-3)",
|
| 99 |
+
"category": "Banking Regulation"
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"question": "ما هو المبدأ من المادة (4-3) من المبادئ القانونية؟",
|
| 103 |
+
"answer": "وضع المشرع التزاماً على الأشخاص المرخص لهم بوضع سياسات لإدارة مخاطر الائتمان والسوق والسيولة والمخاطر التشغيلية في سياسة إدارة المخاطر الخاصة بهم. هذا المبدأ يتعلق بإلزام المؤسسات المرخصة بوضع إطار شامل لإدارة المخاطر يغطي جميع أنواع المخاطر التي قد تواجهها المؤسسة في أعمالها.",
|
| 104 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار عن التزام قانوني محدد بخصوص إدارة المخاطر للمؤسسات المالية المرخصة\n📋 **الأساس القانوني**: المادة (4-3) من المبادئ القانونية في مجلس التأديب\n⚖️ **التفسير القانوني**: المشرع ألزم المؤسسات المرخصة بوضع سياسات شاملة تغطي أربعة أنواع من المخاطر: الائتمان، السوق، السيولة، والتشغيلية\n✅ **القرار النهائي**: **مطلوب** - وجود سياسات إدارة المخاطر الشاملة التزام قانوني إجباري\n💡 **إرشادات الامتثال**: يجب تطوير وتطبيق إطار متكامل لإدارة المخاطر يشمل التحديد والقياس والمراقبة والتحكم في جميع المخاطر",
|
| 105 |
+
"reference": "الكتاب 6.pdf - المبادئ القانونية في مجلس التأديب، المادة (4-3)",
|
| 106 |
+
"category": "Banking Regulation"
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"question": "ما هو ملخص المادة 4-3 من المبادئ القانونية؟",
|
| 110 |
+
"answer": "يتطلب هذا المبدأ من الأشخاص المرخص لهم وضع سياسات شاملة لإدارة المخاطر تشمل: مخاطر الائتمان، ومخاطر السوق، ومخاطر السيولة، والمخاطر التشغيلية. الهدف هو ضمان أن المؤسسات المالية لديها إطار عمل قوي لتحديد وقياس ومراقبة والتحكم في جميع أنواع المخاطر التي تواجهها في عملياتها التجارية.",
|
| 111 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: طلب ملخص للمادة (4-3) من المبادئ القانونية المتعلقة بسياسات إدارة المخاطر\\n📋 **الأساس القانوني**: الكتاب 6 من المبادئ القانونية في مجلس التأديب، المادة (4-3) للمخاطر المصرفية\\n⚖️ **التفسير القانوني**: المشرع يهدف لضمان إدارة شاملة للمخاطر من خلال إلزام المؤسسات بوضع إطار متكامل يشمل دورة كاملة: التحديد والقياس والمراقبة والتحكم\\n✅ **القرار النهائي**: **إلزامي** - إطار عمل قوي وشامل لإدارة جميع أنواع المخاطر الأربعة\\n💡 **إرشادات الامتثال**: تطوير منهجية متد��جة لإدارة المخاطر تبدأ بالتحديد ثم القياس الكمي والمراقبة المستمرة وأخيراً آليات التحكم والتخفيف، مع مراجعة دورية للفعالية",
|
| 112 |
+
"reference": "الكتاب 6.pdf - المبادئ القانونية في مجلس التأديب، المادة (4-3)",
|
| 113 |
+
"category": "Banking Regulation"
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"question": "بناءً على الملكية المذكورة هل تعتبر الشركة الكويتية للاستثمار مترابطة مع شركة إمبلس إنترناشيونال للاتصالات؟",
|
| 117 |
+
"answer": "وفقاً لمفاهيم السيطرة التي تستخدم في تحديد مدى وجود ترابط بين الأطراف من شركات الأموال، فإنه إذا تحقق للطرف (أ) السيطرة بموجب الملكية المباشرة في الطرف/ الأطراف (ب)، وفي ذات الوقت تحقق للطرف (أ) السيطرة بموجب الملكية غير المباشرة في الطرف/ الأطراف (ج)، فإن ذلك يعني أن كل من الطرف/ الأطراف (ب) و(ج) يتم السيطرة عليهم من طرف واحد وهو الطرف (أ)، وعليه يتحقق الارتباط بين الطرف/ الأطراف (ب) و(ج) على هذا الأساس. نعم، تعتبر الشركتان مترابطتين وفقاً لمفهوم العميل الواحد الوارد في تعليمات بنك الكويت المركزي ذات العلاقة.",
|
| 118 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار تطبيقي عن ترابط شركتين محددتين (الشركة الكويتية للاستثمار وشركة إمبلس إنترناشيونال) وفق مفهوم العميل الواحد\\n📋 **الأساس القانوني**: تعليمات بنك الكويت المركزي لمفهوم العميل الواحد ومعايير السيطرة والترابط بين الشركات\\n⚖️ **التفسير القانوني**: نظرية السيطرة المشتركة تنص أن الشركات المترابطة عبر طرف ثالث مسيطر (أ) تُعتبر مترابطة فيما بينها، حيث السيطرة المباشرة وغير المباشرة من نفس الطرف تخلق ترابطاً قانونياً\\n✅ **القرار النهائي**: **نعم** - الشركتان مترابطتان وفقاً لمفهوم العميل الواحد عبر السيطرة المشتركة\\n💡 **إرشادات الامتثال**: تطبيق حدود التمويل الموحدة على الشركتين كعميل واحد، مراقبة إجمالي التعرضات المجمعة، وضمان الامتثال لنسب التركز التمويلي المقررة",
|
| 119 |
+
"reference": "تعليمات بنك الكويت المركزي - مفهوم العميل الواحد",
|
| 120 |
+
"category": "Banking Regulation"
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"question": "لغرض احتساب الحد الأعلى لإجمالي التسهيلات الممنوحة للأطراف المترابطة لاحتساب نسبة التركز التمويلي، هل يتم احتساب التسهيلات غير النقدية على أساس قيمة الحد أو المستخدم من الحد؟",
|
| 124 |
+
"answer": "إن عمليات التمويل غير النقدية التي يتم الاعتداد بها عند احتساب نسبة التركز التمويلي، هي العمليات التي ترتب التزاماً عرضياً على البنك، وهي تشمل ما يلي: الاعتمادات المستندية - خطابات الضمان والكفالات - القبولات. ومعنى ذلك أنه متى ما ترتب فعلاً بسبب عمليات التمويل غير النقدية التزاماً عرضياً على مصرفنا، تم إضافة تلك العمليات إلى إجمالي عمليات التمويل غير النقدية عند احتساب نسبة التركز التمويلي لمصرفنا، أي أنه يتم احتساب الحدود المستخدمة فقط من عمليات التمويل غير النقدية والتي ترتب عليها فعلاً التزاماً عرضياً على مصرفنا، وليست قيمة الحدود التي لم تستخدم ولم ترتب فعلاً التزاماً عرضياً على مصرفنا.",
|
| 125 |
+
"reference": "تعليمات بنك الكويت المركزي - نسبة التركز التمويلي",
|
| 126 |
+
"category": "Banking Regulation"
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"question": "يرجى الإفادة حول ما إذا كان هناك أي تعليمات تمنع العملاء الأفراد من فئة عالي المخاطر بتحديث بياناتهم عبر تطبيق بنك وربة وتستوجب حضورهم شخصياً إلى البنك؟",
|
| 130 |
+
"answer": "لا يوجد تعارض مع التعليمات مع الالتزام التام بسياسة مصرفنا بهذا الشأن.",
|
| 131 |
+
"reference": "سياسات بنك وربة الداخلية",
|
| 132 |
+
"category": "Banking Regulation"
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"question": "توضيح تعليمات البنك المركزي بخصوص منح تسهيلات غير نقدية لشركات لها ترابط مع أحد أعضاء مجلس الإدارة، فهل يجوز الاكتفاء بهامش نقدي لا يغطي 100% من التسهيلات؟",
|
| 136 |
+
"answer": "يتعين أن يتم تحديد الهوامش (التأمينات النقدية) التي يحصل عليها البنك من قيمة عمليات التمويل غير النقدية وفقاً للأصول المصرفية المتعارف عليها والمتبع مع باقي عملاء البنك، مع مراعاة ما يلي: - ألا تقل هذه الهوامش عن 100% من قيمة خطابات الضمان أو الكفالات الصادرة لضمان أي عمليات تمويل يحصل عليها عضو مجلس الإدارة من بنوك أو مؤسسات مالية أخرى. - بالنسبة للاعتمادات المستندية وباقي خطابات الضمان أو الكفالات والالتزامات العرضية الأخرى، يتعين الحصول على هوامش كافية مقابلها، بحيث لا يكون هناك أية معاملة تفضيلية لأي من أعضاء مجلس الإدارة عن باقي عملاء البنك.",
|
| 137 |
+
"reference": "تعليمات بنك الكويت المركزي ذات العلاقة",
|
| 138 |
+
"category": "Banking Regulation"
|
| 139 |
+
},
|
| 140 |
+
{
|
| 141 |
+
"question": "استفسار عن رغبة البنك بفتح حساب لعميل ذو شخصية اعتبارية تم تصنيفه من ذوي مرتفعي المخاطر نظراً لعلاقته بالمجوهرات والذهب وكون الرخصة الصادرة هي رخصة منزلية والتي لا يقوم بها بتقديم بيانات مالية معتمدة؟",
|
| 142 |
+
"answer": "نود الإفادة أنه بالإشارة إلى تعليمات بنك الكويت المركزي بشأن مكافحة غسل الأموال وتمويل الإرهاب الصادرة والمحدثة في 16/02/2023 (تدابير العناية الواجبة المشددة على العملاء ذوي المخاطر المرتفعة..) نصت في النقطة رقم 4 على \"تتضمن تدابير العناية الواجبة المشددة التي تنطبق على علاقات العمل مرتفعة المخاطر ،... ج. الحصول على معلومات إضافية عن العميل (الشخص الاعتباري) وطبيعة علاقة العمل المتوقعة وحجم النشاط والحصول على آخر ميزانية متاحة عن النشاط\". وعليه فإن أي عميل ذو شخصية اعتبارية يتم تصنيفه من ذوي مرتفعي المخاطر عليه تقديم البيانات المالية المتاحة عند تقديم طلب فتح الحساب وتحديث البيانات (KYC) مهما كان نوع الترخيص التجاري.",
|
| 143 |
+
"reference": "تعليمات بنك الكويت المركزي - مكافحة غسل الأموال وتمويل الإرهاب (16/02/2023)",
|
| 144 |
+
"category": "Banking Regulation"
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"question": "ما هي المدة القانونية التي يجب أن يحتفظ فيها الشخص المرخص له ببيانات اعرف عميلك (KYC) بعد انتهاء العلاقة مع العميل؟",
|
| 148 |
+
"answer": "خمس سنوات.",
|
| 149 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار عن المدة الزمنية الإلزامية لحفظ سجلات العملاء بعد إنهاء العلاقة التجارية\\n📋 **الأساس القانوني**: قوانين هيئة أسواق المال ولوائح حفظ السجلات وإدارة المخاطر\\n⚖️ **التفسير القانوني**: هيئة أسواق المال تلزم الأشخاص المرخص لهم بالاحتفاظ ببيانات العملاء لمدة خمس سنوات كاملة من تاريخ إنهاء العلاقة لضمان الامتثال والمراجعة\\n✅ **القرار النهائي**: **إلزامي** - الاحتفاظ بسجلات KYC لمدة خمس سنوات بعد إنهاء العلاقة\\n💡 **إرشادات الامتثال**: وضع نظام أرشفة منظم مع تواريخ واضحة لإنهاء العلاقات، وتنبيهات تلقائية لانتهاء فترات الحفظ، والتأكد من أمان وسرية السجلات المحفوظة",
|
| 150 |
+
"reference": "قوانين هيئة أسواق المال - حفظ السجلات",
|
| 151 |
+
"category": "Capital Markets"
|
| 152 |
+
},
|
| 153 |
+
{
|
| 154 |
+
"question": "ما هي الشروط التي يجب أن تتوافر في التفويض القانوني الذي يسمح لشخص بفتح وتشغيل حساب استثماري نيابة عن عميل؟ وهل يجوز أن يكون التفويض إلكترونياً؟",
|
| 155 |
+
"answer": "يجب أن يكون التفويض صادراً من كاتب عدل أو موثق معتمد من وزارة العدل. يجب أن يتضمن نصاً صريحاً بالصلاحيات المفوضة مثل فتح الحساب، تشغيله، إجراء الحوالات، أو بيع وشراء الأوراق المالية. لا يُعتد بالتفويض الإلكتروني إلا إذا كان موثقاً ومعتمداً وفق قانون المعاملات الإلكترونية.",
|
| 156 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار مركب عن متطلبات التفويض القانوني للعمل نيابة عن العملاء في الاستثمار ومدى قبول التوثيق الإلكتروني\\n📋 **الأساس القانوني**: قوانين هيئة أسواق المال للتفويض القانوني وقانون المعاملات الإلكترونية الكويتي\\n⚖️ **التفسير القانوني**: هيئة أسواق المال تتطلب توثيقاً رسمياً من كاتب عدل معتمد مع نص صريح للصلاحيات، والتفويض الإلكتروني مقبول فقط إذا كان معتمداً وفق قانون المعاملات الإلكترونية\\n✅ **القرار النهائي**: **إلزامي** - توثيق رسمي من كاتب عدل + نص صريح للصلاحيات + اعتماد إلكتروني عند الحاجة\\n💡 **إرشادات الامتثال**: الحصول على تفويض موثق رسمياً يحدد بوضوح جميع الصلاحيات المطلوبة، التأكد من اعتماد التوقيع الإلكتروني وفق القانون الكويتي، حفظ نسخ معتمدة من التفويض",
|
| 157 |
+
"reference": "قوانين هيئة أسواق المال - التفويض القانوني",
|
| 158 |
+
"category": "Capital Markets"
|
| 159 |
+
},
|
| 160 |
+
{
|
| 161 |
+
"question": "هل هناك قيود مفروضة على موظفي الشخص المرخص له من قبل هيئة أسواق المال بالنسبة لتداولاتهم؟",
|
| 162 |
+
"answer": "يتعين على موظفي الشخص المرخص له الالتزام بالتعليمات والقيود المفروضة عليهم من قبل الهيئة، حيث يتعين إبلاغ مسؤول المطابقة والالتزام – على الفور - بأي صفقة (بيع أو شراء أوراق مالية محلية) يجريها عن نفسه أو بالإنابة عن أحد أقربائه أو عن شركة تابعة له أو لأحد أقربائه.",
|
| 163 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار عن القيود التنظيمية المفروضة على تداولات موظفي الأشخاص المرخص لهم من هيئة أسواق المال\\n📋 **الأساس القانوني**: تعليمات هيئة أسواق المال لقيود التداول للموظفين ومنع تضارب المصالح\\n⚖️ **التفسير القانوني**: هيئة أسواق المال تفرض قيوداً صارمة على موظفي الشركات المرخصة لمنع الاستفادة من المعلومات الداخلية، وتتطلب الإبلاغ الفوري عن جميع التداولات الشخصية\\n✅ **القرار النهائي**: **إلزامي** - إبلاغ فوري لمسؤول المطابقة عن جميع التداولات الشخصية والعائلية\\n💡 **إرشادات الامتثال**: وضع نظام إبلاغ فعال مع مسؤول المطابقة، تدريب الموظفين على قيود التداول، توثيق جميع التداولات، والتأكد من عدم الاستفادة من المعلومات الداخلية",
|
| 164 |
+
"reference": "تعليمات هيئة أسواق المال - قيود التداول للموظفين",
|
| 165 |
+
"category": "Capital Markets"
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"question": "هل يجوز للشخص المرخص له بمزاولة نشاط مدير استثمار جماعي أن يتواصل مع بعض العملاء لمعرفة رأيهم بشأن الاستثمار في صندوق يرغب بتسويقه مستقبلاً؟ وذلك قبل حصوله على ترخيص تسويق الصندوق من الهيئة؟",
|
| 169 |
+
"answer": "يجوز للهيئة – بناءً على طلب مقدم - أن تسمح للمسوق خلال فترة استكمال متطلبات الهيئة التواصل مع العملاء المحترفين المحتملين لبحث رغبتهم بالاستثمار في الصندوق المزمع تسويقه دون الإعلان في وسائل الإعلام، وأن لا يتم تقاضي أي مبالغ نقدية أو غير نقدية أو الحصول على التزام نهائي من العملاء بالاشتراك في الصندوق قبل الحصول على ترخيص التسويق من الهيئة.",
|
| 170 |
+
"chain_of_thought": "🔍 **تحليل السؤال**: استفسار قانوني عن إمكانية التسويق المبكر للصناديق ا��استثمارية قبل الحصول على ترخيص التسويق من الهيئة\\n📋 **الأساس القانوني**: تعليمات هيئة أسواق المال لتسويق الصناديق الاستثمارية وحماية المستثمرين\\n⚖️ **التفسير القانوني**: الهيئة تسمح بالتواصل المحدود مع العملاء المحترفين فقط لاستطلاع الآراء بشروط صارمة: عدم الإعلان العام، عدم جمع الأموال، عدم الحصول على التزامات نهائية\\n✅ **القرار النهائي**: **مشروط** - يجوز بطلب مسبق من الهيئة مع العملاء المحترفين فقط وبدون جمع أموال\\n💡 **إرشادات الامتثال**: تقديم طلب رسمي للهيئة، التواصل مع العملاء المحترفين المؤهلين فقط، تجنب الإعلانات العامة، عدم جمع أي أموال أو التزامات، توثيق جميع المراسلات والاتصالات",
|
| 171 |
+
"reference": "تعليمات هيئة أسواق المال - تسويق الصناديق الاستثمارية",
|
| 172 |
+
"category": "Capital Markets"
|
| 173 |
+
}
|
| 174 |
+
]
|
requirements.txt
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Enhanced Arabic Document Chatbot Dependencies
|
| 2 |
+
|
| 3 |
+
# Core web framework
|
| 4 |
+
gradio>=4.0.0
|
| 5 |
+
|
| 6 |
+
# AI and language models
|
| 7 |
+
google-generativeai>=0.3.0
|
| 8 |
+
openai>=1.3.0
|
| 9 |
+
|
| 10 |
+
# Vector search and embeddings
|
| 11 |
+
faiss-cpu>=1.7.4
|
| 12 |
+
sentence-transformers>=2.2.0
|
| 13 |
+
|
| 14 |
+
# PDF processing
|
| 15 |
+
PyMuPDF>=1.23.0
|
| 16 |
+
pdfplumber>=0.10.0
|
| 17 |
+
pytesseract>=0.3.10
|
| 18 |
+
easyocr>=1.7.0
|
| 19 |
+
|
| 20 |
+
# Data processing
|
| 21 |
+
numpy>=1.24.0
|
| 22 |
+
PyYAML>=6.0
|
| 23 |
+
|
| 24 |
+
# Arabic NLP tools
|
| 25 |
+
camel-tools>=1.5.2
|
| 26 |
+
pyarabic>=0.6.15
|
| 27 |
+
arabic-reshaper>=3.0.0
|
| 28 |
+
python-bidi>=0.4.2
|
| 29 |
+
|
| 30 |
+
# Utilities
|
| 31 |
+
tenacity>=8.2.0
|
| 32 |
+
|
| 33 |
+
# Optional: GPU support (uncomment if needed)
|
| 34 |
+
# faiss-gpu>=1.7.4
|
| 35 |
+
# torch>=2.0.0
|
requirements.yml
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Enhanced Arabic Document Chatbot - Comprehensive Dependencies
|
| 2 |
+
# Supports CAMeL Tools for Arabic NLP and GPU-accelerated embeddings
|
| 3 |
+
# Installation: pip install -r requirements.yml
|
| 4 |
+
|
| 5 |
+
name: arabic-document-chatbot
|
| 6 |
+
channels:
|
| 7 |
+
- pytorch
|
| 8 |
+
- nvidia
|
| 9 |
+
- conda-forge
|
| 10 |
+
- defaults
|
| 11 |
+
|
| 12 |
+
dependencies:
|
| 13 |
+
# Python version
|
| 14 |
+
- python=3.11
|
| 15 |
+
|
| 16 |
+
# Core ML and GPU Dependencies
|
| 17 |
+
- pytorch>=2.1.0
|
| 18 |
+
- torchvision>=0.16.0
|
| 19 |
+
- torchaudio>=2.1.0
|
| 20 |
+
- pytorch-cuda=12.1 # CUDA 12.1 support
|
| 21 |
+
- cudatoolkit=12.1
|
| 22 |
+
- nvidia::cuda-toolkit=12.1
|
| 23 |
+
|
| 24 |
+
# Core Python Dependencies
|
| 25 |
+
- pip>=23.0
|
| 26 |
+
- setuptools>=65.0
|
| 27 |
+
- wheel>=0.38.0
|
| 28 |
+
|
| 29 |
+
# Data Science and ML
|
| 30 |
+
- numpy>=1.24.0
|
| 31 |
+
- pandas>=2.0.0
|
| 32 |
+
- scipy>=1.10.0
|
| 33 |
+
- scikit-learn>=1.3.0
|
| 34 |
+
|
| 35 |
+
# NLP and Text Processing
|
| 36 |
+
- nltk>=3.8
|
| 37 |
+
- regex>=2023.5.5
|
| 38 |
+
|
| 39 |
+
# PDF Processing and OCR
|
| 40 |
+
- pillow>=10.0.0
|
| 41 |
+
|
| 42 |
+
# Vector Database and Search
|
| 43 |
+
- faiss-gpu>=1.7.4 # GPU-accelerated FAISS
|
| 44 |
+
- h5py>=3.8.0
|
| 45 |
+
|
| 46 |
+
# Web Framework and API
|
| 47 |
+
- flask>=2.3.0
|
| 48 |
+
- flask-cors>=4.0.0
|
| 49 |
+
- gunicorn>=21.0.0
|
| 50 |
+
|
| 51 |
+
# Async and Concurrency
|
| 52 |
+
- aiofiles>=23.0.0
|
| 53 |
+
- asyncio
|
| 54 |
+
|
| 55 |
+
# Configuration and Data
|
| 56 |
+
- pyyaml>=6.0
|
| 57 |
+
- python-dotenv>=1.0.0
|
| 58 |
+
- configparser>=5.3.0
|
| 59 |
+
|
| 60 |
+
# Logging and Monitoring
|
| 61 |
+
- colorlog>=6.7.0
|
| 62 |
+
|
| 63 |
+
# Development and Testing
|
| 64 |
+
- pytest>=7.4.0
|
| 65 |
+
- pytest-asyncio>=0.21.0
|
| 66 |
+
- black>=23.0.0
|
| 67 |
+
- flake8>=6.0.0
|
| 68 |
+
|
| 69 |
+
# System and File Operations
|
| 70 |
+
- pathlib2>=2.3.7
|
| 71 |
+
- tqdm>=4.65.0
|
| 72 |
+
|
| 73 |
+
# Pip-only Dependencies
|
| 74 |
+
- pip:
|
| 75 |
+
# Arabic NLP - CAMeL Tools
|
| 76 |
+
- camel-tools>=1.5.2
|
| 77 |
+
|
| 78 |
+
# Advanced Embeddings and Transformers
|
| 79 |
+
- sentence-transformers>=2.2.2
|
| 80 |
+
- transformers>=4.35.0
|
| 81 |
+
- tokenizers>=0.15.0
|
| 82 |
+
- accelerate>=0.24.0
|
| 83 |
+
|
| 84 |
+
# Specialized Embedding Models
|
| 85 |
+
- InstructorEmbedding>=1.0.1
|
| 86 |
+
- FlagEmbedding>=1.2.0
|
| 87 |
+
|
| 88 |
+
# PDF Processing Libraries
|
| 89 |
+
- PyMuPDF>=1.23.0
|
| 90 |
+
- pdfplumber>=0.10.0
|
| 91 |
+
- pymupdf-fonts>=1.0.5
|
| 92 |
+
|
| 93 |
+
# OCR and Image Processing
|
| 94 |
+
- easyocr>=1.7.0
|
| 95 |
+
- opencv-python>=4.8.0
|
| 96 |
+
- pytesseract>=0.3.10
|
| 97 |
+
|
| 98 |
+
# Google AI Integration
|
| 99 |
+
- google-generativeai>=0.3.0
|
| 100 |
+
- google-auth>=2.23.0
|
| 101 |
+
- google-auth-oauthlib>=1.1.0
|
| 102 |
+
- google-auth-httplib2>=0.2.0
|
| 103 |
+
|
| 104 |
+
# Advanced Text Processing
|
| 105 |
+
- arabic-reshaper>=3.0.0
|
| 106 |
+
- python-bidi>=0.4.2
|
| 107 |
+
- polyglot>=16.7.4
|
| 108 |
+
- langdetect>=1.0.9
|
| 109 |
+
|
| 110 |
+
# Enhanced Logging and Debugging
|
| 111 |
+
- rich>=13.5.0
|
| 112 |
+
- loguru>=0.7.0
|
| 113 |
+
|
| 114 |
+
# Configuration and Environment
|
| 115 |
+
- hydra-core>=1.3.0
|
| 116 |
+
- omegaconf>=2.3.0
|
| 117 |
+
|
| 118 |
+
# Performance and Optimization
|
| 119 |
+
- numba>=0.58.0
|
| 120 |
+
- cython>=3.0.0
|
| 121 |
+
|
| 122 |
+
# Additional Utilities
|
| 123 |
+
- click>=8.1.0
|
| 124 |
+
- typer>=0.9.0
|
| 125 |
+
- jinja2>=3.1.0
|
| 126 |
+
|
| 127 |
+
# Database and Storage (Optional)
|
| 128 |
+
- sqlalchemy>=2.0.0
|
| 129 |
+
- redis>=5.0.0
|
| 130 |
+
|
| 131 |
+
# HTTP and API Clients
|
| 132 |
+
- requests>=2.31.0
|
| 133 |
+
- httpx>=0.25.0
|
| 134 |
+
- aiohttp>=3.8.0
|
| 135 |
+
|
| 136 |
+
# JSON and Data Serialization
|
| 137 |
+
- orjson>=3.9.0
|
| 138 |
+
- msgpack>=1.0.0
|
| 139 |
+
|
| 140 |
+
# Validation and Type Checking
|
| 141 |
+
- pydantic>=2.4.0
|
| 142 |
+
- typing-extensions>=4.8.0
|
| 143 |
+
|
| 144 |
+
# Development Tools
|
| 145 |
+
- pre-commit>=3.4.0
|
| 146 |
+
- mypy>=1.5.0
|
| 147 |
+
- isort>=5.12.0
|