Riy777 commited on
Commit
4ac113d
·
verified ·
1 Parent(s): b31cbc9

Update learning_hub/memory_store.py

Browse files
Files changed (1) hide show
  1. learning_hub/memory_store.py +49 -138
learning_hub/memory_store.py CHANGED
@@ -1,157 +1,68 @@
1
  # learning_hub/memory_store.py
 
 
2
  import json
3
  import asyncio
4
- from datetime import datetime
5
- # 🔴 --- START OF CHANGE --- 🔴
6
- from typing import List, Dict, Optional, Any # (Import Any here)
7
- # 🔴 --- END OF CHANGE --- 🔴
8
- from .schemas import Delta, ReflectorOutput
9
- from .policy_engine import PolicyEngine
10
-
11
- # للتوافق مع R2Service (نفترض أنه سيتم تمريره)
12
- # (لا يمكننا استيراده مباشرة لتجنب التبعيات الدائرية)
13
 
14
  class MemoryStore:
15
- # (The __init__ signature now correctly uses the imported Any)
16
- def __init__(self, r2_service: Any, policy_engine: PolicyEngine, llm_service: Any):
17
- self.r2_service = r2_service
18
- self.policy_engine = policy_engine
19
- self.llm_service = llm_service # نحتاجه لعملية "التقطير" (Distillation)
20
-
21
- self.domain_files = {
22
- "strategy": "learning_deltas_strategy.json",
23
- "pattern": "learning_deltas_pattern.json",
24
- "indicator": "learning_deltas_indicator.json",
25
- "monte_carlo": "learning_deltas_monte_carlo.json",
26
- "general": "learning_deltas_general.json"
27
- }
28
-
29
- self.distill_threshold = 50 # (من النقطة 6)
30
- print("✅ Learning Hub Module: Memory Store loaded (FIXED: Imported Any)")
31
 
32
- async def _load_deltas_from_r2(self, domain: str) -> List[Dict]:
33
- """تحميل ملف الدلتا المحدد من R2"""
34
- key = self.domain_files.get(domain, self.domain_files["general"])
35
  try:
36
- response = self.r2_service.s3_client.get_object(Bucket="trading", Key=key)
37
- deltas_list = json.loads(response['Body'].read())
38
- return deltas_list
 
 
 
 
39
  except Exception as e:
40
- # إذا فشل التحميل (مثل الملف غير موجود)، أعد قائمة فارغة
41
- print(f"ℹ️ [MemoryStore] لم يتم العثور على ملف دلتا لـ {domain}، سيتم إنشاء واحد جديد.")
42
- return []
43
 
44
- async def _save_deltas_to_r2(self, domain: str, deltas_list: List[Dict]):
45
- """حفظ ملف الدلتا المحدث إلى R2"""
46
- key = self.domain_files.get(domain, self.domain_files["general"])
47
  try:
48
- # (Ensure list contains dicts before saving)
49
- deltas_to_save = [d.model_dump() if isinstance(d, Delta) else d for d in deltas_list]
50
- data_json = json.dumps(deltas_to_save, indent=2, ensure_ascii=False).encode('utf-8')
51
- self.r2_service.s3_client.put_object(
52
- Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
53
- )
54
- except Exception as e:
55
- print(f"❌ [MemoryStore] فشل حفظ الدلتا إلى R2: {e}")
56
 
57
- async def save_new_delta(self,
58
- reflector_output: ReflectorOutput,
59
- trade_object: Dict[str, Any],
60
- domain: str = "strategy"):
61
- """
62
- حفظ "دلتا" جديدة بناءً على مخرجات المنعكس وسياسة القبول.
63
- """
64
  try:
65
- trade_pnl_percent = trade_object.get('pnl_percent', 0)
 
66
 
67
- # 1. التحقق من سياسة القبول
68
- is_approved, approval_reason = self.policy_engine.get_delta_acceptance(
69
- reflector_output,
70
- trade_pnl_percent
71
- )
72
 
