diff --git a/memory/__init__.py b/memory/__init__.py deleted file mode 100644 index 452b936..0000000 --- a/memory/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#memory package \ No newline at end of file diff --git a/memory/config_manager.py b/memory/config_manager.py deleted file mode 100644 index dbd3ec3..0000000 --- a/memory/config_manager.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import sys -from pathlib import Path - - -def get_base_dir() -> Path: - if getattr(sys, "frozen", False): - return Path(sys.executable).parent - return Path(__file__).resolve().parent.parent - - -BASE_DIR = get_base_dir() -CONFIG_DIR = BASE_DIR / "config" -CONFIG_FILE = CONFIG_DIR / "api_keys.json" - - -def ensure_config_dir() -> None: - CONFIG_DIR.mkdir(parents=True, exist_ok=True) - - -def config_exists() -> bool: - return CONFIG_FILE.exists() - - -def save_api_keys(gemini_api_key: str) -> None: - ensure_config_dir() - - data: dict = {} - if CONFIG_FILE.exists(): - try: - data = json.loads(CONFIG_FILE.read_text(encoding="utf-8")) - except Exception: - data = {} - - data["gemini_api_key"] = gemini_api_key.strip() - - CONFIG_FILE.write_text( - json.dumps(data, indent=2), - encoding="utf-8" - ) - - -def load_api_keys() -> dict: - if not CONFIG_FILE.exists(): - return {} - try: - return json.loads(CONFIG_FILE.read_text(encoding="utf-8")) - except Exception as e: - print(f"❌ Failed to load api_keys.json: {e}") - return {} - - -def get_gemini_key() -> str | None: - return load_api_keys().get("gemini_api_key") - - -def is_configured() -> bool: - key = get_gemini_key() - return bool(key and len(key) > 15) \ No newline at end of file diff --git a/memory/memory_manager.py b/memory/memory_manager.py deleted file mode 100644 index c5005a1..0000000 --- a/memory/memory_manager.py +++ /dev/null @@ -1,298 +0,0 @@ -""" -memory_manager.py — MARK XXV Hafıza Sistemi -============================================ -Düzeltmeler: - - _MEMORY_EVERY_N_TURNS: 3 → 1 (her turda kontrol) - - Stage 1 YES/NO check daha geniş kriterlere sahip - - Extraction prompt daha kapsamlı ve agresif - - Projeleri, favori şeyleri, arkadaşları daha iyi yakalar -""" - -import json -from datetime import datetime -from threading import Lock -from pathlib import Path -import sys - - -def get_base_dir() -> Path: - if getattr(sys, "frozen", False): - return Path(sys.executable).parent - return Path(__file__).resolve().parent.parent - - -BASE_DIR = get_base_dir() -MEMORY_PATH = BASE_DIR / "memory" / "long_term.json" -_lock = Lock() -MAX_VALUE_LENGTH = 400 - - -def _empty_memory() -> dict: - return { - "identity": {}, - "preferences": {}, - "projects": {}, - "relationships": {}, - "wishes": {}, - "notes": {} - } - - -def load_memory() -> dict: - if not MEMORY_PATH.exists(): - return _empty_memory() - - with _lock: - try: - data = json.loads(MEMORY_PATH.read_text(encoding="utf-8")) - if isinstance(data, dict): - base = _empty_memory() - for key in base: - if key not in data: - data[key] = {} - return data - return _empty_memory() - except Exception as e: - print(f"[Memory] ⚠️ Load error: {e}") - return _empty_memory() - - -def save_memory(memory: dict) -> None: - if not isinstance(memory, dict): - return - MEMORY_PATH.parent.mkdir(parents=True, exist_ok=True) - with _lock: - MEMORY_PATH.write_text( - json.dumps(memory, indent=2, ensure_ascii=False), - encoding="utf-8" - ) - - -def _truncate_value(val: str) -> str: - if isinstance(val, str) and len(val) > MAX_VALUE_LENGTH: - return val[:MAX_VALUE_LENGTH].rstrip() + "…" - return val - - -def _recursive_update(target: dict, updates: dict) -> bool: - changed = False - for key, value in updates.items(): - if value is None: - continue - if isinstance(value, str) and not value.strip(): - continue - - if isinstance(value, dict) and "value" not in value: - if key not in target or not isinstance(target[key], dict): - target[key] = {} - changed = True - if _recursive_update(target[key], value): - changed = True - else: - if isinstance(value, dict) and "value" in value: - new_val = _truncate_value(str(value["value"])) - else: - new_val = _truncate_value(str(value)) - - entry = {"value": new_val, "updated": datetime.now().strftime("%Y-%m-%d")} - existing = target.get(key, {}) - if not isinstance(existing, dict) or existing.get("value") != new_val: - target[key] = entry - changed = True - - return changed - - -def update_memory(memory_update: dict) -> dict: - if not isinstance(memory_update, dict) or not memory_update: - return load_memory() - - memory = load_memory() - if _recursive_update(memory, memory_update): - save_memory(memory) - print(f"[Memory] 💾 Saved: {list(memory_update.keys())}") - return memory - - -def should_extract_memory(user_text: str, jarvis_text: str, api_key: str) -> bool: - """ - Stage 1: Hızlı YES/NO kontrolü. - Öncekinden daha geniş kriterler — favori şeyler, projeler, arkadaşlar da dahil. - """ - try: - import google.generativeai as genai - genai.configure(api_key=api_key) - model = genai.GenerativeModel("gemini-2.5-flash-lite") - - # Her iki tarafı da gönder — Jarvis'in söyledikleri de bilgi içerebilir - combined = f"User: {user_text[:300]}\nJarvis: {jarvis_text[:200]}" - - check = model.generate_content( - f"Does this conversation contain ANY of the following?\n" - f"- Personal facts (name, age, city, job, birthday, nationality)\n" - f"- Preferences or favorites (food, color, music, sport, game, film, book, etc.)\n" - f"- Active projects or goals the user is working on\n" - f"- People in the user's life (friends, family, partner, colleagues)\n" - f"- Things the user wants to do or buy in the future\n" - f"- Any other fact worth remembering long-term\n\n" - f"Reply only YES or NO.\n\nConversation:\n{combined}" - ) - return "YES" in check.text.upper() - except Exception as e: - print(f"[Memory] ⚠️ Stage1 check failed: {e}") - return False - - -def extract_memory(user_text: str, jarvis_text: str, api_key: str) -> dict: - """ - Stage 2: Detaylı çıkarım. Her iki tarafı da analiz eder. - """ - try: - import google.generativeai as genai - genai.configure(api_key=api_key) - model = genai.GenerativeModel("gemini-2.5-flash-lite") - - combined = f"User: {user_text[:500]}\nJarvis: {jarvis_text[:300]}" - - raw = model.generate_content( - f"Extract ALL memorable personal facts from this conversation. Any language.\n" - f"Return ONLY valid JSON. Use {{}} if truly nothing is worth saving.\n\n" - f"Category guide:\n" - f" identity → name, age, birthday, city, country, job, school, nationality, language\n" - f" preferences → ANY favorite or preferred thing:\n" - f" favorite_food, favorite_color, favorite_music, favorite_film,\n" - f" favorite_game, favorite_sport, favorite_book, favorite_artist,\n" - f" favorite_country, hobbies, interests, dislikes, etc.\n" - f" projects → projects being built, ongoing work, goals, ideas in progress\n" - f" (e.g. mark_xxv: 'Building a JARVIS-like AI assistant')\n" - f" relationships → people mentioned: friends, family, partner, colleagues\n" - f" (e.g. best_friend_ali: 'Best friend, met in university')\n" - f" wishes → future plans, things to buy, travel plans, dreams\n" - f" notes → anything else worth remembering (habits, schedule, etc.)\n\n" - f"IMPORTANT:\n" - f"- Be LIBERAL: if something MIGHT be worth remembering, include it.\n" - f"- Extract from BOTH user and Jarvis turns.\n" - f"- Skip: weather, reminders, search results, one-time commands.\n" - f"- Use concise English values regardless of conversation language.\n\n" - f"Format:\n" - f'{{"identity":{{"name":{{"value":"Ali"}}}},\n' - f' "preferences":{{"favorite_color":{{"value":"blue"}}, "hobby":{{"value":"gaming"}}}},\n' - f' "projects":{{"mark_xxv":{{"value":"JARVIS-like AI assistant on Windows"}}}},\n' - f' "relationships":{{"friend_yusuf":{{"value":"close friend"}}}},\n' - f' "wishes":{{"buy_guitar":{{"value":"wants an acoustic guitar"}}}},\n' - f' "notes":{{"works_at_night":{{"value":"usually active late at night"}}}}}}\n\n' - f"Conversation:\n{combined}\n\nJSON:" - ).text.strip() - - import re - raw = re.sub(r"```(?:json)?", "", raw).strip().rstrip("`").strip() - if not raw or raw == "{}": - return {} - - return json.loads(raw) - - except json.JSONDecodeError: - return {} - except Exception as e: - if "429" not in str(e): - print(f"[Memory] ⚠️ Extract failed: {e}") - return {} - - -def format_memory_for_prompt(memory: dict | None) -> str: - if not memory: - return "" - - lines = [] - - identity = memory.get("identity", {}) - id_fields = ["name", "age", "birthday", "city", "job", "language", "school", "nationality"] - for field in id_fields: - entry = identity.get(field) - if entry: - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f"{field.title()}: {val}") - for key, entry in identity.items(): - if key in id_fields: - continue - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f"{key.replace('_', ' ').title()}: {val}") - - prefs = memory.get("preferences", {}) - if prefs: - lines.append("") - lines.append("Preferences:") - for key, entry in list(prefs.items())[:15]: - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f" - {key.replace('_', ' ').title()}: {val}") - - projects = memory.get("projects", {}) - if projects: - lines.append("") - lines.append("Active Projects / Goals:") - for key, entry in list(projects.items())[:8]: - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f" - {key.replace('_', ' ').title()}: {val}") - - rels = memory.get("relationships", {}) - if rels: - lines.append("") - lines.append("People in their life:") - for key, entry in list(rels.items())[:10]: - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f" - {key.replace('_', ' ').title()}: {val}") - - wishes = memory.get("wishes", {}) - if wishes: - lines.append("") - lines.append("Wishes / Plans / Wants:") - for key, entry in list(wishes.items())[:8]: - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f" - {key.replace('_', ' ').title()}: {val}") - - notes = memory.get("notes", {}) - if notes: - lines.append("") - lines.append("Other notes:") - for key, entry in list(notes.items())[:8]: - val = entry.get("value") if isinstance(entry, dict) else entry - if val: - lines.append(f" - {key}: {val}") - - if not lines: - return "" - - header = "[WHAT YOU KNOW ABOUT THIS PERSON — use naturally, never recite like a list]\n" - result = header + "\n".join(lines) - if len(result) > 2000: - result = result[:1997] + "…" - - return result + "\n" - - -def remember(key: str, value: str, category: str = "notes") -> str: - valid = {"identity", "preferences", "projects", "relationships", "wishes", "notes"} - if category not in valid: - category = "notes" - update_memory({category: {key: {"value": value}}}) - return f"Remembered: {category}/{key} = {value}" - - -def forget(key: str, category: str = "notes") -> str: - memory = load_memory() - cat = memory.get(category, {}) - if key in cat: - del cat[key] - memory[category] = cat - save_memory(memory) - return f"Forgotten: {category}/{key}" - return f"Not found: {category}/{key}" - -# Alias — eski import'larla uyumluluk için -forget_memory = forget \ No newline at end of file