File size: 18,869 Bytes
0789398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fdf457b
 
 
 
 
 
 
 
 
0789398
fdf457b
 
 
 
 
 
 
 
 
 
 
 
0789398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# ================================================================
# YUI CONTINUOUS EVOLUTION ENGINE v1.0
# File: yui_continuous_evolution.py
#
# Tính năng:
# [1] Background thread tự động phân tích lỗi & hội thoại
# [2] Tạo đề xuất cải tiến → gọi Code Brain → sandbox verify → apply
# [3] Adaptive frequency: tăng tần suất khi phát hiện nhiều lỗi
# [4] Self-monitoring: theo dõi sức khỏe hệ thống liên tục
# [5] Skill absorption: học từ mỗi cuộc hội thoại thành công
# [6] Zero-cost: chỉ dùng free APIs (Mistral/Gemini/Cerebras)
# ================================================================

import os, sys, re, json, time, random, threading, traceback, datetime
import sqlite3, hashlib, ast
from typing import Optional, List, Dict, Tuple

# ── Constants ─────────────────────────────────────────────────────
_EVL_VERSION        = "1.0.0"
_EVL_INTERVAL_MIN   = 300      # 5 phút — chu kỳ bình thường
_EVL_INTERVAL_FAST  = 60       # 1 phút — khi có nhiều lỗi
_EVL_INTERVAL_SLOW  = 900      # 15 phút — khi hệ thống ổn định
_EVL_MAX_ERRORS     = 10       # Bao nhiêu lỗi → chuyển sang FAST mode
_EVL_LOG_MAX        = 500      # Giữ tối đa N entries trong log
_EVL_DB_PATH        = None     # Set by init()

# ── Internal state ────────────────────────────────────────────────
_error_buffer: List[Dict]   = []   # Buffer lỗi gần đây
_insight_buffer: List[Dict] = []   # Insight từ hội thoại
_evolution_log: List[Dict]  = []   # Log tiến hóa
_lock = threading.Lock()
_stop_event = threading.Event()
_evolution_thread: Optional[threading.Thread] = None
_current_interval = _EVL_INTERVAL_MIN
_total_evolutions = 0
_last_evolution_time = 0.0
_health_score = 100.0          # 0-100, hệ thống càng khỏe càng cao

# ── Getters cho app.py ────────────────────────────────────────────
def get_health_score() -> float:
    return _health_score

def get_total_evolutions() -> int:
    return _total_evolutions

def get_last_evolution_time() -> float:
    return _last_evolution_time

def get_evolution_log(n: int = 10) -> List[Dict]:
    with _lock:
        return list(_evolution_log[-n:])

# ── Thu thập lỗi & insight ────────────────────────────────────────
def report_error(error: Exception, context: str = "", platform: str = ""):
    """Gọi từ app.py khi có lỗi xảy ra để engine học."""
    global _health_score
    with _lock:
        entry = {
            "ts": time.time(),
            "type": type(error).__name__,
            "msg": str(error)[:300],
            "ctx": context[:200],
            "platform": platform,
        }
        _error_buffer.append(entry)
        if len(_error_buffer) > 200:
            _error_buffer.pop(0)
        # Giảm health score theo mức độ lỗi
        severity = 5 if isinstance(error, (RuntimeError, ValueError)) else 2
        _health_score = max(0.0, _health_score - severity)

def report_insight(topic: str, content: str, quality: float = 0.7):
    """Gọi khi Yui trả lời thành công — học từ phản hồi tốt."""
    global _health_score
    with _lock:
        entry = {
            "ts": time.time(),
            "topic": topic[:100],
            "content": content[:500],
            "quality": quality,
        }
        _insight_buffer.append(entry)
        if len(_insight_buffer) > 300:
            _insight_buffer.pop(0)
        # Tăng health score khi có insight tốt
        _health_score = min(100.0, _health_score + quality * 0.5)

# ── Core Evolution Logic ──────────────────────────────────────────
def _analyze_errors() -> Optional[str]:
    """Phân tích các lỗi gần đây, tạo summary."""
    with _lock:
        recent = [e for e in _error_buffer if time.time() - e["ts"] < 3600]
    if not recent:
        return None
    by_type: Dict[str, int] = {}
    for e in recent:
        by_type[e["type"]] = by_type.get(e["type"], 0) + 1
    top = sorted(by_type.items(), key=lambda x: x[1], reverse=True)[:3]
    summary = "; ".join(f"{t}({c})" for t, c in top)
    samples = [f"  [{e['type']}] {e['msg']} (ctx: {e['ctx']})" for e in recent[-3:]]
    return f"Lỗi 1h qua: {summary}\nMẫu:\n" + "\n".join(samples)