73
- # 2. إنشاء كائن الدلتا
74
- new_delta = Delta(
75
- text=reflector_output.suggested_rule,
76
- domain=domain,
77
- score=reflector_output.confidence,
78
- evidence_refs=[trade_object.get('id', 'unknown_trade_id')],
79
- approved=is_approved,
80
- trade_strategy=trade_object.get('strategy', 'unknown'),
81
- exit_profile=trade_object.get('decision_data', {}).get('exit_profile', 'unknown')
82
- )
83
-
84
- # 3. تحميل، إضافة، وحفظ الدلتا
85
- deltas_list = await self._load_deltas_from_r2(domain)
86
- deltas_list.append(new_delta.model_dump()) # Use model_dump() for pydantic models
87
- await self._save_deltas_to_r2(domain, deltas_list)
88
-
89
- print(f"✅ [MemoryStore] تم حفظ دلتا جديدة لـ {domain}. الحالة: {approval_reason}")
90
-
91
- # 4. تفعيل عملية "التقطير" (لا يتم تنفيذها هنا مباشرة)
92
- if len([d for d in deltas_list if d.get('approved')]) % self.distill_threshold == 0 and is_approved:
93
- print(f"ℹ️ [MemoryStore] تم الوصول إلى حد {self.distill_threshold} دلتا لـ {domain}. التقطير سيتم جدولته.")
94
- # (Curator will handle the actual check and distillation)
95
 
 
 
 
 
96
  except Exception as e:
97
- print(f"❌ [MemoryStore] فشل فادح في حفظ الدلتا: {e}")
98
- traceback.print_exc()
99
-
100
 
101
- async def get_active_context(self, domain: str, query: str, top_k: int = 3) -> str:
102
- """
103
- جلب "السياق النشط" (Active Context) لإرساله إلى النموذج.
104
- """
105
  try:
106
- all_deltas_dicts = await self._load_deltas_from_r2(domain)
107
-
108
- # 1. تصفية الدلتا المعتمدة فقط
109
- approved_deltas = [Delta(**d) for d in all_deltas_dicts if d.get('approved', False)]
110
-
111
- if not approved_deltas:
112
- # (Return English text for consistency)
113
- return "Playbook: No approved learning rules (Deltas) found for this domain yet."
114
 
115
- # 2. خوارزمية الاسترجاع (نسخة مبسطة)
116
- scored_deltas = []
117
- for delta in approved_deltas:
118
- priority_map = {"high": 1.0, "medium": 0.6, "low": 0.2}
119
- priority_score = priority_map.get(delta.priority, 0.6)
120
-
121
- try:
122
- age_days = (datetime.now() - datetime.fromisoformat(delta.created_at)).days
123
- freshness_score = max(0, 1.0 - (age_days / 90.0))
124
- except Exception:
125
- freshness_score = 0.5
126
-
127
- relevance_score = 0.5
128
- query_words = set(query.lower().split())
129
- delta_words = set(delta.text.lower().split())
130
- if query_words.intersection(delta_words):
131
- relevance_score = 1.0
132
- elif delta.trade_strategy and delta.trade_strategy.lower() in query_words:
133
- relevance_score = 0.8
134
-
135
- final_score = (0.6 * relevance_score) + (0.3 * priority_score) + (0.1 * freshness_score)
136
- scored_deltas.append((final_score, delta))
137
-
138
- # 3. فرز واختيار أفضل K
139
- scored_deltas.sort(key=lambda x: x[0], reverse=True)
140
- top_deltas = [delta for score, delta in scored_deltas[:top_k]]
141
-
142
- # 4. تنسيق الموجه (باللغة الإنجليزية)
143
- if not top_deltas:
144
- return "Playbook: No relevant learning rules (Deltas) found for this query."
145
-
146
- playbook_header = f"Playbook (Top {len(top_deltas)} Rules - Domain: {domain}):"
147
- # (Added score for context)
148
- delta_lines = [f"• {delta.text} (Score: {delta.score:.2f}, Prio: {delta.priority})" for delta in top_deltas]
149
-
150
- # (Distilled rules are just high-priority deltas in this implementation)
151
-
152
- return "\n".join([playbook_header] + delta_lines)
153
-
154
- except Exception as e:
155
- print(f"❌ [MemoryStore] فشل جلب السياق النشط: {e}")
156
- # (Return English text for consistency)
157
- return "Playbook: Error retrieving learning context."
 
