|
|
|
|
|
|
|
""" |
|
وحدة تخصيص وضبط نماذج الذكاء الاصطناعي للمصطلحات التعاقدية المتخصصة |
|
تتيح هذه الوحدة إمكانية تدريب نماذج الذكاء الاصطناعي على المصطلحات المتخصصة في مجال العقود والمناقصات |
|
""" |
|
|
|
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 = [] |
|
|
|
|
|
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(""" |
|
<div class='custom-box info-box'> |
|
<h3>📚 قاموس المصطلحات المتخصصة</h3> |
|
<p>أضف وحرر المصطلحات الفنية المتخصصة في مجال العقود والمناقصات باللغة العربية.</p> |
|
<p>هذه المصطلحات ستستخدم لتدريب وضبط نماذج الذكاء الاصطناعي للتعرف عليها بدقة عالية.</p> |
|
</div> |
|
""", 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 |
|
|
|
|
|
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(""" |
|
<div class='custom-box info-box'> |
|
<h3>🔬 إعداد بيانات التدريب</h3> |
|
<p>قم بإنشاء وتحرير أمثلة التدريب لضبط نماذج الذكاء الاصطناعي على المصطلحات المتخصصة.</p> |
|
<p>يمكنك إنشاء أمثلة يدوياً أو استيرادها من ملف أو توليدها تلقائياً باستخدام نماذج الذكاء الاصطناعي الحالية.</p> |
|
</div> |
|
""", 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("### توليد أمثلة تدريب تلقائياً") |
|
|
|
|
|
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: |
|
|
|
if "gpt" in model and self.api_key: |
|
|
|
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_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 |
|
|
|
|
|
examples_data = json.loads(result_json) |
|
|
|
|
|
if isinstance(examples_data, dict) and "examples" in examples_data: |
|
examples_data = examples_data["examples"] |
|
|
|
elif "claude" in model and self.anthropic_api_key: |
|
|
|
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_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 |
|
|
|
|
|
examples_data = json.loads(result_json) |
|
|
|
|
|
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": |
|
|
|
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_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": |
|
|
|
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": |
|
|
|
content = uploaded_file.read().decode('utf-8') |
|
examples = json.loads(content) |
|
|
|
elif format == "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": |
|
|
|
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(""" |
|
<div class='custom-box info-box'> |
|
<h3>🧠 تدريب النموذج</h3> |
|
<p>قم بتدريب نموذج الذكاء الاصطناعي على المصطلحات المتخصصة باستخدام أمثلة التدريب.</p> |
|
<p>يمكنك اختيار النموذج الأساسي والإعدادات المناسبة لعملية التدريب.</p> |
|
</div> |
|
""", 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("### إعداد عملية التدريب") |
|
|
|
|
|
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": |
|
|
|
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="✅"): |
|
|
|
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.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") |
|
|
|
|
|
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: |
|
|
|
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: |
|
|
|
job_id = job.get("job_id") |
|
|
|
|
|
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: |
|
|
|
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(""" |
|
<div class='custom-box info-box'> |
|
<h3>🧪 اختبار النموذج</h3> |
|
<p>اختبر نموذج الذكاء الاصطناعي المدرب على المصطلحات المتخصصة.</p> |
|
<p>يمكنك تجريب أسئلة مختلفة ومقارنة النتائج مع النماذج الأخرى.</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if not self.api_key and not self.anthropic_api_key: |
|
st.warning("لم يتم العثور على مفاتيح API للذكاء الاصطناعي. يرجى إضافة OPENAI_API_KEY أو ANTHROPIC_API_KEY إلى المتغيرات البيئية.") |
|
return |
|
|
|
|
|
available_models = [] |
|
|
|
|
|
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")) |
|
|
|
|
|
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" |
|
|
|
|
|
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_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 |
|
|
|
|
|
elif "claude" in model and self.anthropic_api_key: |
|
|
|
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(""" |
|
<div class='custom-box info-box'> |
|
<h3>🤖 المساعد المتخصص</h3> |
|
<p>استخدم المساعد الذكي المتخصص في المصطلحات التعاقدية الهندسية.</p> |
|
<p>يمكنك طرح أسئلة حول المصطلحات وتفسيراتها واستخداماتها.</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if not self.api_key and not self.anthropic_api_key: |
|
st.warning("لم يتم العثور على مفاتيح API للذكاء الاصطناعي. يرجى إضافة OPENAI_API_KEY أو ANTHROPIC_API_KEY إلى المتغيرات البيئية.") |
|
return |
|
|
|
|
|
available_models = [] |
|
|
|
|
|
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")) |
|
|
|
|
|
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""" |
|
<div style='background-color: #e6f7ff; border-radius: 10px; padding: 10px; margin-bottom: 10px;'> |
|
<strong>أنت:</strong> {message["content"]} |
|
</div> |
|
""", unsafe_allow_html=True) |
|
else: |
|
st.markdown(f""" |
|
<div style='background-color: #f0f0f0; border-radius: 10px; padding: 10px; margin-bottom: 10px;'> |
|
<strong>المساعد:</strong> {message["content"]} |
|
</div> |
|
""", 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" |
|
|
|
|
|
messages = [{"role": "system", "content": system_prompt}] |
|
|
|
for msg in chat_history: |
|
messages.append({"role": msg["role"], "content": msg["content"]}) |
|
|
|
|
|
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_key = self.api_key |
|
|
|
response = openai.chat.completions.create( |
|
model=model, |
|
messages=messages, |
|
temperature=0.7 |
|
) |
|
|
|
return response.choices[0].message.content |
|
|
|
|
|
elif "claude" in model and self.anthropic_api_key: |
|
|
|
from anthropic import 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:[email protected]', |
|
'Report a bug': 'mailto:[email protected]', |
|
'About': 'وحدة تخصيص وضبط نماذج الذكاء الاصطناعي للمصطلحات التعاقدية المتخصصة - جزء من نظام WAHBi AI لتحليل المناقصات' |
|
} |
|
) |
|
|
|
|
|
model_finetuning = ModelFinetuning() |
|
|
|
|
|
model_finetuning.render() |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |