183 lines
8.1 KiB
Python
183 lines
8.1 KiB
Python
# arabic_product_node.py
|
|
|
|
import hashlib
|
|
import os
|
|
import json
|
|
|
|
# --- Simple logging utility ---
|
|
def log(message):
|
|
print(f"[ArabicProductDesc] {message}")
|
|
|
|
# --- Paths and Preset Setup (Same as original) ---
|
|
script_directory = os.path.dirname(os.path.abspath(__file__))
|
|
USER_PRESETS_FILE = os.path.join(script_directory, "user_kontext_presets.json")
|
|
|
|
# --- Built-in Presets for Arabic Product Descriptions ---
|
|
ARABIC_PRODUCT_PRESETS = {
|
|
"عام - وصفي": {
|
|
"system": (
|
|
"أنت خبير تسويق إلكتروني. اكتب وصفًا احترافيًا باللغة العربية للمنتج بناءً على الوصف المرئي أو المعلومات المقدمة. "
|
|
"يجب أن يتضمن الوصف: اسم المنتج (إن عُرف)، المكونات أو المواصفات، الفوائد الرئيسية، طريقة الاستخدام، الفئة المستهدفة، وأي ملاحظات أمان. "
|
|
"استخدم لغة تسويقية جذابة، واضحة، وسلسة. لا تستخدم تنسيق Markdown أو عناوين فرعية. لا تضيف تعليقات خارجية."
|
|
)
|
|
},
|
|
"عناية بالبشرة": {
|
|
"system": (
|
|
"أنت أخصائي عناية بالبشرة. اكتب وصفًا عربيًا احترافيًا لكريم أو سيروم بناءً على الوصف المرئي أو التقني. "
|
|
"ركّز على المكونات الفعالة (مثل SPF، حمض الهيالورونيك)، الفوائد (ترطيب، حماية، تفتيح)، ومدى ملاءمته لأنواع البشرة. "
|
|
"اذكر طريقة الاستخدام (قبل النوم، بعد التنظيف، إلخ)، وكم مرة يُستخدم. استخدم لغة جذابة لكن علمية."
|
|
)
|
|
},
|
|
"طعام ومشروبات": {
|
|
"system": (
|
|
"أنت كاتب محتوى غذائي. اكتب وصفًا شهيًا وملهمًا بالعربية لمنتج غذائي أو شراب. "
|
|
"صف الطعم، القوام، المكونات الطبيعية، القيمة الغذائية، ومتى يُفضل تناوله (صباحًا، بعد التمرين). "
|
|
"استخدم لغة حسية ومشوقة. لا تبالغ. لا تستخدم تنسيق."
|
|
)
|
|
},
|
|
"إلكترونيات": {
|
|
"system": (
|
|
"أنت خبير تقني. اكتب وصفًا عربيًا واضحًا لجهاز إلكتروني (مثل سماعة، شاحن، ساعة ذكية). "
|
|
"اذكر المواصفات الأساسية (البطارية، التوصيل، الجودة)، الفوائد (راحة، دقة، كفاءة)، والجمهور المستهدف. "
|
|
"اجعله سهل الفهم للمستخدم العادي مع لمسة احترافية."
|
|
)
|
|
}
|
|
}
|
|
|
|
def load_user_presets():
|
|
"""Load user-defined presets from JSON file."""
|
|
if os.path.exists(USER_PRESETS_FILE):
|
|
try:
|
|
with open(USER_PRESETS_FILE, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
except (json.JSONDecodeError, IOError) as e:
|
|
log(f"Error loading user presets: {e}")
|
|
return {}
|
|
return {}
|
|
|
|
def get_all_product_presets():
|
|
"""Get combined built-in and user-defined presets."""
|
|
all_presets = ARABIC_PRODUCT_PRESETS.copy()
|
|
user_presets = load_user_presets()
|
|
all_presets.update(user_presets)
|
|
return all_presets
|
|
|
|
# --- Category for UI ---
|
|
PRODUCT_DESC_CATEGORY = "Local LLM Nodes/Product Description (Arabic)"
|
|
|
|
|
|
class ArabicProductDescriptionGenerator:
|
|
"""
|
|
Generates a detailed Arabic product description using:
|
|
- Optional image description
|
|
- User instruction in Arabic
|
|
- Selected preset (system prompt)
|
|
- Local LLM via LLMServiceConnector
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
all_presets = get_all_product_presets()
|
|
default_preset = next(iter(all_presets.keys()), "") if all_presets else ""
|
|
|
|
return {
|
|
"required": {
|
|
"llm_service_connector": ("LLMServiceConnector",),
|
|
"user_instruction_ar": ("STRING", {
|
|
"default": "اكتب وصفًا تفصيليًا لهذا المنتج",
|
|
"multiline": True,
|
|
"tooltip": "التعليمات من المستخدم بالعربية، مثلاً: اكتب وصفًا تسويقيًا"
|
|
}),
|
|
"preset": (list(all_presets.keys()), {
|
|
"default": default_preset,
|
|
"tooltip": "اختر نمط الوصف (عام، عناية بالبشرة، إلخ)"
|
|
}),
|
|
"seed": ("INT", {
|
|
"default": 0,
|
|
"min": 0,
|
|
"max": 0xffffffffffffffff,
|
|
"control_after_generate": True
|
|
})
|
|
},
|
|
"optional": {
|
|
"image_description": ("STRING", {
|
|
"default": "",
|
|
"multiline": True,
|
|
"tooltip": "وصف الصورة أو التفاصيل التقنية عن المنتج (اختياري)"
|
|
})
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("arabic_product_description",)
|
|
FUNCTION = "generate_description"
|
|
CATEGORY = PRODUCT_DESC_CATEGORY
|
|
|
|
def generate_description(self, llm_service_connector, user_instruction_ar, preset, seed=None, image_description=None):
|
|
# Load preset
|
|
all_presets = get_all_product_presets()
|
|
preset_data = all_presets.get(preset)
|
|
if not preset_data:
|
|
raise ValueError(f"النمط غير معروف: {preset}")
|
|
|
|
# Safe string conversion
|
|
def safe_str(s):
|
|
return str(s) if s is not None else ""
|
|
|
|
img_desc = safe_str(image_description).strip()
|
|
user_inst = safe_str(user_instruction_ar).strip()
|
|
|
|
# Build user content
|
|
user_content_parts = []
|
|
if img_desc:
|
|
user_content_parts.append(f"وصف المنتج أو الصورة: {img_desc}")
|
|
if user_inst:
|
|
user_content_parts.append(f"التعليمات: {user_inst}")
|
|
else:
|
|
user_content_parts.append("التعليمات: اكتب وصفًا تفصيليًا للمنتج.")
|
|
|
|
user_content = " ".join(user_content_parts).strip()
|
|
if not user_content:
|
|
user_content = "وصف المنتج: منتج على خلفية بيضاء. التعليمات: اكتب وصفًا تسويقيًا بالعربية."
|
|
|
|
# Build messages
|
|
messages = [
|
|
{"role": "system", "content": preset_data["system"]},
|
|
{"role": "user", "content": user_content}
|
|
]
|
|
|
|
# Call LLM
|
|
try:
|
|
response = llm_service_connector.invoke(messages, seed=seed)
|
|
return (response.strip(),)
|
|
except Exception as e:
|
|
error_msg = f"[خطأ] تعذر إنشاء الوصف: {str(e)}"
|
|
log(error_msg)
|
|
return (error_msg,)
|
|
|
|
def is_changed(self, llm_service_connector, user_instruction_ar, preset, seed, image_description=None):
|
|
"""Ensure node re-runs only when inputs change."""
|
|
try:
|
|
hasher = hashlib.md5()
|
|
hasher.update(str(user_instruction_ar).encode('utf-8'))
|
|
hasher.update(preset.encode('utf-8'))
|
|
hasher.update(str(seed).encode('utf-8'))
|
|
hasher.update(str(image_description).encode('utf-8'))
|
|
|
|
# Add preset system prompt
|
|
all_presets = get_all_product_presets()
|
|
preset_data = all_presets.get(preset)
|
|
if preset_data and "system" in preset_data:
|
|
hasher.update(preset_data["system"].encode('utf-8'))
|
|
|
|
# Add connector state
|
|
try:
|
|
connector_state = llm_service_connector.get_state()
|
|
except AttributeError:
|
|
connector_state = str(llm_service_connector)
|
|
hasher.update(connector_state.encode('utf-8'))
|
|
|
|
return hasher.hexdigest()
|
|
except Exception as e:
|
|
log(f"is_changed error: {e}")
|
|
return float("nan") |