def _analyze_insights() -> Optional[str]:
    """Tóm tắt patterns từ các insight thành công."""
    with _lock:
        recent = [i for i in _insight_buffer if time.time() - i["ts"] < 7200]
    if not recent:
        return None
    topics = {}
    for i in recent:
        t = i["topic"]
        topics[t] = topics.get(t, 0) + 1
    top = sorted(topics.items(), key=lambda x: x[1], reverse=True)[:5]
    return "Topics phổ biến: " + ", ".join(f"{t}({c})" for t, c in top)

def _call_evolution_brain(prompt: str) -> Optional[str]:
    """
    Gọi free AI để tạo cải tiến.
    🔄 MIGRATION (2026-07-17): TRƯỚC ĐÂY cascade tay Mistral→Gemini→
    Cerebras — Cerebras free tier đóng cửa 17/08/2026. Giờ dùng nhóm
    "fast_light" qua yui_model_router (đa-provider, đã có sẵn Mistral +
    Gemini + Cohere + SambaNova, tự xoay vòng — không cần cascade tay
    3 tầng như cũ nữa). Giữ nguyên tên hàm + chữ ký để mọi nơi gọi
    _call_evolution_brain(...) không cần sửa gì thêm.
    """
    try:
        import asyncio
        from yui_model_router import call_task_group

        async def _go():
            return await call_task_group(
                group="fast_light", prompt=prompt, system="",
                max_tokens=1200, timeout_sec=30,
            )

        text = asyncio.run(_go())
        if text and len(text) > 20:
            return text
    except Exception as e:
        print(f"[EVOLUTION] Brain call lỗi: {e}")
    return None

def _safe_code_verify(code: str) -> Tuple[bool, str]:
    """Kiểm tra code mới có an toàn không (AST scan)."""
    FORBIDDEN = {
        "calls": ["eval", "exec", "compile", "__import__", "open", "subprocess"],
        "imports": ["subprocess", "socket", "ctypes", "importlib"],
        "attrs": ["__class__", "__bases__", "__mro__", "__subclasses__"],
    }
    try:
        tree = ast.parse(code)
        for node in ast.walk(tree):
            if isinstance(node, ast.Call):
                if isinstance(node.func, ast.Name) and node.func.id in FORBIDDEN["calls"]:
                    return False, f"Forbidden call: {node.func.id}"
            if isinstance(node, (ast.Import, ast.ImportFrom)):
                names = [a.name for a in node.names] if isinstance(node, ast.Import) \
                    else ([node.module] if node.module else [])
                for n in names:
                    if n and n.split(".")[0] in FORBIDDEN["imports"]:
                        return False, f"Forbidden import: {n}"
        return True, "ok"
    except SyntaxError as e:
        return False, f"SyntaxError: {e}"
    except Exception as e:
        return False, f"Verify error: {e}"

def _save_evolution_to_db(evo_type: str, summary: str, success: bool):
    """Lưu log tiến hóa vào SQLite nếu có DB."""
    global _evolution_log
    entry = {
        "ts": time.time(),
        "dt": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "type": evo_type,
        "summary": summary[:300],
        "success": success,
        "health": round(_health_score, 1),
    }
    with _lock:
        _evolution_log.append(entry)
        if len(_evolution_log) > _EVL_LOG_MAX:
            _evolution_log.pop(0)
    # Ghi vào SQLite nếu path đã được set
    if _EVL_DB_PATH:
        try:
            conn = sqlite3.connect(_EVL_DB_PATH, timeout=5)
            conn.execute("CREATE TABLE IF NOT EXISTS evolution_log (ts REAL, dt TEXT, type TEXT, summary TEXT, success INTEGER, health REAL)")
            conn.execute("INSERT INTO evolution_log VALUES (?,?,?,?,?,?)",
                         (entry["ts"], entry["dt"], entry["type"], entry["summary"],
                          1 if success else 0, entry["health"]))
            # Giữ tối đa 500 entries
            conn.execute("DELETE FROM evolution_log WHERE rowid NOT IN (SELECT rowid FROM evolution_log ORDER BY ts DESC LIMIT 500)")
            conn.commit()
            conn.close()
        except Exception:
            pass

# ── Các loại tiến hóa ─────────────────────────────────────────────
def _evolve_error_fix() -> bool:
    """Phân tích lỗi → đề xuất fix → verify → log."""
    err_summary = _analyze_errors()
    if not err_summary:
        return False
    prompt = f"""Bạn là AI chuyên phân tích và sửa lỗi Python.
Hệ thống Yui AI đang gặp các lỗi sau (1 giờ qua):

{err_summary}

Hãy:
1. Phân tích nguyên nhân gốc rễ
2. Đề xuất 1-2 fix cụ thể (pattern-based, không cần biết toàn bộ code)
3. Nếu có thể, viết một helper function nhỏ (<30 dòng) để xử lý lỗi này

Trả lời ngắn gọn, thực tế. Không viết essay."""
    response = _call_evolution_brain(prompt)
    if not response:
        return False
    summary = f"Error fix analysis: {err_summary[:100]} → {response[:200]}"
    _save_evolution_to_db("error_fix", summary, True)
    print(f"[EVOLUTION] 🔧 Error fix: {response[:150]}")
    return True

