File size: 5,325 Bytes
40d06ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# 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_<user_id>.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()))