1
  # learning_hub/memory_store.py
2
+ # (V13.0 - Samples Storage)
3
+
4
  import json
5
  import asyncio
6
+ from typing import List, Any
7
+ from .schemas import TrainingSample
 
 
 
 
 
 
 
8
 
9
  class MemoryStore:
10
+ def __init__(self, r2_service: Any):
11
+ self.r2 = r2_service
12
+ self.pending_samples_key = "datasets/pending_training_batch.json"
13
+ self.archive_dataset_key = "datasets/historical_training_data.jsonl" # JSON Lines for big data
14
+ print("✅ Learning Hub: Memory Store loaded")
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ async def append_training_sample(self, sample: TrainingSample):
17
+ """إضافة عينة لقائمة الانتظار في R2"""
 
18
  try:
19
+ # 1. جلب القائمة الحالية (مكلف قليلاً لكن ضروري في بيئة stateless)
20
+ # تحسين: يمكن الاحتفاظ بذاكرة مؤقتة (Cache) وتحديث R2 كل 10 عينات
21
+ current_batch = await self._load_json_from_r2(self.pending_samples_key) or []
22
+ current_batch.append(sample.model_dump())
23
+
24
+ await self._save_json_to_r2(self.pending_samples_key, current_batch)
25
+
26
  except Exception as e:
27
+ print(f"❌ [MemoryStore] Failed to append sample: {e}")
 
 
28
 
29
+ async def get_pending_samples_count(self) -> int:
 
 
30
  try:
31
+ current_batch = await self._load_json_from_r2(self.pending_samples_key) or []
32
+ return len(current_batch)
33
+ except:
34
+ return 0
 
 
 
 
35
 
36
+ async def load_and_clear_pending_samples(self) -> List[TrainingSample]:
37
+ """جلب العينات للتدريب ومسحها من الانتظار وأرشفتها"""
 
 
 
 
 
38
  try:
39
+ raw_data = await self._load_json_from_r2(self.pending_samples_key)
40
+ if not raw_data: return []
41
 
42
+ samples = [TrainingSample(**d) for d in raw_data]
 
 
 
 
43
 
44
+ # 1. أرشفة البيانات (Append to History)
45
+ # لتقليل استهلاك الذاكرة، نرفع البيانات كنص JSONL مباشرة
46
+ lines = "\n".join([json.dumps(d) for d in raw_data])
47
+ # (هنا نحتاج دالة append في R2، إذا لم توجد، يمكن تجاوز الأرشفة الآن أو حفظ ملفات مؤرخة)
48
+ # للتسهيل: سنحفظ ملف أرشيف جديد لكل دفعة
49
+ archive_key = f"datasets/archive/batch_{samples[0].id}.json"
50
+ await self._save_json_to_r2(archive_key, raw_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ # 2. مسح القائمة الحالية
53
+ await self._save_json_to_r2(self.pending_samples_key, [])
54
+
55
+ return samples
56
  except Exception as e:
57
+ print(f"❌ [MemoryStore] Failed to load/clear samples: {e}")
58
+ return []
 
59
 
60
+ # --- Helpers ---
61
+ async def _load_json_from_r2(self, key):
 
 
62
  try:
63
+ resp = await self.r2.get_file_async(key) # Assuming R2Service has this
64
+ return json.loads(resp)
65
+ except: return None
 
 
 
 
 
66
 
67
+ async def _save_json_to_r2(self, key, data):
68
+ await self.r2.upload_json_async(data, key) # Assuming R2Service has this