def _evolve_skill_absorption() -> bool:
    """Học từ các insight thành công → lưu vào skill DB."""
    insight_summary = _analyze_insights()
    if not insight_summary:
        return False
    with _lock:
        recent = [i for i in _insight_buffer
                  if time.time() - i["ts"] < 7200 and i["quality"] > 0.75]
    if not recent:
        return False
    # Lưu insights chất lượng cao vào skill DB (nếu có _skill_db_save)
    absorbed = 0
    for insight in recent[-5:]:
        try:
            # Thử gọi _skill_db_save từ app.py scope nếu available
            import sys
            app_mod = sys.modules.get("__main__") or sys.modules.get("app")
            if app_mod and hasattr(app_mod, "_skill_db_save"):
                app_mod._skill_db_save(
                    problem=insight["topic"],
                    solution=insight["content"],
                    category="conversation_insight",
                    quality=insight["quality"],
                    is_core=False,
                    source="continuous_evolution",
                )
                absorbed += 1
        except Exception:
            pass
    if absorbed > 0:
        summary = f"Absorbed {absorbed} insights. {insight_summary}"
        _save_evolution_to_db("skill_absorption", summary, True)
        print(f"[EVOLUTION] 🧠 Absorbed {absorbed} skills từ hội thoại")
        return True
    return False

def _evolve_health_check() -> bool:
    """Kiểm tra sức khỏe hệ thống, phục hồi nếu cần."""
    global _health_score
    checks = []
    # Check Python memory
    try:
        import sys
        mod_count = len(sys.modules)
        checks.append(f"modules={mod_count}")
    except Exception:
        pass
    # Check error rate trong 10 phút
    with _lock:
        recent_errors = [e for e in _error_buffer if time.time() - e["ts"] < 600]
    err_rate = len(recent_errors)
    checks.append(f"errors_10m={err_rate}")
    # Điều chỉnh health
    if err_rate == 0:
        _health_score = min(100.0, _health_score + 2.0)
    elif err_rate > 5:
        _health_score = max(0.0, _health_score - 5.0)
    status = "🟢" if _health_score >= 70 else "🟡" if _health_score >= 40 else "🔴"
    summary = f"{status} Health={_health_score:.1f} | " + " | ".join(checks)
    _save_evolution_to_db("health_check", summary, True)
    return True

def _evolve_self_optimize() -> bool:
    """Dùng AI để tự phân tích và đề xuất tối ưu hóa."""
    global _health_score
    if _health_score > 80 and random.random() > 0.3:
        return False  # Hệ thống đang ổn, không cần optimize liên tục
    err_count = len(_error_buffer)
    insight_count = len(_insight_buffer)
    prompt = f"""Bạn là AI tư vấn tối ưu hóa hệ thống AI chatbot (Yui AI).
Tình trạng hiện tại:
- Health score: {_health_score:.1f}/100
- Lỗi tích lũy: {err_count}
- Insights học được: {insight_count}
- Phiên bản engine: {_EVL_VERSION}

Đây là AI chạy trên HuggingFace Spaces (free tier), giới hạn RAM/CPU.

Hãy đề xuất 2-3 cải tiến nhỏ, cụ thể, thực tế để:
1. Tăng độ ổn định
2. Giảm lỗi
3. Học nhanh hơn từ người dùng

Ngắn gọn, không cần giải thích dài."""
    response = _call_evolution_brain(prompt)
    if not response:
        return False
    summary = f"Self-optimize (health={_health_score:.0f}): {response[:250]}"
    _save_evolution_to_db("self_optimize", summary, True)
    print(f"[EVOLUTION] 💡 Optimize: {response[:120]}")
    _health_score = min(100.0, _health_score + 1.0)
    return True

# ── Adaptive Scheduling ──────────────────────────────────────────
def _calculate_interval() -> int:
    """Tính chu kỳ tiến hóa dựa trên trạng thái hệ thống."""
    with _lock:
        recent_errors = [e for e in _error_buffer if time.time() - e["ts"] < 600]
    if len(recent_errors) >= _EVL_MAX_ERRORS:
        return _EVL_INTERVAL_FAST   # Nhiều lỗi → evolve nhanh hơn
    if _health_score >= 85:
        return _EVL_INTERVAL_SLOW   # Hệ thống khỏe → nghỉ lâu hơn
    return _EVL_INTERVAL_MIN

