# memory.py # -*- coding: utf-8 -*- """ ذاكرة المستخدم/العامة + قاعدة المعرفة في ملف واحد، بدون أي استيراد عكسي (circular import). يوفّر: - load_memory(user_id) / save_memory(user_id, data) - load_global_memory() / save_global_memory(data) - load_knowledge_base() / save_knowledge_base(data) - learn_from_unknown(prompt) / update_knowledge_base(prompt, answer) """ from __future__ import annotations import json import os import tempfile import threading from typing import Any, Dict # ========= مسارات التخزين ========= BASE_DIR = os.path.abspath(os.path.dirname(__file__)) # مجلد بيانات الذاكرة DATA_DIR = os.path.join(BASE_DIR, "data") os.makedirs(DATA_DIR, exist_ok=True) # ملفات الذاكرة GLOBAL_MEMORY_FILE = os.path.join(DATA_DIR, "global_memory.json") USER_MEMORY_PREFIX = "memory_" # memory_.json # ملف قاعدة المعرفة (متوافق مع نسختك السابقة) knowledge_base_path = os.path.join(BASE_DIR, "knowledge_base.json") # قفل للتزامن _lock = threading.RLock() # ========= أدوات JSON آمنة ========= def _safe_write_json(path: str, data: Dict[str, Any]) -> None: """ كتابة ذرّية لضمان عدم تلف الملف حتى مع انقطاع مفاجئ. """ with _lock: os.makedirs(os.path.dirname(path), exist_ok=True) fd, tmp_path = tempfile.mkstemp(prefix=".tmp_", dir=os.path.dirname(path)) try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) f.flush() os.fsync(f.fileno()) os.replace(tmp_path, path) finally: if os.path.exists(tmp_path): try: os.remove(tmp_path) except OSError: pass def _read_json(path: str) -> Dict[str, Any]: """ قراءة JSON بأمان. يعيد dict فارغ عند عدم وجود الملف أو في حال تلفه. """ with _lock: if not os.path.exists(path): return {} try: with open(path, "r", encoding="utf-8") as f: return json.load(f) except Exception: return {} def _user_file(user_id: str) -> str: """ تكوين اسم ملف آمن للمستخدم. """ safe_id = "".join(ch for ch in str(user_id) if ch.isalnum() or ch in ("-", "_")) or "default" return os.path.join(DATA_DIR, f"{USER_MEMORY_PREFIX}{safe_id}.json") # ========= واجهة الذاكرة (لـ learner.py) ========= def load_memory(user_id: str) -> Dict[str, Any]: """ قراءة ذاكرة مستخدم (dict). """ return _read_json(_user_file(user_id)) def save_memory(user_id: str, data: Dict[str, Any]) -> None: """ حفظ ذاكرة مستخدم (dict). """ if not isinstance(data, dict): raise TypeError("save_memory expects a dict") _safe_write_json(_user_file(user_id), data) def load_global_memory() -> Dict[str, Any]: """ قراءة الذاكرة العامة (dict). """ return _read_json(GLOBAL_MEMORY_FILE) def save_global_memory(data: Dict[str, Any]) -> None: """ حفظ الذاكرة العامة (dict). """ if not isinstance(data, dict): raise TypeError("save_global_memory expects a dict") _safe_write_json(GLOBAL_MEMORY_FILE, data) # ========= واجهة قاعدة المعرفة (متوافقة مع نسختك السابقة) ========= def load_knowledge_base() -> Dict[str, Any]: return _read_json(knowledge_base_path) def save_knowledge_base(data: Dict[str, Any]) -> None: if not isinstance(data, dict): raise TypeError("save_knowledge_base expects a dict") _safe_write_json(knowledge_base_path, data) def learn_from_unknown(prompt: str): """ نفس السلوك السابق: يطبع رسالة ويطلب إجابة ليتم تعلمها. """ print("نورا: لا أملك إجابة لهذا السؤال حاليًا. سأبحث عن إجابة وأتعلم.") answer = input("يرجى تزويدي بالإجابة المناسبة لأتعلمها: ") if answer and answer.strip(): return answer return None def update_knowledge_base(prompt: str, answer: str) -> None: kb = load_knowledge_base() kb[prompt] = answer save_knowledge_base(kb) # ========= تصدير الأسماء ========= __all__ = [ "load_memory", "save_memory", "load_global_memory", "save_global_memory", "load_knowledge_base", "save_knowledge_base", "learn_from_unknown", "update_knowledge_base", ] # ========= اختبار سريع ========= if __name__ == "__main__": uid = "osama" # اختبار ذاكرة المستخدم/العامة u = load_memory(uid) u["counter"] = u.get("counter", 0) + 1 save_memory(uid, u) g = load_global_memory() g["last_user"] = uid save_global_memory(g) print("User memory:", load_memory(uid)) print("Global memory:", load_global_memory()) # اختبار قاعدة المعرفة update_knowledge_base("ما هي نورا؟", "مساعد ذكي") print("KB size:", len(load_knowledge_base()))