diff --git "a/modules/ai_finetuning/model_finetuning.py" "b/modules/ai_finetuning/model_finetuning.py"
new file mode 100644--- /dev/null
+++ "b/modules/ai_finetuning/model_finetuning.py"
@@ -0,0 +1,2081 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+وحدة تخصيص وضبط نماذج الذكاء الاصطناعي للمصطلحات التعاقدية المتخصصة
+تتيح هذه الوحدة إمكانية تدريب نماذج الذكاء الاصطناعي على المصطلحات المتخصصة في مجال العقود والمناقصات
+"""
+
+import os
+import sys
+import streamlit as st
+import pandas as pd
+import numpy as np
+import json
+import time
+import datetime
+from typing import List, Dict, Any, Optional, Tuple
+import openai
+import matplotlib.pyplot as plt
+import tempfile
+import csv
+import re
+import random
+from pathlib import Path
+
+# إضافة مسار النظام للوصول للملفات المشتركة
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
+
+# استيراد مكونات واجهة المستخدم
+from utils.components.header import render_header
+from utils.components.credits import render_credits
+from utils.helpers import format_number, format_currency, styled_button
+
+
+class ModelFinetuning:
+ """فئة تخصيص وضبط نماذج الذكاء الاصطناعي"""
+
+ def __init__(self):
+ """تهيئة وحدة تخصيص وضبط نماذج الذكاء الاصطناعي"""
+ # تهيئة مجلدات حفظ البيانات
+ self.data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../data/finetuning"))
+ os.makedirs(self.data_dir, exist_ok=True)
+
+ # تهيئة الملفات والمجلدات الفرعية
+ self.training_data_dir = os.path.join(self.data_dir, "training_data")
+ os.makedirs(self.training_data_dir, exist_ok=True)
+
+ self.models_dir = os.path.join(self.data_dir, "models")
+ os.makedirs(self.models_dir, exist_ok=True)
+
+ self.terminology_file = os.path.join(self.data_dir, "terminology.json")
+
+ # تهيئة حالة الجلسة
+ if 'terminology_data' not in st.session_state:
+ if os.path.exists(self.terminology_file):
+ with open(self.terminology_file, 'r', encoding='utf-8') as f:
+ st.session_state.terminology_data = json.load(f)
+ else:
+ st.session_state.terminology_data = {
+ "terms": [],
+ "training_examples": [],
+ "models": []
+ }
+
+ if 'active_training_job' not in st.session_state:
+ st.session_state.active_training_job = None
+
+ if 'training_results' not in st.session_state:
+ st.session_state.training_results = []
+
+ # ضبط API مفاتيح الذكاء الاصطناعي
+ self.api_key = os.environ.get("OPENAI_API_KEY")
+ self.anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
+
+ def render(self):
+ """عرض واجهة وحدة تخصيص وضبط نماذج الذكاء الاصطناعي"""
+ # عرض الشعار والعنوان الرئيسي
+ render_header("تخصيص وضبط نماذج الذكاء الاصطناعي للمصطلحات التعاقدية المتخصصة")
+
+ # تبويبات الوحدة
+ tabs = st.tabs([
+ "قاموس المصطلحات المتخصصة",
+ "إعداد بيانات التدريب",
+ "تدريب النموذج",
+ "اختبار النموذج",
+ "المساعد المتخصص"
+ ])
+
+ # تبويب قاموس المصطلحات المتخصصة
+ with tabs[0]:
+ self._render_terminology_dictionary()
+
+ # تبويب إعداد بيانات التدريب
+ with tabs[1]:
+ self._render_training_data_setup()
+
+ # تبويب تدريب النموذج
+ with tabs[2]:
+ self._render_model_training()
+
+ # تبويب اختبار النموذج
+ with tabs[3]:
+ self._render_model_testing()
+
+ # تبويب المساعد المتخصص
+ with tabs[4]:
+ self._render_specialized_assistant()
+
+ # عرض حقوق النشر
+ render_credits()
+
+ def _render_terminology_dictionary(self):
+ """عرض قاموس المصطلحات المتخصصة"""
+ st.markdown("""
+
+
📚 قاموس المصطلحات المتخصصة
+
أضف وحرر المصطلحات الفنية المتخصصة في مجال العقود والمناقصات باللغة العربية.
+
هذه المصطلحات ستستخدم لتدريب وضبط نماذج الذكاء الاصطناعي للتعرف عليها بدقة عالية.
+
+ """, unsafe_allow_html=True)
+
+ # إضا��ة مصطلح جديد
+ st.markdown("### إضافة مصطلح جديد")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ term = st.text_input("المصطلح", key="new_term")
+ category = st.selectbox(
+ "الفئة",
+ options=[
+ "شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة",
+ "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية",
+ "محتوى محلي", "أخرى"
+ ],
+ key="new_term_category"
+ )
+
+ with col2:
+ english_term = st.text_input("المصطلح بالإنجليزية (اختياري)", key="new_term_english")
+ importance = st.slider("مستوى الأهمية", 1, 5, 3, key="new_term_importance")
+
+ definition = st.text_area("التعريف", key="new_term_definition")
+ examples = st.text_area("أمثلة على استخدام المصطلح (فصل بين الأمثلة بسطر جديد)", key="new_term_examples")
+
+ # زر إضافة المصطلح
+ if styled_button("إضافة المصطلح", key="add_term", type="primary", icon="➕"):
+ if not term or not definition:
+ st.error("يرجى تعبئة المصطلح والتعريف على الأقل.")
+ else:
+ # إنشاء كائن المصطلح
+ new_term = {
+ "term": term,
+ "definition": definition,
+ "category": category,
+ "english_term": english_term,
+ "importance": importance,
+ "examples": [ex.strip() for ex in examples.split("\n") if ex.strip()],
+ "added_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ }
+
+ # إضافة المصطلح للقائمة
+ st.session_state.terminology_data["terms"].append(new_term)
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ st.success(f"تمت إضافة المصطلح '{term}' بنجاح!")
+ st.rerun()
+
+ # عرض المصطلحات الموجودة
+ st.markdown("### المصطلحات الموجودة")
+
+ terms = st.session_state.terminology_data.get("terms", [])
+
+ if not terms:
+ st.info("لا توجد مصطلحات مضافة. يرجى إضافة مصطلحات جديدة.")
+ else:
+ # تصفية المصطلحات
+ filter_col1, filter_col2 = st.columns(2)
+
+ with filter_col1:
+ filter_category = st.selectbox(
+ "تصفية حسب الفئة",
+ options=["الكل"] + list(set(t.get("category") for t in terms)),
+ key="filter_term_category"
+ )
+
+ with filter_col2:
+ search_query = st.text_input("بحث", key="search_term")
+
+ # تطبيق التصفية
+ filtered_terms = terms
+ if filter_category != "الكل":
+ filtered_terms = [t for t in filtered_terms if t.get("category") == filter_category]
+
+ if search_query:
+ filtered_terms = [
+ t for t in filtered_terms
+ if search_query.lower() in t.get("term", "").lower() or
+ search_query.lower() in t.get("definition", "").lower() or
+ search_query.lower() in t.get("english_term", "").lower()
+ ]
+
+ # عرض المصطلحات المصفاة
+ if not filtered_terms:
+ st.warning("لا توجد مصطلحات تطابق معايير التصفية.")
+ else:
+ # إعداد بيانات للعرض
+ for i, term in enumerate(filtered_terms):
+ with st.expander(f"{term.get('term')} ({term.get('english_term', '')})", expanded=i==0 and len(filtered_terms)<5):
+ term_col1, term_col2 = st.columns([3, 1])
+
+ with term_col1:
+ st.markdown(f"**التعريف:** {term.get('definition')}")
+ st.markdown(f"**الفئة:** {term.get('category')}")
+ st.markdown(f"**المصطلح بالإنجليزية:** {term.get('english_term', '-')}")
+
+ if "examples" in term and term["examples"]:
+ st.markdown("**أمثلة:**")
+ for ex in term["examples"]:
+ st.markdown(f"- {ex}")
+
+ with term_col2:
+ st.markdown(f"**مستوى الأهمية:** {'⭐' * term.get('importance', 3)}")
+ st.markdown(f"**تاريخ الإضافة:** {term.get('added_at', '-')}")
+
+ # أزرار التحرير والحذف
+ if styled_button("تحرير", key=f"edit_term_{i}", type="secondary", icon="✏️"):
+ st.session_state.term_to_edit = i
+
+ if styled_button("حذف", key=f"delete_term_{i}", type="danger", icon="🗑️"):
+ st.session_state.term_to_delete = i
+
+ # معالجة تحرير أو حذف المصطلح
+ if "term_to_edit" in st.session_state:
+ self._render_edit_term_form(st.session_state.term_to_edit, filtered_terms)
+
+ if "term_to_delete" in st.session_state:
+ if st.warning(f"هل أنت متأكد من حذف المصطلح '{filtered_terms[st.session_state.term_to_delete].get('term')}'؟"):
+ if styled_button("تأكيد الحذف", key="confirm_delete", type="danger", icon="🗑️"):
+ # حذف المصطلح
+ term_index = terms.index(filtered_terms[st.session_state.term_to_delete])
+ del st.session_state.terminology_data["terms"][term_index]
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة الحذف
+ del st.session_state.term_to_delete
+
+ st.success("تم حذف المصطلح بنجاح!")
+ st.rerun()
+
+ if styled_button("إلغاء", key="cancel_delete", type="secondary", icon="❌"):
+ del st.session_state.term_to_delete
+ st.rerun()
+
+ # تصدير المصطلحات
+ st.markdown("### تصدير وتوريد المصطلحات")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ if styled_button("تصدير المصطلحات إلى CSV", key="export_terms", type="primary", icon="📤"):
+ self._export_terms_to_csv()
+
+ with col2:
+ uploaded_file = st.file_uploader("استيراد المصطلحات من ملف CSV", type=["csv"], key="import_terms_file")
+
+ if uploaded_file is not None:
+ if styled_button("استيراد المصطلحات", key="import_terms", type="success", icon="📥"):
+ self._import_terms_from_csv(uploaded_file)
+
+ def _render_edit_term_form(self, term_index, terms_list):
+ """عرض نموذج تحرير المصطلح"""
+ term = terms_list[term_index]
+
+ st.markdown("### تحرير المصطلح")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ edited_term = st.text_input("المصطلح", value=term.get("term", ""), key="edit_term_name")
+ edited_category = st.selectbox(
+ "الفئة",
+ options=[
+ "شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة",
+ "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية",
+ "محتوى محلي", "أخرى"
+ ],
+ index=["شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة", "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية", "محتوى محلي", "أخرى"].index(term.get("category", "أخرى")),
+ key="edit_term_category"
+ )
+
+ with col2:
+ edited_english_term = st.text_input("المصطلح بالإنجليزية (اختياري)", value=term.get("english_term", ""), key="edit_term_english")
+ edited_importance = st.slider("مستوى الأهمية", 1, 5, term.get("importance", 3), key="edit_term_importance")
+
+ edited_definition = st.text_area("التعريف", value=term.get("definition", ""), key="edit_term_definition")
+ edited_examples = st.text_area("أمثلة على استخدام المصطلح (فصل بين الأمثلة بسطر جديد)", value="\n".join(term.get("examples", [])), key="edit_term_examples")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ if styled_button("حفظ التغييرات", key="save_edited_term", type="primary", icon="💾"):
+ if not edited_term or not edited_definition:
+ st.error("يرجى تعبئة المصطلح والتعريف على الأقل.")
+ else:
+ # تحديث المصطلح
+ updated_term = {
+ "term": edited_term,
+ "definition": edited_definition,
+ "category": edited_category,
+ "english_term": edited_english_term,
+ "importance": edited_importance,
+ "examples": [ex.strip() for ex in edited_examples.split("\n") if ex.strip()],
+ "added_at": term.get("added_at", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
+ "updated_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ }
+
+ # الحصول على المؤشر الفعلي في القائمة الكاملة
+ all_terms = st.session_state.terminology_data["terms"]
+ actual_index = all_terms.index(term)
+
+ # تحديث المصطلح
+ st.session_state.terminology_data["terms"][actual_index] = updated_term
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة التحرير
+ del st.session_state.term_to_edit
+
+ st.success(f"تم تحديث المصطلح '{edited_term}' بنجاح!")
+ st.rerun()
+
+ with col2:
+ if styled_button("إلغاء", key="cancel_edit_term", type="secondary", icon="❌"):
+ del st.session_state.term_to_edit
+ st.rerun()
+
+ def _export_terms_to_csv(self):
+ """تصدير المصطلحات إلى ملف CSV"""
+ terms = st.session_state.terminology_data.get("terms", [])
+
+ if not terms:
+ st.error("لا توجد مصطلحات للتصدير.")
+ return
+
+ # إنشاء ملف CSV مؤقت
+ with tempfile.NamedTemporaryFile(mode='w+', suffix='.csv', newline='', encoding='utf-8', delete=False) as f:
+ writer = csv.writer(f)
+
+ # كتابة الترويسة
+ writer.writerow([
+ 'المصطلح', 'التعريف', 'الفئة', 'المصطلح بالإنجليزية',
+ 'مستوى الأهمية', 'الأمثلة', 'تاريخ الإضافة'
+ ])
+
+ # كتابة المصطلحات
+ for term in terms:
+ writer.writerow([
+ term.get('term', ''),
+ term.get('definition', ''),
+ term.get('category', ''),
+ term.get('english_term', ''),
+ term.get('importance', 3),
+ '|'.join(term.get('examples', [])),
+ term.get('added_at', '')
+ ])
+
+ # الحصول على مسار الملف
+ csv_path = f.name
+
+ # قراءة الملف وتقديمه للتنزيل
+ with open(csv_path, 'r', encoding='utf-8') as f:
+ csv_data = f.read()
+
+ # تقديم الملف للتنزيل
+ st.download_button(
+ label="تنزيل ملف CSV",
+ data=csv_data,
+ file_name="terminology_dictionary.csv",
+ mime="text/csv"
+ )
+
+ # حذف الملف المؤقت
+ os.unlink(csv_path)
+
+ def _import_terms_from_csv(self, uploaded_file):
+ """استيراد المصطلحات من ملف CSV"""
+ try:
+ # قراءة الملف
+ df = pd.read_csv(uploaded_file, encoding='utf-8')
+
+ # التحقق من وجود الأعمدة المطلوبة
+ required_columns = ['المصطلح', 'التعريف']
+ missing_columns = [col for col in required_columns if col not in df.columns]
+
+ if missing_columns:
+ st.error(f"الملف لا يحتوي على الأعمدة التالية: {', '.join(missing_columns)}")
+ return
+
+ # إضافة المصطلحات
+ terms_added = 0
+ terms_updated = 0
+
+ for _, row in df.iterrows():
+ term = row['المصطلح']
+
+ # البحث عن المصطلح الموجود
+ existing_term = next((t for t in st.session_state.terminology_data["terms"] if t.get("term") == term), None)
+
+ # تحضير كائن المصطلح
+ term_obj = {
+ "term": term,
+ "definition": row.get('التعريف', ''),
+ "category": row.get('الفئة', 'أخرى'),
+ "english_term": row.get('المصطلح بالإنجليزية', ''),
+ "importance": int(row.get('مستوى الأهمية', 3)),
+ "examples": row.get('الأمثلة', '').split('|') if 'الأمثلة' in row else [],
+ "added_at": row.get('تاريخ الإضافة', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ }
+
+ if existing_term:
+ # تحديث المصطلح الموجود
+ index = st.session_state.terminology_data["terms"].index(existing_term)
+ st.session_state.terminology_data["terms"][index] = term_obj
+ terms_updated += 1
+ else:
+ # إضافة مصطلح جديد
+ st.session_state.terminology_data["terms"].append(term_obj)
+ terms_added += 1
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ st.success(f"تم استيراد المصطلحات بنجاح! (تمت إضافة {terms_added} مصطلح جديد، وتحديث {terms_updated} مصطلح موجود)")
+ st.rerun()
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء استيراد المصطلحات: {str(e)}")
+
+ def _render_training_data_setup(self):
+ """عرض إعداد بيانات التدريب"""
+ st.markdown("""
+
+
🔬 إعداد بيانات التدريب
+
قم بإنشاء وتحرير أمثلة التدريب لضبط نماذج الذكاء الاصطناعي على المصطلحات المتخصصة.
+
يمكنك إنشاء أمثلة يدوياً أو استيرادها من ملف أو توليدها تلقائياً باستخدام نماذج الذكاء الاصطناعي الحالية.
+
+ """, unsafe_allow_html=True)
+
+ # تبويبات إعداد البيانات
+ training_tabs = st.tabs(["أمثلة التدريب الحالية", "إنشاء أمثلة يدوياً", "توليد أمثلة تلقائياً", "استيراد وتصدير البيانات"])
+
+ # عرض أمثلة التدريب الحالية
+ with training_tabs[0]:
+ self._render_existing_training_examples()
+
+ # إنشاء أمثلة يدوياً
+ with training_tabs[1]:
+ self._render_manual_example_creation()
+
+ # توليد أمثلة تلقائياً
+ with training_tabs[2]:
+ self._render_automatic_example_generation()
+
+ # استيراد وتصدير البيانات
+ with training_tabs[3]:
+ self._render_import_export_training_data()
+
+ def _render_existing_training_examples(self):
+ """عرض أمثلة التدريب الحالية"""
+ st.markdown("### أمثلة التدريب الحالية")
+
+ examples = st.session_state.terminology_data.get("training_examples", [])
+
+ if not examples:
+ st.info("لا توجد أمثلة تدريب. يرجى إنشاء أمثلة جديدة.")
+ return
+
+ # عرض إحصائيات البيانات
+ st.markdown("#### إحصائيات البيانات")
+
+ total_examples = len(examples)
+ categories = {}
+ terms_used = set()
+
+ for ex in examples:
+ cat = ex.get("category", "غير مصنف")
+ categories[cat] = categories.get(cat, 0) + 1
+
+ for term in ex.get("terms", []):
+ terms_used.add(term)
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("إجمالي الأمثلة", total_examples)
+
+ with col2:
+ st.metric("عدد المصطلحات المستخدمة", len(terms_used))
+
+ with col3:
+ st.metric("عدد الفئات", len(categories))
+
+ # عرض توزيع الفئات
+ st.markdown("#### توزيع الأمثلة حسب الفئة")
+
+ categories_df = pd.DataFrame({
+ "الفئة": list(categories.keys()),
+ "عدد الأمثلة": list(categories.values())
+ })
+
+ fig, ax = plt.subplots(figsize=(10, 6))
+ ax.bar(categories_df["الفئة"], categories_df["عدد الأمثلة"])
+ ax.set_title("توزيع أمثلة التدريب حسب الفئة")
+ ax.set_xlabel("الفئة")
+ ax.set_ylabel("عدد الأمثلة")
+
+ # تدوير أسماء الفئات لتسهيل القراءة
+ plt.xticks(rotation=45, ha='right')
+ plt.tight_layout()
+
+ st.pyplot(fig)
+
+ # تصفية الأمثلة
+ st.markdown("#### تصفية الأمثلة")
+
+ filter_col1, filter_col2 = st.columns(2)
+
+ with filter_col1:
+ filter_category = st.selectbox(
+ "تصفية حسب الفئة",
+ options=["الكل"] + list(categories.keys()),
+ key="filter_example_category"
+ )
+
+ with filter_col2:
+ search_query = st.text_input("بحث في النص", key="search_example")
+
+ # تطبيق التصفية
+ filtered_examples = examples
+ if filter_category != "الكل":
+ filtered_examples = [ex for ex in filtered_examples if ex.get("category") == filter_category]
+
+ if search_query:
+ filtered_examples = [
+ ex for ex in filtered_examples
+ if search_query.lower() in ex.get("input", "").lower() or
+ search_query.lower() in ex.get("output", "").lower()
+ ]
+
+ # عرض الأمثلة المصفاة
+ if not filtered_examples:
+ st.warning("لا توجد أمثلة تطابق معايير التصفية.")
+ else:
+ # عرض عدد محدود من الأمثلة في كل صفحة
+ examples_per_page = 10
+ total_pages = (len(filtered_examples) - 1) // examples_per_page + 1
+
+ # التنقل بين الصفحات
+ col1, col2, col3 = st.columns([1, 3, 1])
+
+ with col2:
+ page = st.slider("الصفحة", 1, max(1, total_pages), 1, key="examples_page")
+
+ start_idx = (page - 1) * examples_per_page
+ end_idx = min(start_idx + examples_per_page, len(filtered_examples))
+
+ page_examples = filtered_examples[start_idx:end_idx]
+
+ # عرض الأمثلة
+ for i, example in enumerate(page_examples):
+ example_idx = start_idx + i
+ with st.expander(f"مثال #{example_idx+1} - {example.get('category', 'غير مصنف')}", expanded=i==0 and len(page_examples)<5):
+ ex_col1, ex_col2 = st.columns([3, 1])
+
+ with ex_col1:
+ st.markdown("**النص المدخل:**")
+ st.markdown(f"```\n{example.get('input', '')}\n```")
+
+ st.markdown("**النص المتوقع:**")
+ st.markdown(f"```\n{example.get('output', '')}\n```")
+
+ with ex_col2:
+ st.markdown("**الفئة:** " + example.get('category', 'غير مصنف'))
+ st.markdown("**المصطلحات المستخدمة:**")
+ for term in example.get("terms", []):
+ st.markdown(f"- {term}")
+
+ # تاريخ الإنشاء
+ if "created_at" in example:
+ st.markdown(f"**تاريخ الإنشاء:** {example['created_at']}")
+
+ # أزرار التحرير والحذف
+ if styled_button("تحرير", key=f"edit_example_{example_idx}", type="secondary", icon="✏️"):
+ st.session_state.example_to_edit = example_idx
+
+ if styled_button("حذف", key=f"delete_example_{example_idx}", type="danger", icon="🗑️"):
+ st.session_state.example_to_delete = example_idx
+
+ # معالجة تحرير أو حذف مثال
+ if "example_to_edit" in st.session_state:
+ self._render_edit_example_form(st.session_state.example_to_edit, filtered_examples)
+
+ if "example_to_delete" in st.session_state:
+ if st.warning(f"هل أنت متأكد من حذف المثال #{st.session_state.example_to_delete+1}؟"):
+ if styled_button("تأكيد الحذف", key="confirm_delete_example", type="danger", icon="🗑️"):
+ # حذف المثال
+ example_index = examples.index(filtered_examples[st.session_state.example_to_delete])
+ del st.session_state.terminology_data["training_examples"][example_index]
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة الحذف
+ del st.session_state.example_to_delete
+
+ st.success("تم حذف المثال بنجاح!")
+ st.rerun()
+
+ if styled_button("إلغاء", key="cancel_delete_example", type="secondary", icon="❌"):
+ del st.session_state.example_to_delete
+ st.rerun()
+
+ def _render_edit_example_form(self, example_index, examples_list):
+ """عرض نموذج تحرير مثال التدريب"""
+ example = examples_list[example_index]
+
+ st.markdown("### تحرير مثال التدريب")
+
+ # اختيار المصطلحات المستخدمة
+ all_terms = [term.get("term") for term in st.session_state.terminology_data.get("terms", [])]
+ selected_terms = st.multiselect(
+ "المصطلحات المستخدمة في المثال",
+ options=all_terms,
+ default=example.get("terms", []),
+ key="edit_example_terms"
+ )
+
+ # إدخال النص المدخل والمتوقع
+ input_text = st.text_area("النص المدخل", value=example.get("input", ""), key="edit_example_input", height=150)
+ output_text = st.text_area("النص المتوقع", value=example.get("output", ""), key="edit_example_output", height=150)
+
+ # اختيار الفئة
+ category = st.selectbox(
+ "الفئة",
+ options=["شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة", "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية", "محتوى محلي", "أخرى"],
+ index=["شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة", "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية", "محتوى محلي", "أخرى"].index(example.get("category", "أخرى")),
+ key="edit_example_category"
+ )
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ if styled_button("حفظ التغييرات", key="save_edited_example", type="primary", icon="💾"):
+ if not input_text or not output_text:
+ st.error("يرجى تعبئة النص المدخل والنص المتوقع.")
+ else:
+ # تحديث المثال
+ updated_example = {
+ "input": input_text,
+ "output": output_text,
+ "category": category,
+ "terms": selected_terms,
+ "created_at": example.get("created_at", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
+ "updated_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ }
+
+ # الحصول على المؤشر الفعلي في القائمة الكاملة
+ all_examples = st.session_state.terminology_data["training_examples"]
+ actual_index = all_examples.index(example)
+
+ # تحديث المثال
+ st.session_state.terminology_data["training_examples"][actual_index] = updated_example
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة التحرير
+ del st.session_state.example_to_edit
+
+ st.success("تم تحديث مثال التدريب بنجاح!")
+ st.rerun()
+
+ with col2:
+ if styled_button("إلغاء", key="cancel_edit_example", type="secondary", icon="❌"):
+ del st.session_state.example_to_edit
+ st.rerun()
+
+ def _render_manual_example_creation(self):
+ """عرض نموذج إنشاء أمثلة يدوياً"""
+ st.markdown("### إنشاء مثال تدريب جديد")
+
+ # اختيار المصطلحات المستخدمة
+ all_terms = [term.get("term") for term in st.session_state.terminology_data.get("terms", [])]
+ selected_terms = st.multiselect(
+ "المصطلحات المستخدمة في المثال",
+ options=all_terms,
+ key="new_example_terms"
+ )
+
+ # اختيار الفئة
+ category = st.selectbox(
+ "الفئة",
+ options=["شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة", "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية", "محتوى محلي", "أخرى"],
+ key="new_example_category"
+ )
+
+ # إدخال النص المدخل والمتوقع
+ st.markdown("**النص المدخل** (نص السؤال أو الطلب)")
+ input_text = st.text_area("", key="new_example_input", height=150, placeholder="مثال: قم بشرح معنى مصطلح 'محتوى محلي' وكيفية حسابه في المشاريع الحكومية.")
+
+ st.markdown("**النص المتوقع** (الإجابة المثالية التي يجب أن يقدمها النموذج)")
+ output_text = st.text_area("", key="new_example_output", height=150, placeholder="مثال: المحتوى المحلي (Local Content) هو نسبة القيمة المحلية المضافة في المنتجات والخدمات المقدمة في المشروع...")
+
+ # عرض تعريفات المصطلحات المختارة للمساعدة
+ if selected_terms:
+ with st.expander("تعريفات المصطلحات المختارة", expanded=True):
+ for term_name in selected_terms:
+ term = next((t for t in st.session_state.terminology_data.get("terms", []) if t.get("term") == term_name), None)
+ if term:
+ st.markdown(f"**{term_name}**: {term.get('definition', '')}")
+
+ # زر إضافة المثال
+ if styled_button("إضافة مثال التدريب", key="add_example", type="primary", icon="➕"):
+ if not input_text or not output_text:
+ st.error("يرجى تعبئة النص المدخل والنص المتوقع.")
+ else:
+ # إنشاء كائن المثال
+ new_example = {
+ "input": input_text,
+ "output": output_text,
+ "category": category,
+ "terms": selected_terms,
+ "created_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ }
+
+ # إضافة المثال للقائمة
+ if "training_examples" not in st.session_state.terminology_data:
+ st.session_state.terminology_data["training_examples"] = []
+
+ st.session_state.terminology_data["training_examples"].append(new_example)
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ st.success("تم إضافة مثال التدريب بنجاح!")
+ st.rerun()
+
+ def _render_automatic_example_generation(self):
+ """عرض واجهة توليد أمثلة تلقائياً"""
+ st.markdown("### توليد أمثلة تدريب تلقائياً")
+
+ # التحقق من وجود مفاتيح API
+ if not self.api_key and not self.anthropic_api_key:
+ st.warning("لم يتم العثور على مفاتيح API للذكاء الاصطناعي. يرجى إضافة OPENAI_API_KEY أو ANTHROPIC_API_KEY إلى المتغيرات البيئية.")
+ return
+
+ # اختيار نموذج الذكاء الاصطناعي
+ ai_models = []
+
+ if self.api_key:
+ ai_models.extend(["gpt-4o", "gpt-3.5-turbo"])
+
+ if self.anthropic_api_key:
+ ai_models.extend(["claude-3-7-sonnet-20250219"])
+
+ selected_model = st.selectbox(
+ "اختر نموذج الذكاء الاصطناعي",
+ options=ai_models,
+ key="auto_gen_model"
+ )
+
+ # اختيار المصطلحات لتوليد أمثلة حولها
+ all_terms = [term.get("term") for term in st.session_state.terminology_data.get("terms", [])]
+ selected_terms = st.multiselect(
+ "اختر المصطلحات لتوليد أمثلة حولها",
+ options=all_terms,
+ key="auto_gen_terms"
+ )
+
+ # اختيار عدد الأمثلة المراد توليدها
+ num_examples = st.slider("عدد الأمثلة لكل مصطلح", 1, 5, 2, key="auto_gen_count")
+
+ # اختيار الفئات المرغوبة
+ selected_categories = st.multiselect(
+ "اختر الفئات المرغوبة للأمثلة",
+ options=["شروط تعاقدية", "مواصفات فنية", "مستندات مناقصة", "بنود مالية", "جداول كميات", "ضمانات", "مصطلحات قانونية", "محتوى محلي", "أخرى"],
+ default=["شروط تعاقدية", "مستندات مناقصة", "مصطلحات قانونية"],
+ key="auto_gen_categories"
+ )
+
+ # زر توليد الأمثلة
+ if styled_button("توليد الأمثلة", key="generate_examples", type="primary", icon="✨"):
+ if not selected_terms:
+ st.error("يرجى اختيار مصطلح واحد على الأقل.")
+ elif not selected_categories:
+ st.error("يرجى اختيار فئة واحدة على الأقل.")
+ else:
+ # عرض شريط التقدم
+ progress_bar = st.progress(0)
+ status_text = st.empty()
+
+ # تجهيز المصطلحات وتعريفاتها
+ terms_with_definitions = {}
+ for term_name in selected_terms:
+ term = next((t for t in st.session_state.terminology_data.get("terms", []) if t.get("term") == term_name), None)
+ if term:
+ terms_with_definitions[term_name] = term.get('definition', '')
+
+ # توليد الأمثلة
+ generated_examples = []
+ total_iterations = len(selected_terms) * len(selected_categories)
+ current_iteration = 0
+
+ for term_name, definition in terms_with_definitions.items():
+ for category in selected_categories:
+ current_iteration += 1
+ progress = current_iteration / total_iterations
+ progress_bar.progress(progress)
+ status_text.text(f"جاري توليد أمثلة للمصطلح '{term_name}' في الفئة '{category}'...")
+
+ # توليد أمثلة لهذا المصطلح والفئة
+ examples = self._generate_examples_with_ai(
+ term_name,
+ definition,
+ category,
+ num_examples,
+ selected_model
+ )
+
+ generated_examples.extend(examples)
+
+ # إضافة الأمثلة المولدة إلى البيانات
+ if "training_examples" not in st.session_state.terminology_data:
+ st.session_state.terminology_data["training_examples"] = []
+
+ st.session_state.terminology_data["training_examples"].extend(generated_examples)
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إكمال شريط التقدم
+ progress_bar.progress(1.0)
+ status_text.text(f"تم توليد {len(generated_examples)} مثال بنجاح!")
+
+ st.success(f"تم توليد {len(generated_examples)} مثال بنجاح!")
+ st.rerun()
+
+ def _generate_examples_with_ai(self, term_name, definition, category, num_examples, model):
+ """توليد أمثلة باستخدام الذكاء الاصطناعي"""
+ # تحضير الرسالة
+ prompt = f"""
+ أنت خبير في توليد أمثلة تدريب لضبط نماذج الذكاء الاصطناعي على المصطلحات التعاقدية والهندسية المتخصصة باللغة العربية.
+
+ أريد منك توليد {num_examples} مثال تدريب للمصطلح التالي:
+
+ المصطلح: {term_name}
+ التعريف: {definition}
+ الفئة: {category}
+
+ لكل مثال، قم بتوليد:
+ 1. نص المدخل (سؤال أو طلب حول المصطلح)
+ 2. نص المخرج المتوقع (الإجابة المثالية التي يجب أن يقدمها النموذج)
+
+ تأكد من:
+ - جعل الأمثلة متنوعة وواقعية
+ - تضمين سياقات مختلفة لاستخدام المصطلح
+ - استخدام أسلوب مناسب لوثائق المناقصات والعقود
+ - تضمين تفاصيل تقنية دقيقة عند الحاجة
+
+ قم بإرجاع النتائج بتنسيق JSON كما يلي:
+
+ ```json
+ [
+ {
+ "input": "نص المدخل للمثال الأول",
+ "output": "نص المخرج المتوقع للمثال الأول"
+ },
+ {
+ "input": "نص المدخل للمثال الثاني",
+ "output": "نص المخرج المتوقع للمثال الثاني"
+ },
+ ...
+ ]
+ ```
+
+ أرجع البيانات بتنسيق JSON فقط.
+ """
+
+ try:
+ # استدعاء API المناسب حسب النموذج المختار
+ if "gpt" in model and self.api_key:
+ # استخدام OpenAI API
+ openai.api_key = self.api_key
+
+ response = openai.chat.completions.create(
+ model=model,
+ messages=[
+ {"role": "system", "content": "أنت مساعد محترف متخصص في توليد بيانات تدريب لضبط نماذج الذكاء الاصطناعي."},
+ {"role": "user", "content": prompt}
+ ],
+ temperature=0.7,
+ response_format={"type": "json_object"}
+ )
+
+ # استخراج النتيجة
+ result_text = response.choices[0].message.content
+
+ # تنظيف النص واستخراج JSON
+ json_match = re.search(r'```json\s*(.*?)\s*```', result_text, re.DOTALL)
+ if json_match:
+ result_json = json_match.group(1)
+ else:
+ result_json = result_text
+
+ # تحليل JSON
+ examples_data = json.loads(result_json)
+
+ # إذا كان الناتج كائن JSON بخاصية examples
+ if isinstance(examples_data, dict) and "examples" in examples_data:
+ examples_data = examples_data["examples"]
+
+ elif "claude" in model and self.anthropic_api_key:
+ # استخدام Anthropic API
+ from anthropic import Anthropic
+
+ anthropic_client = Anthropic(api_key=self.anthropic_api_key)
+
+ response = anthropic_client.messages.create(
+ model=model,
+ max_tokens=4000,
+ messages=[
+ {"role": "user", "content": prompt}
+ ],
+ temperature=0.7
+ )
+
+ # استخراج النتيجة
+ result_text = response.content[0].text
+
+ # تنظيف النص واستخراج JSON
+ json_match = re.search(r'```json\s*(.*?)\s*```', result_text, re.DOTALL)
+ if json_match:
+ result_json = json_match.group(1)
+ else:
+ result_json = result_text
+
+ # تحليل JSON
+ examples_data = json.loads(result_json)
+
+ # إذا كان الناتج كائن JSON بخاصية examples
+ if isinstance(examples_data, dict) and "examples" in examples_data:
+ examples_data = examples_data["examples"]
+
+ else:
+ # في حالة عدم توفر النموذج المطلوب
+ return []
+
+ # تحويل البيانات إلى الصيغة المطلوبة للأمثلة
+ formatted_examples = []
+
+ for example in examples_data:
+ formatted_examples.append({
+ "input": example["input"],
+ "output": example["output"],
+ "category": category,
+ "terms": [term_name],
+ "created_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ "generated_by": model
+ })
+
+ return formatted_examples
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء توليد الأمثلة: {str(e)}")
+ return []
+
+ def _render_import_export_training_data(self):
+ """عرض واجهة استيراد وتصدير بيانات التدريب"""
+ st.markdown("### استيراد وتصدير بيانات التدريب")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.markdown("#### تصدير بيانات التدريب")
+
+ export_format = st.selectbox(
+ "صيغة التصدير",
+ options=["JSON", "JSONL", "CSV"],
+ key="export_format"
+ )
+
+ if styled_button("تصدير البيانات", key="export_data", type="primary", icon="📤"):
+ self._export_training_data(export_format)
+
+ with col2:
+ st.markdown("#### استيراد بيانات التدريب")
+
+ import_format = st.selectbox(
+ "صيغة الاستيراد",
+ options=["JSON", "JSONL", "CSV"],
+ key="import_format"
+ )
+
+ uploaded_file = st.file_uploader("استيراد بيانات التدريب", type=["json", "jsonl", "csv"], key="import_data_file")
+
+ if uploaded_file is not None:
+ if styled_button("استيراد البيانات", key="import_data", type="success", icon="📥"):
+ self._import_training_data(uploaded_file, import_format)
+
+ def _export_training_data(self, format):
+ """تصدير بيانات التدريب إلى ملف"""
+ examples = st.session_state.terminology_data.get("training_examples", [])
+
+ if not examples:
+ st.error("لا توجد بيانات تدريب للتصدير.")
+ return
+
+ try:
+ if format == "JSON":
+ # تصدير إلى ملف JSON
+ with tempfile.NamedTemporaryFile(mode='w+', suffix='.json', encoding='utf-8', delete=False) as f:
+ json.dump(examples, f, ensure_ascii=False, indent=2)
+ json_path = f.name
+
+ # قراءة الملف وتقديمه للتنزيل
+ with open(json_path, 'r', encoding='utf-8') as f:
+ json_data = f.read()
+
+ st.download_button(
+ label="تنزيل ملف JSON",
+ data=json_data,
+ file_name="training_data.json",
+ mime="application/json"
+ )
+
+ # حذف الملف المؤقت
+ os.unlink(json_path)
+
+ elif format == "JSONL":
+ # تصدير إلى ملف JSONL
+ jsonl_content = ""
+ for example in examples:
+ jsonl_content += json.dumps(example, ensure_ascii=False) + "\n"
+
+ st.download_button(
+ label="تنزيل ملف JSONL",
+ data=jsonl_content,
+ file_name="training_data.jsonl",
+ mime="application/jsonl"
+ )
+
+ elif format == "CSV":
+ # تصدير إلى ملف CSV
+ with tempfile.NamedTemporaryFile(mode='w+', suffix='.csv', newline='', encoding='utf-8', delete=False) as f:
+ writer = csv.writer(f)
+
+ # كتابة الترويسة
+ writer.writerow([
+ 'النص المدخل', 'النص المتوقع', 'الفئة', 'المصطلحات', 'تاريخ الإنشاء', 'تم التوليد بواسطة'
+ ])
+
+ # كتابة البيانات
+ for example in examples:
+ writer.writerow([
+ example.get('input', ''),
+ example.get('output', ''),
+ example.get('category', ''),
+ '|'.join(example.get('terms', [])),
+ example.get('created_at', ''),
+ example.get('generated_by', 'يدوي')
+ ])
+
+ # الحصول على مسار الملف
+ csv_path = f.name
+
+ # قراءة الملف وتقديمه للتنزيل
+ with open(csv_path, 'r', encoding='utf-8') as f:
+ csv_data = f.read()
+
+ st.download_button(
+ label="تنزيل ملف CSV",
+ data=csv_data,
+ file_name="training_data.csv",
+ mime="text/csv"
+ )
+
+ # حذف الملف المؤقت
+ os.unlink(csv_path)
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء تصدير البيانات: {str(e)}")
+
+ def _import_training_data(self, uploaded_file, format):
+ """استيراد بيانات التدريب من ملف"""
+ try:
+ examples = []
+
+ if format == "JSON":
+ # استيراد من ملف JSON
+ content = uploaded_file.read().decode('utf-8')
+ examples = json.loads(content)
+
+ elif format == "JSONL":
+ # استيراد من ملف JSONL
+ content = uploaded_file.read().decode('utf-8')
+
+ for line in content.strip().split('\n'):
+ if line.strip():
+ examples.append(json.loads(line))
+
+ elif format == "CSV":
+ # استيراد من ملف CSV
+ df = pd.read_csv(uploaded_file, encoding='utf-8')
+
+ for _, row in df.iterrows():
+ example = {
+ "input": row.get('النص المدخل', ''),
+ "output": row.get('النص المتوقع', ''),
+ "category": row.get('الفئة', 'أخرى'),
+ "terms": row.get('المصطلحات', '').split('|') if pd.notna(row.get('المصطلحات', '')) else [],
+ "created_at": row.get('تاريخ الإنشاء', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
+ "generated_by": row.get('تم التوليد بواسطة', 'مستورد')
+ }
+
+ examples.append(example)
+
+ # إضافة الأمثلة المستوردة
+ if "training_examples" not in st.session_state.terminology_data:
+ st.session_state.terminology_data["training_examples"] = []
+
+ # فلترة الأمثلة الصحيحة
+ valid_examples = []
+ for ex in examples:
+ if "input" in ex and "output" in ex:
+ valid_examples.append(ex)
+
+ # إضافة الأمثلة وحفظ البيانات
+ if valid_examples:
+ st.session_state.terminology_data["training_examples"].extend(valid_examples)
+ self._save_terminology_data()
+
+ st.success(f"تم استيراد {len(valid_examples)} مثال بنجاح!")
+ st.rerun()
+ else:
+ st.error("لم يتم العثور على أمثلة صالحة في الملف.")
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء استيراد البيانات: {str(e)}")
+
+ def _render_model_training(self):
+ """عرض واجهة تدريب النموذج"""
+ st.markdown("""
+
+
🧠 تدريب النموذج
+
قم بتدريب نموذج الذكاء الاصطناعي على المصطلحات المتخصصة باستخدام أمثلة التدريب.
+
يمكنك اختيار النموذج الأساسي والإعدادات المناسبة لعملية التدريب.
+
+ """, unsafe_allow_html=True)
+
+ # التحقق من وجود بيانات تدريب كافية
+ examples = st.session_state.terminology_data.get("training_examples", [])
+ if len(examples) < 10:
+ st.warning(f"عدد أمثلة التدريب الحالية ({len(examples)}) غير كافٍ للتدريب. يُنصح بوجود 10 أمثلة على الأقل.")
+
+ # تبويبات تدريب النموذج
+ training_tabs = st.tabs(["إعداد التدريب", "نماذج سابقة", "وظائف التدريب النشطة"])
+
+ # تبويب إعداد التدريب
+ with training_tabs[0]:
+ self._render_training_setup()
+
+ # تبويب النماذج السابقة
+ with training_tabs[1]:
+ self._render_previous_models()
+
+ # تبويب وظائف التدريب النشطة
+ with training_tabs[2]:
+ self._render_active_training_jobs()
+
+ def _render_training_setup(self):
+ """عرض إعدادات تدريب النموذج"""
+ st.markdown("### إعداد عملية التدريب")
+
+ # التحقق من وجود مفاتيح API
+ if not self.api_key and not self.anthropic_api_key:
+ st.warning("لم يتم العثور على مفاتيح API للذكاء الاصطناعي. يرجى إضافة OPENAI_API_KEY أو ANTHROPIC_API_KEY إلى المتغيرات البيئية.")
+ return
+
+ # اختيار نموذج الذكاء الاصطناعي الأساسي
+ provider_options = []
+ if self.api_key:
+ provider_options.append("OpenAI")
+ if self.anthropic_api_key:
+ provider_options.append("Anthropic")
+
+ provider = st.selectbox(
+ "مزود الذكاء الاصطناعي",
+ options=provider_options,
+ key="training_provider"
+ )
+
+ # الإعدادات حسب المزود
+ if provider == "OpenAI":
+ # نماذج OpenAI المتاحة للضبط
+ base_model = st.selectbox(
+ "النموذج الأساسي",
+ options=["gpt-3.5-turbo-0125", "gpt-4o-mini"],
+ key="openai_base_model"
+ )
+
+ # إعدادات التدريب
+ col1, col2 = st.columns(2)
+
+ with col1:
+ n_epochs = st.slider("عدد الحقب (Epochs)", 1, 4, 2, key="openai_epochs")
+ batch_size = st.selectbox("حجم الدفعة (Batch Size)", options=[1, 2, 4, 8], index=1, key="openai_batch_size")
+
+ with col2:
+ learning_rate_multiplier = st.slider("مضاعف معدل التعلم", 0.1, 2.0, 1.0, 0.1, key="openai_lr")
+ suffix = st.text_input("لاحقة اسم النموذج", value="arabic-contracts-expert", key="openai_suffix")
+
+ # زر بدء التدريب
+ if styled_button("بدء التدريب", key="start_openai_training", type="primary", icon="🚀"):
+ # التحقق من وجود بيانات كافية
+ examples = st.session_state.terminology_data.get("training_examples", [])
+ if len(examples) < 10:
+ st.error("عدد أ��ثلة التدريب الحالية قليل جداً. يُفضل وجود على الأقل 10 أمثلة للتدريب.")
+ else:
+ # التأكيد على بدء التدريب
+ confirm = st.warning(f"سيتم بدء عملية تدريب نموذج {base_model} باستخدام {len(examples)} مثال. هل أنت متأكد؟")
+ if styled_button("تأكيد بدء التدريب", key="confirm_openai_training", type="success", icon="✅"):
+ # توجيه بيانات التدريب لصيغة OpenAI
+ formatted_data = self._format_training_data_for_openai(examples)
+
+ # بدء التدريب
+ self._start_openai_training(
+ base_model=base_model,
+ training_data=formatted_data,
+ n_epochs=n_epochs,
+ batch_size=batch_size,
+ learning_rate_multiplier=learning_rate_multiplier,
+ suffix=suffix
+ )
+
+ elif provider == "Anthropic":
+ st.info("ضبط نماذج Anthropic غير متاح حالياً في واجهة البرمجة العامة. يمكنك استخدام أمثلة التدريب مع المساعد المتخصص.")
+
+ def _format_training_data_for_openai(self, examples):
+ """تنسيق بيانات التدريب لواجهة برمجة OpenAI"""
+ formatted_examples = []
+
+ for example in examples:
+ formatted_examples.append({
+ "messages": [
+ {"role": "user", "content": example.get("input", "")},
+ {"role": "assistant", "content": example.get("output", "")}
+ ]
+ })
+
+ return formatted_examples
+
+ def _start_openai_training(self, base_model, training_data, n_epochs, batch_size, learning_rate_multiplier, suffix):
+ """بدء عملية تدريب نموذج OpenAI"""
+ try:
+ # تهيئة واجهة برمجة OpenAI
+ openai.api_key = self.api_key
+
+ # إنشاء ملف تدريب
+ training_file_path = os.path.join(self.training_data_dir, f"training_data_{int(time.time())}.jsonl")
+
+ with open(training_file_path, 'w', encoding='utf-8') as f:
+ for example in training_data:
+ f.write(json.dumps(example, ensure_ascii=False) + "\n")
+
+ # رفع ملف التدريب إلى OpenAI
+ with open(training_file_path, 'rb') as f:
+ response = openai.files.create(
+ file=f,
+ purpose="fine-tune"
+ )
+
+ file_id = response.id
+
+ # بدء وظيفة التدريب
+ response = openai.fine_tuning.jobs.create(
+ training_file=file_id,
+ model=base_model,
+ hyperparameters={
+ "n_epochs": n_epochs,
+ "batch_size": batch_size,
+ "learning_rate_multiplier": learning_rate_multiplier
+ },
+ suffix=suffix
+ )
+
+ job_id = response.id
+
+ # تخزين معلومات وظيفة التدريب
+ training_job = {
+ "job_id": job_id,
+ "provider": "OpenAI",
+ "base_model": base_model,
+ "n_epochs": n_epochs,
+ "batch_size": batch_size,
+ "learning_rate_multiplier": learning_rate_multiplier,
+ "suffix": suffix,
+ "status": "running",
+ "file_id": file_id,
+ "file_path": training_file_path,
+ "examples_count": len(training_data),
+ "started_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ "finished_at": None,
+ "fine_tuned_model": None
+ }
+
+ # إضافة الوظيفة إلى حالة الجلسة
+ st.session_state.active_training_job = training_job
+
+ # إضافة الوظيفة إلى قائمة وظائف التدريب
+ if "training_jobs" not in st.session_state.terminology_data:
+ st.session_state.terminology_data["training_jobs"] = []
+
+ st.session_state.terminology_data["training_jobs"].append(training_job)
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ st.success(f"تم بدء وظيفة التدريب بنجاح! معرف الوظيفة: {job_id}")
+ st.info("يمكنك متابعة حالة التدريب من تبويب 'وظائف التدريب النشطة'.")
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء بدء عملية التدريب: {str(e)}")
+
+ def _render_previous_models(self):
+ """عرض النماذج المدربة سابقاً"""
+ st.markdown("### النماذج المدربة سابقاً")
+
+ # الحصول على النماذج المدربة
+ models = st.session_state.terminology_data.get("models", [])
+
+ if not models:
+ st.info("لا توجد نماذج مدربة سابقاً.")
+ return
+
+ # عرض النماذج
+ for i, model in enumerate(models):
+ with st.expander(f"{model.get('model_id')} - {model.get('base_model')}", expanded=i==0):
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ st.markdown(f"**معرف النموذج:** {model.get('model_id')}")
+ st.markdown(f"**النموذج الأساسي:** {model.get('base_model')}")
+ st.markdown(f"**عدد أمثلة التدريب:** {model.get('examples_count')}")
+ st.markdown(f"**تاريخ الإنشاء:** {model.get('created_at')}")
+
+ # عرض مؤشرات الأداء إن وجدت
+ if "metrics" in model:
+ st.markdown("#### مؤشرات الأداء")
+
+ metrics = model.get("metrics", {})
+ for metric_name, metric_value in metrics.items():
+ st.markdown(f"**{metric_name}:** {metric_value}")
+
+ with col2:
+ # أزرار الاستخدام والحذف
+ if styled_button("استخدام النموذج", key=f"use_model_{i}", type="primary", icon="✅"):
+ st.session_state.selected_model = model.get('model_id')
+ st.success(f"تم اختيار النموذج {model.get('model_id')} للاستخدام.")
+
+ if styled_button("حذف النموذج", key=f"delete_model_{i}", type="danger", icon="🗑️"):
+ st.session_state.model_to_delete = i
+
+ # عرض الوصف والملاحظات
+ st.markdown(f"**الوصف:** {model.get('description', 'لا يوجد وصف.')}")
+
+ # عرض النماذج المستخدمة في التدريب
+ if "examples_preview" in model and model["examples_preview"]:
+ with st.expander("عينة من أمثلة التدريب"):
+ for j, example in enumerate(model["examples_preview"]):
+ st.markdown(f"**مثال #{j+1}**")
+ st.markdown(f"**المدخل:** {example.get('input')}")
+ st.markdown(f"**المخرج:** {example.get('output')}")
+ st.markdown("---")
+
+ # معالجة حذف النموذج
+ if "model_to_delete" in st.session_state:
+ if st.warning(f"هل أنت متأكد من حذف النموذج '{models[st.session_state.model_to_delete].get('model_id')}'؟"):
+ if styled_button("تأكيد الحذف", key="confirm_delete_model", type="danger", icon="🗑️"):
+ # حذف النموذج
+ del st.session_state.terminology_data["models"][st.session_state.model_to_delete]
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة الحذف
+ del st.session_state.model_to_delete
+
+ st.success("تم حذف النموذج بنجاح!")
+ st.rerun()
+
+ if styled_button("إلغاء", key="cancel_delete_model", type="secondary", icon="❌"):
+ del st.session_state.model_to_delete
+ st.rerun()
+
+ def _render_active_training_jobs(self):
+ """عرض وظائف التدريب النشطة"""
+ st.markdown("### وظائف التدريب النشطة")
+
+ # الحصول على وظائف التدريب
+ jobs = st.session_state.terminology_data.get("training_jobs", [])
+
+ # فرز الوظائف حسب الحالة
+ active_jobs = [job for job in jobs if job.get("status") in ["running", "validating_files", "queued"]]
+ completed_jobs = [job for job in jobs if job.get("status") == "succeeded"]
+ failed_jobs = [job for job in jobs if job.get("status") in ["failed", "cancelled"]]
+
+ # زر تحديث حالة الوظائف
+ if styled_button("تحديث حالة الوظائف", key="refresh_jobs", type="primary", icon="🔄"):
+ self._refresh_training_jobs_status()
+
+ # عرض الوظائف النشطة
+ if active_jobs:
+ st.markdown("#### الوظائف النشطة")
+
+ for i, job in enumerate(active_jobs):
+ with st.expander(f"{job.get('job_id')} - {job.get('base_model')}", expanded=True):
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ st.markdown(f"**معرف الوظيفة:** {job.get('job_id')}")
+ st.markdown(f"**النموذج الأساسي:** {job.get('base_model')}")
+ st.markdown(f"**الحالة:** {job.get('status')}")
+ st.markdown(f"**تاريخ البدء:** {job.get('started_at')}")
+
+ # عرض تقدم التدريب إن وجد
+ if "progress" in job:
+ progress = job.get("progress", 0)
+ st.progress(progress)
+ st.markdown(f"**التقدم:** {progress*100:.1f}%")
+
+ with col2:
+ # زر إلغاء الوظيفة
+ if styled_button("إلغاء الوظيفة", key=f"cancel_job_{i}", type="danger", icon="⛔"):
+ st.session_state.job_to_cancel = i
+
+ # عرض معلومات إضافية
+ st.markdown(f"**عدد الحقب:** {job.get('n_epochs')}")
+ st.markdown(f"**حجم الدفعة:** {job.get('batch_size')}")
+ st.markdown(f"**مضاعف معدل التعلم:** {job.get('learning_rate_multiplier')}")
+ else:
+ st.info("لا توجد وظائف تدريب نشطة حالياً.")
+
+ # عرض الوظائف المكتملة
+ if completed_jobs:
+ st.markdown("#### الوظائف المكتملة")
+
+ for i, job in enumerate(completed_jobs):
+ with st.expander(f"{job.get('job_id')} - {job.get('base_model')}", expanded=False):
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ st.markdown(f"**معرف الوظيفة:** {job.get('job_id')}")
+ st.markdown(f"**النموذج الأساسي:** {job.get('base_model')}")
+ st.markdown(f"**تاريخ البدء:** {job.get('started_at')}")
+ st.markdown(f"**تاريخ الانتهاء:** {job.get('finished_at')}")
+ st.markdown(f"**النموذج المدرب:** {job.get('fine_tuned_model')}")
+
+ with col2:
+ # زر استخدام النموذج المدرب
+ if styled_button("استخدام النموذج", key=f"use_trained_model_{i}", type="primary", icon="✅"):
+ st.session_state.selected_model = job.get('fine_tuned_model')
+ st.success(f"تم اختيار النموذج {job.get('fine_tuned_model')} للاستخدام.")
+
+ # زر حذف الوظيفة
+ if styled_button("حذف الوظيفة", key=f"delete_completed_job_{i}", type="danger", icon="🗑️"):
+ st.session_state.completed_job_to_delete = len(active_jobs) + i
+
+ # عرض الوظائف الفاشلة
+ if failed_jobs:
+ st.markdown("#### الوظائف الفاشلة")
+
+ for i, job in enumerate(failed_jobs):
+ with st.expander(f"{job.get('job_id')} - {job.get('base_model')}", expanded=False):
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ st.markdown(f"**معرف الوظيفة:** {job.get('job_id')}")
+ st.markdown(f"**النموذج الأساسي:** {job.get('base_model')}")
+ st.markdown(f"**الحالة:** {job.get('status')}")
+ st.markdown(f"**تاريخ البدء:** {job.get('started_at')}")
+
+ # عرض سبب الفشل إن وجد
+ if "error" in job:
+ st.error(f"سبب الفشل: {job.get('error')}")
+
+ with col2:
+ # زر حذف الوظيفة
+ if styled_button("حذف الوظيفة", key=f"delete_failed_job_{i}", type="danger", icon="🗑️"):
+ st.session_state.failed_job_to_delete = len(active_jobs) + len(completed_jobs) + i
+
+ # معالجة إلغاء الوظيفة
+ if "job_to_cancel" in st.session_state:
+ if st.warning(f"هل أنت م��أكد من إلغاء وظيفة التدريب '{active_jobs[st.session_state.job_to_cancel].get('job_id')}'؟"):
+ if styled_button("تأكيد الإلغاء", key="confirm_cancel_job", type="danger", icon="🗑️"):
+ # إلغاء الوظيفة
+ self._cancel_training_job(active_jobs[st.session_state.job_to_cancel])
+
+ # إعادة ضبط حالة الإلغاء
+ del st.session_state.job_to_cancel
+
+ st.success("تم إلغاء وظيفة التدريب بنجاح!")
+ st.rerun()
+
+ if styled_button("إلغاء", key="cancel_job_cancellation", type="secondary", icon="❌"):
+ del st.session_state.job_to_cancel
+ st.rerun()
+
+ # معالجة حذف الوظائف المكتملة
+ if "completed_job_to_delete" in st.session_state:
+ idx = st.session_state.completed_job_to_delete
+ if 0 <= idx < len(jobs):
+ if st.warning(f"هل أنت متأكد من حذف وظيفة التدريب '{jobs[idx].get('job_id')}'؟"):
+ if styled_button("تأكيد الحذف", key="confirm_delete_completed_job", type="danger", icon="🗑️"):
+ # حذف الوظيفة
+ del st.session_state.terminology_data["training_jobs"][idx]
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة الحذف
+ del st.session_state.completed_job_to_delete
+
+ st.success("تم حذف وظيفة التدريب بنجاح!")
+ st.rerun()
+
+ if styled_button("إلغاء", key="cancel_completed_job_deletion", type="secondary", icon="❌"):
+ del st.session_state.completed_job_to_delete
+ st.rerun()
+
+ # معالجة حذف الوظائف الفاشلة
+ if "failed_job_to_delete" in st.session_state:
+ idx = st.session_state.failed_job_to_delete
+ if 0 <= idx < len(jobs):
+ if st.warning(f"هل أنت متأكد من حذف وظيفة التدريب '{jobs[idx].get('job_id')}'؟"):
+ if styled_button("تأكيد الحذف", key="confirm_delete_failed_job", type="danger", icon="🗑️"):
+ # حذف الوظيفة
+ del st.session_state.terminology_data["training_jobs"][idx]
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ # إعادة ضبط حالة الحذف
+ del st.session_state.failed_job_to_delete
+
+ st.success("تم حذف وظيفة التدريب بنجاح!")
+ st.rerun()
+
+ if styled_button("إلغاء", key="cancel_failed_job_deletion", type="secondary", icon="❌"):
+ del st.session_state.failed_job_to_delete
+ st.rerun()
+
+ def _refresh_training_jobs_status(self):
+ """تحديث حالة وظائف التدريب"""
+ jobs = st.session_state.terminology_data.get("training_jobs", [])
+
+ # فلترة الوظائف النشطة
+ active_jobs = [job for job in jobs if job.get("status") in ["running", "validating_files", "queued"]]
+
+ if not active_jobs:
+ st.info("لا توجد وظائف تدريب نشطة للتحديث.")
+ return
+
+ try:
+ # تحديث حالة كل وظيفة نشطة
+ for job in active_jobs:
+ if job.get("provider") == "OpenAI" and self.api_key:
+ # تحديث حالة وظيفة OpenAI
+ job_id = job.get("job_id")
+
+ # استعلام عن حالة الوظيفة
+ openai.api_key = self.api_key
+ response = openai.fine_tuning.jobs.retrieve(job_id)
+
+ # تحديث حالة الوظيفة
+ job["status"] = response.status
+
+ # تحديث التقدم إن وجد
+ if hasattr(response, "progress") and response.progress is not None:
+ job["progress"] = response.progress
+
+ # إذا اكتملت الوظيفة، تحديث معلومات النموذج المدرب
+ if response.status == "succeeded":
+ job["finished_at"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ job["fine_tuned_model"] = response.fine_tuned_model
+
+ # إضافة النموذج المدرب إلى قائمة النماذج
+ self._add_trained_model(job, response)
+
+ # إذا فشلت الوظيفة، تسجيل سبب الفشل
+ elif response.status == "failed" and hasattr(response, "error"):
+ job["error"] = response.error
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ st.success("تم تحديث حالة وظائف التدريب بنجاح!")
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء تحديث حالة وظائف التدريب: {str(e)}")
+
+ def _cancel_training_job(self, job):
+ """إلغاء وظيفة تدريب"""
+ try:
+ if job.get("provider") == "OpenAI" and self.api_key:
+ # إلغاء وظيفة OpenAI
+ job_id = job.get("job_id")
+
+ # استدعاء واجهة برمجة OpenAI
+ openai.api_key = self.api_key
+ openai.fine_tuning.jobs.cancel(job_id)
+
+ # تحديث حالة الوظيفة
+ job["status"] = "cancelled"
+ job["finished_at"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+ # حفظ البيانات
+ self._save_terminology_data()
+
+ return True
+
+ return False
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء إلغاء وظيفة التدريب: {str(e)}")
+ return False
+
+ def _add_trained_model(self, job, response):
+ """إضافة النموذج المدرب إلى قائمة النماذج"""
+ # إنشاء كائن النموذج
+ model = {
+ "model_id": response.fine_tuned_model,
+ "base_model": job.get("base_model"),
+ "provider": job.get("provider"),
+ "training_job_id": job.get("job_id"),
+ "description": f"النموذج المدرب على المصطلحات المتخصصة في {job.get('suffix')}",
+ "examples_count": job.get("examples_count"),
+ "created_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ "metrics": {}
+ }
+
+ # إضافة مؤشرات الأداء إن وجدت
+ if hasattr(response, "result_files") and response.result_files:
+ # تنزيل ملف النتائج وقراءة مؤشرات الأداء
+ pass
+
+ # إضافة عينة من أمثلة التدريب
+ examples = st.session_state.terminology_data.get("training_examples", [])
+ if examples:
+ # أخذ 5 أمثلة كعينة
+ sample_examples = random.sample(examples, min(5, len(examples)))
+ model["examples_preview"] = sample_examples
+
+ # إضافة النموذج إلى قائمة النماذج
+ if "models" not in st.session_state.terminology_data:
+ st.session_state.terminology_data["models"] = []
+
+ st.session_state.terminology_data["models"].append(model)
+
+ def _render_model_testing(self):
+ """عرض واجهة اختبار النموذج"""
+ st.markdown("""
+
+
🧪 اختبار النموذج
+
اختبر نموذج الذكاء الاصطناعي المدرب على المصطلحات المتخصصة.
+
يمكنك تجريب أسئلة مختلفة ومقارنة النتائج مع النماذج الأخرى.
+
+ """, unsafe_allow_html=True)
+
+ # التحقق من وجود مفاتيح API
+ if not self.api_key and not self.anthropic_api_key:
+ st.warning("لم يتم العثور على مفاتيح API للذكاء الاصطناعي. يرجى إضافة OPENAI_API_KEY أو ANTHROPIC_API_KEY إلى المتغيرات البيئية.")
+ return
+
+ # الحصول على قائمة النماذج المتاحة
+ available_models = []
+
+ # OpenAI
+ if self.api_key:
+ available_models.extend(["gpt-4o", "gpt-3.5-turbo"])
+
+ # إضافة النماذج المدربة إن وجدت
+ for model in st.session_state.terminology_data.get("models", []):
+ if model.get("provider") == "OpenAI":
+ available_models.append(model.get("model_id"))
+
+ # Anthropic
+ if self.anthropic_api_key:
+ available_models.extend(["claude-3-7-sonnet-20250219"])
+
+ # اختيار النموذج
+ selected_model = st.selectbox(
+ "اختر النموذج",
+ options=available_models,
+ key="test_model"
+ )
+
+ # إدخال النص للاختبار
+ test_input = st.text_area(
+ "أدخل نص الاختبار",
+ value="ما هو مفهوم المحتوى المحلي في المشاريع الحكومية وكيف يتم حسابه؟",
+ height=150,
+ key="test_input"
+ )
+
+ # خيارات متقدمة
+ with st.expander("خيارات متقدمة"):
+ temperature = st.slider("درجة الإبداعية (Temperature)", 0.0, 1.0, 0.7, 0.1, key="test_temperature")
+ max_tokens = st.slider("الحد الأقصى للرموز (Max Tokens)", 100, 2000, 500, 100, key="test_max_tokens")
+
+ # تحميل المصطلحات والتعريفات
+ terms_with_definitions = {}
+ for term in st.session_state.terminology_data.get("terms", []):
+ terms_with_definitions[term.get("term")] = term.get("definition")
+
+ # زر إجراء الاختبار
+ if styled_button("إجراء الاختبار", key="run_test", type="primary", icon="🧪"):
+ if not test_input:
+ st.error("يرجى إدخال نص للاختبار.")
+ else:
+ # عرض شريط التقدم
+ with st.spinner("جاري معالجة النص..."):
+ # إجراء الاختبار
+ response = self._test_model(
+ model=selected_model,
+ input_text=test_input,
+ temperature=temperature,
+ max_tokens=max_tokens,
+ terms_with_definitions=terms_with_definitions
+ )
+
+ # عرض النتيجة
+ st.markdown("### نتيجة الاختبار")
+ st.markdown(response)
+
+ # تحليل الاستجابة لاكتشاف المصطلحات المستخدمة
+ used_terms = []
+ for term in terms_with_definitions:
+ if term in response:
+ used_terms.append(term)
+
+ if used_terms:
+ st.markdown("### المصطلحات المكتشفة في الاستجابة")
+ for term in used_terms:
+ st.markdown(f"- **{term}**: {terms_with_definitions[term]}")
+
+ def _test_model(self, model, input_text, temperature, max_tokens, terms_with_definitions):
+ """اختبار النموذج"""
+ try:
+ # تجهيز المحتوى النظامي
+ system_prompt = "أنت مساعد متخصص في عقود المقاولات والمناقصات باللغة العربية. قم بالإجابة بدقة على الأسئلة والطلبات مع مراعاة المصطلحات الفنية المتخصصة."
+
+ # إضافة المصطلحات إلى المحتوى النظامي
+ if terms_with_definitions:
+ system_prompt += "\n\nفيما يلي قائمة بالمصطلحات المتخصصة وتعريفاتها:\n\n"
+ for term, definition in terms_with_definitions.items():
+ system_prompt += f"- {term}: {definition}\n"
+
+ # OpenAI
+ if "gpt" in model or any(model_data.get("model_id") == model for model_data in st.session_state.terminology_data.get("models", [])):
+ # استخدام OpenAI API
+ openai.api_key = self.api_key
+
+ response = openai.chat.completions.create(
+ model=model,
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": input_text}
+ ],
+ temperature=temperature,
+ max_tokens=max_tokens
+ )
+
+ return response.choices[0].message.content
+
+ # Anthropic
+ elif "claude" in model and self.anthropic_api_key:
+ # استخدام Anthropic API
+ from anthropic import Anthropic
+
+ anthropic_client = Anthropic(api_key=self.anthropic_api_key)
+
+ response = anthropic_client.messages.create(
+ model=model,
+ max_tokens=max_tokens,
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": input_text}
+ ],
+ temperature=temperature
+ )
+
+ return response.content[0].text
+
+ else:
+ return "النموذج المختار غير مدعوم حالياً."
+
+ except Exception as e:
+ return f"حدث خطأ أثناء اختبار النموذج: {str(e)}"
+
+ def _render_specialized_assistant(self):
+ """عرض واجهة المساعد المتخصص"""
+ st.markdown("""
+
+
🤖 المساعد المتخصص
+
استخدم المساعد الذكي المتخصص في المصطلحات التعاقدية الهندسية.
+
يمكنك طرح أسئلة حول المصطلحات وتفسيراتها واستخداماتها.
+
+ """, unsafe_allow_html=True)
+
+ # التحقق من وجود مفاتيح API
+ if not self.api_key and not self.anthropic_api_key:
+ st.warning("لم يتم العثور على مفاتيح API للذكاء الاصطناعي. يرجى إضافة OPENAI_API_KEY أو ANTHROPIC_API_KEY إلى المتغيرات البيئية.")
+ return
+
+ # الحصول على قائمة النماذج المتاحة
+ available_models = []
+
+ # OpenAI
+ if self.api_key:
+ available_models.extend(["gpt-4o", "gpt-3.5-turbo"])
+
+ # إضافة النماذج المدربة إن وجدت
+ for model in st.session_state.terminology_data.get("models", []):
+ if model.get("provider") == "OpenAI":
+ available_models.append(model.get("model_id"))
+
+ # Anthropic
+ if self.anthropic_api_key:
+ available_models.extend(["claude-3-7-sonnet-20250219"])
+
+ # تهيئة حالة المحادثة
+ if "chat_history" not in st.session_state:
+ st.session_state.chat_history = []
+
+ if "assistant_model" not in st.session_state:
+ st.session_state.assistant_model = available_models[0] if available_models else ""
+
+ # اختيار النموذج
+ selected_model = st.selectbox(
+ "اختر نموذج المساعد",
+ options=available_models,
+ index=available_models.index(st.session_state.assistant_model) if st.session_state.assistant_model in available_models else 0,
+ key="assistant_model_selector"
+ )
+
+ # تحديث النموذج المختار
+ if selected_model != st.session_state.assistant_model:
+ st.session_state.assistant_model = selected_model
+ st.rerun()
+
+ # عرض المحادثة
+ st.markdown("### المحادثة")
+
+ for message in st.session_state.chat_history:
+ if message["role"] == "user":
+ st.markdown(f"""
+
+ أنت: {message["content"]}
+
+ """, unsafe_allow_html=True)
+ else:
+ st.markdown(f"""
+
+ المساعد: {message["content"]}
+
+ """, unsafe_allow_html=True)
+
+ # إدخال رسالة جديدة
+ user_input = st.text_area("اكتب رسالتك هنا", key="assistant_input", height=100)
+
+ # أزرار التحكم
+ col1, col2, col3 = st.columns([1, 1, 1])
+
+ with col1:
+ if styled_button("إرسال", key="send_message", type="primary", icon="✉️"):
+ if not user_input:
+ st.error("يرجى كتابة رسالة للإرسال.")
+ else:
+ # إضافة رسالة المستخدم إلى المحادثة
+ st.session_state.chat_history.append({
+ "role": "user",
+ "content": user_input
+ })
+
+ # الحصول على الرد من النموذج
+ with st.spinner("المساعد يفكر..."):
+ # تحميل المصطلحات والتعريفات
+ terms_with_definitions = {}
+ for term in st.session_state.terminology_data.get("terms", []):
+ terms_with_definitions[term.get("term")] = term.get("definition")
+
+ # الحصول على رد المساعد
+ response = self._get_assistant_response(
+ model=st.session_state.assistant_model,
+ chat_history=st.session_state.chat_history,
+ terms_with_definitions=terms_with_definitions
+ )
+
+ # إضافة رد المساعد إلى المحادثة
+ st.session_state.chat_history.append({
+ "role": "assistant",
+ "content": response
+ })
+
+ # إعادة تشغيل لتحديث واجهة المستخدم
+ st.rerun()
+
+ with col2:
+ if styled_button("مسح المحادثة", key="clear_chat", type="danger", icon="🗑️"):
+ st.session_state.chat_history = []
+ st.rerun()
+
+ with col3:
+ if styled_button("اقتراح أسئلة", key="suggest_questions", type="secondary", icon="💡"):
+ # عرض أسئلة مقترحة
+ st.markdown("### أسئلة مقترحة")
+
+ # الحصول على المصطلحات المتاحة
+ terms = [term.get("term") for term in st.session_state.terminology_data.get("terms", [])]
+
+ # إنشاء أسئلة مقترحة
+ suggested_questions = [
+ f"ما هو تعريف مصطلح {term}؟" for term in random.sample(terms, min(3, len(terms)))
+ ]
+
+ suggested_questions.extend([
+ "كيف يتم حساب المحتوى المحلي في مشاريع البنية التحتية؟",
+ "ما هي أهم البنود التي يجب الانتباه لها في العقود الهندسية؟",
+ "ما الفرق بين الضمان الابتدائي والضمان النهائي؟",
+ "كيف يمكن تقييم المخاطر في مشاريع البناء؟"
+ ])
+
+ # عرض الأسئلة المقترحة
+ for i, question in enumerate(suggested_questions):
+ if styled_button(question, key=f"suggested_q_{i}", type="info", icon="❓"):
+ # إضافة السؤال المقترح إلى المحادثة
+ st.session_state.chat_history.append({
+ "role": "user",
+ "content": question
+ })
+
+ # الحصول على الرد من النموذج
+ with st.spinner("المساعد يفكر..."):
+ # تحميل المصطلحات والتعريفات
+ terms_with_definitions = {}
+ for term in st.session_state.terminology_data.get("terms", []):
+ terms_with_definitions[term.get("term")] = term.get("definition")
+
+ # الحصول على رد المساعد
+ response = self._get_assistant_response(
+ model=st.session_state.assistant_model,
+ chat_history=st.session_state.chat_history,
+ terms_with_definitions=terms_with_definitions
+ )
+
+ # إضافة رد المساعد إلى المحادثة
+ st.session_state.chat_history.append({
+ "role": "assistant",
+ "content": response
+ })
+
+ # إعادة تشغيل لتحديث واجهة المستخدم
+ st.rerun()
+
+ def _get_assistant_response(self, model, chat_history, terms_with_definitions):
+ """الحصول على رد المساعد المتخصص"""
+ try:
+ # تجهيز المحتوى النظامي
+ system_prompt = """أنت مساعد متخصص في عقود المقاولات والمناقصات باللغة العربية.
+ مهمتك هي تقديم معلومات دقيقة ومفصلة حول المصطلحات التعاقدية والهندسية المتخصصة.
+ قدم شرحاً واضحاً ومفهوماً للمصطلحات، مع أمثلة عملية عند الإمكان.
+ استخدم لغة مهنية ودقيقة، مع مراعاة السياق الهندسي والقانوني للمصطلحات."""
+
+ # إضافة المصطلحات إلى المحتوى النظامي
+ if terms_with_definitions:
+ system_prompt += "\n\nفيما يلي قائمة بالمصطلحات المتخصصة وتعريفاتها التي يجب عليك استخدامها في إجاباتك:\n\n"
+ for term, definition in terms_with_definitions.items():
+ system_prompt += f"- {term}: {definition}\n"
+
+ # تحويل محادثة streamlit إلى صيغة مناسبة للـ API
+ messages = [{"role": "system", "content": system_prompt}]
+
+ for msg in chat_history:
+ messages.append({"role": msg["role"], "content": msg["content"]})
+
+ # OpenAI
+ if "gpt" in model or any(model_data.get("model_id") == model for model_data in st.session_state.terminology_data.get("models", [])):
+ # استخدام OpenAI API
+ openai.api_key = self.api_key
+
+ response = openai.chat.completions.create(
+ model=model,
+ messages=messages,
+ temperature=0.7
+ )
+
+ return response.choices[0].message.content
+
+ # Anthropic
+ elif "claude" in model and self.anthropic_api_key:
+ # استخدام Anthropic API
+ from anthropic import Anthropic
+
+ # تعديل الرسائل لتتناسب مع صيغة Anthropic
+ anthropic_messages = []
+ for msg in messages[1:]: # تخطي رسالة النظام
+ anthropic_messages.append({"role": msg["role"], "content": msg["content"]})
+
+ anthropic_client = Anthropic(api_key=self.anthropic_api_key)
+
+ response = anthropic_client.messages.create(
+ model=model,
+ max_tokens=2000,
+ system=system_prompt,
+ messages=anthropic_messages,
+ temperature=0.7
+ )
+
+ return response.content[0].text
+
+ else:
+ return "النموذج المختار غير مدعوم حالياً."
+
+ except Exception as e:
+ return f"عذراً، حدث خطأ أثناء معالجة طلبك: {str(e)}"
+
+ def _save_terminology_data(self):
+ """حفظ بيانات المصطلحات"""
+ try:
+ # التأكد من وجود المجلد
+ os.makedirs(os.path.dirname(self.terminology_file), exist_ok=True)
+
+ # حفظ البيانات
+ with open(self.terminology_file, 'w', encoding='utf-8') as f:
+ json.dump(st.session_state.terminology_data, f, ensure_ascii=False, indent=2)
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء حفظ البيانات: {str(e)}")
+
+
+# تشغيل النموذج بشكل مستقل
+def main():
+ """تشغيل وحدة تخصيص وضبط نماذج الذكاء الاصطناعي بشكل مستقل"""
+ # تهيئة الواجهة
+ st.set_page_config(
+ page_title="تخصيص وضبط نماذج الذكاء الاصطناعي | WAHBi AI",
+ page_icon="🧠",
+ layout="wide",
+ initial_sidebar_state="expanded",
+ menu_items={
+ 'Get Help': 'mailto:support@wahbi-ai.com',
+ 'Report a bug': 'mailto:support@wahbi-ai.com',
+ 'About': 'وحدة تخصيص وضبط نماذج الذكاء الاصطناعي للمصطلحات التعاقدية المتخصصة - جزء من نظام WAHBi AI لتحليل المناقصات'
+ }
+ )
+
+ # تهيئة وحدة الضبط
+ model_finetuning = ModelFinetuning()
+
+ # عرض واجهة الوحدة
+ model_finetuning.render()
+
+# تشغيل النموذج عند استدعاء الملف مباشرة
+if __name__ == "__main__":
+ main()
\ No newline at end of file