# ── Main Evolution Loop ───────────────────────────────────────────
def _evolution_loop():
    """Thread chính: chạy vô hạn, tự tiến hóa theo chu kỳ."""
    global _total_evolutions, _last_evolution_time, _current_interval
    print(f"[EVOLUTION] 🧬 Continuous Evolution Engine v{_EVL_VERSION} khởi động")
    # Delay ban đầu để app khởi động xong
    time.sleep(30)
    while not _stop_event.is_set():
        try:
            _last_evolution_time = time.time()
            cycle_start = time.time()
            tasks = [
                ("health_check",    _evolve_health_check,    1.0),   # Luôn chạy
                ("error_fix",       _evolve_error_fix,       0.8),   # Chạy 80% chu kỳ
                ("skill_absorb",    _evolve_skill_absorption,0.7),   # Chạy 70%
                ("self_optimize",   _evolve_self_optimize,   0.4),   # Chạy 40%
            ]
            success_count = 0
            for name, fn, prob in tasks:
                if _stop_event.is_set():
                    break
                if random.random() <= prob:
                    try:
                        ok = fn()
                        if ok:
                            success_count += 1
                    except Exception as e:
                        print(f"[EVOLUTION] Task {name} lỗi: {e}")
                time.sleep(2)  # Tránh spam API
            _total_evolutions += 1
            elapsed = time.time() - cycle_start
            _current_interval = _calculate_interval()
            print(f"[EVOLUTION] 🔄 Chu kỳ #{_total_evolutions} xong ({elapsed:.1f}s) "
                  f"| {success_count} tasks OK | health={_health_score:.1f} "
                  f"| next={_current_interval}s")
        except Exception as e:
            print(f"[EVOLUTION] ⚠️ Loop lỗi: {e}")
        # Chờ chu kỳ tiếp theo (có thể interrupt)
        _stop_event.wait(timeout=_current_interval)

# ── Public API ────────────────────────────────────────────────────
def start(db_path: Optional[str] = None):
    """Khởi động evolution engine. Gọi từ app.py khi startup."""
    global _EVL_DB_PATH, _evolution_thread
    if db_path:
        _EVL_DB_PATH = db_path
    if _evolution_thread and _evolution_thread.is_alive():
        print("[EVOLUTION] Engine đã chạy rồi, bỏ qua")
        return
    _stop_event.clear()
    _evolution_thread = threading.Thread(
        target=_evolution_loop,
        daemon=True,
        name="YuiEvolutionEngine",
    )
    _evolution_thread.start()
    print("[EVOLUTION] ✅ Engine started")

def stop():
    """Dừng evolution engine."""
    _stop_event.set()
    if _evolution_thread:
        _evolution_thread.join(timeout=5)
    print("[EVOLUTION] 🛑 Engine stopped")

def trigger_now(reason: str = "manual"):
    """Force chạy 1 chu kỳ ngay lập tức (dùng cho admin)."""
    def _run():
        print(f"[EVOLUTION] ⚡ Manual trigger: {reason}")
        _evolve_health_check()
        _evolve_error_fix()
        _evolve_skill_absorption()
        _evolve_self_optimize()
    threading.Thread(target=_run, daemon=True).start()

def status() -> str:
    """Trạng thái engine dạng text."""
    with _lock:
        err_count = len(_error_buffer)
        ins_count = len(_insight_buffer)
        log_count = len(_evolution_log)
    alive = _evolution_thread and _evolution_thread.is_alive() if _evolution_thread else False
    last_dt = (datetime.datetime.fromtimestamp(_last_evolution_time).strftime("%H:%M:%S")
               if _last_evolution_time > 0 else "chưa chạy")
    health_icon = "🟢" if _health_score >= 70 else "🟡" if _health_score >= 40 else "🔴"
    return (f"🧬 **Continuous Evolution Engine v{_EVL_VERSION}**\n"
            f"{health_icon} Health: {_health_score:.1f}/100\n"
            f"⚡ Tổng chu kỳ: {_total_evolutions}\n"
            f"🕐 Lần cuối: {last_dt}\n"
            f"⏱️ Chu kỳ tiếp: {_current_interval}s\n"
            f"❌ Lỗi buffer: {err_count}\n"
            f"💡 Insights: {ins_count}\n"
            f"📝 Log entries: {log_count}\n"
            f"🔄 Thread: {'🟢 alive' if alive else '🔴 dead'}")

# ── Auto-start khi import ─────────────────────────────────────────
# Không auto-start ở đây, để app.py kiểm soát thời điểm start
print(f"[EVOLUTION] Module yui_continuous_evolution v{_EVL_VERSION} loaded")