import os from pathlib import Path import yaml def _yaml_get(*keys, default=None): _CONFIG_PATH = Path(__file__).resolve().parent / "config.yaml" _yaml = {} if _CONFIG_PATH.is_file(): with open(_CONFIG_PATH, "r", encoding="utf-8") as file_object: _yaml = yaml.safe_load(file_object) or {} current = _yaml for key in keys: if key in current: current = current[key] else: return default return current if current is not None else default def _as_list(value): if isinstance(value, list): return [str(v).strip() for v in value if str(v).strip()] if value: print(f"[config] Warning: '{value}' bukan YAML list, diabaikan. Gunakan format list.", flush=True) return [] TUI_USER_ID = _yaml_get("user", "id", default=None) ragroleplay_db_path = _yaml_get("ragroleplay", "db_path", default="./.ragroleplay" ) ragroleplay_vector_size = _yaml_get("ragroleplay", "vector_size", default=768 ) # used on table create only # Model Option (moop) — penyimpanan model/provider/key pindah ke sqlite. moop_db_path = os.path.expanduser( _yaml_get("moop", "db_path", default="~/.config/hendrik/moop.sqlite3") ) llm_timeout = int(_yaml_get("llm", "timeout", default=600)) XMPP_USERNAME = _yaml_get("xmpp", "username", default="") XMPP_PASSWORD = _yaml_get("xmpp", "password", default="") AGENT_MAX_ITERATIONS = int(_yaml_get("agent", "max_iterations", default="30")) AGENT_MAX_TOOL_OUTPUT = int(_yaml_get("agent", "max_tool_output", default="40000")) PERSONALITY_NAME = "" PERSONALITY_CODENAME = "" # ─── Character & Skills ──────────────────────────────────────────────────────── AGENT_CHARACTER = _yaml_get("agent", "character", default="").strip().lower() AGENT_SKILLS = _yaml_get("agent", "skills", default="").strip().lower() # ─── XMPP ────────────────────────────────────────────────────────────────────── XMPP_ENABLED = str(_yaml_get("xmpp", "enabled", default="false")).strip().lower() in ("true", "1", "yes") XMPP_MUC_ROOMS = _as_list(_yaml_get("xmpp", "muc_rooms", default=[])) XMPP_NICKNAME = _yaml_get("xmpp", "nickname", default="").strip() XMPP_DEBUG = str(_yaml_get("xmpp", "debug", default="false")).strip().lower() in ("true", "1", "yes") XMPP_SELECTIVE_RESPONSE = str(_yaml_get("xmpp", "selective_response", default="true")).strip().lower() in ("true", "1", "yes") # ─── Telegram ────────────────────────────────────────────────────────────────── TELEGRAM_ENABLED = str(_yaml_get("telegram", "enabled", default="false")).strip().lower() in ("true", "1", "yes") TELEGRAM_TOKEN = _yaml_get("telegram", "token", default="") TELEGRAM_ALLOWED_GROUP_IDS = _as_list(_yaml_get("telegram", "allowed_group_ids", default=[])) TELEGRAM_SELECTIVE_RESPONSE = str(_yaml_get("telegram", "selective_response", default="true")).strip().lower() in ("true", "1", "yes") # ─── Session (TinyDB) ──────────────────────────────────────────────────────────── SESSION_DB_PATH = os.path.expanduser( _yaml_get("session", "db_path", default="~/.config/hendrik/sessions.json") ) # Humanize Delay (YAML) READ_DELAY_MIN = float( _yaml_get("delay", "read_min", default="1.0" ) ) READ_DELAY_MAX = float( _yaml_get("delay", "read_max", default="2.0" ) ) TYPING_SPEED = float( _yaml_get("delay", "typing_speed", default="15.0" ) ) TYPING_MAX = float( _yaml_get("delay", "typing_max", default="10.0" ) ) # ─── Image Generation (OpenRouter) ────────────────────────────────────────────── # IMAGEGEN model kini dikelola via moop (sqlite), bukan config.yaml. # ─── Character Loader ────────────────────────────────────────────────────────── CHARACTERS_DIR = Path(__file__).resolve().parent / "agent" / "characters" def _normalize_skills(value) -> str: """Normalisasi field 'skill' dari info.yaml (YAML list atau CSV string) menjadi CSV lowercase tanpa spasi, mis. 'roleplayer,programmer'.""" if value is None: return "" if isinstance(value, list): parts = [str(v).strip().lower() for v in value if str(v).strip()] else: parts = [p.strip().lower() for p in str(value).split(",") if p.strip()] return ",".join(parts) def load_character(char_override=None): """Load character info dari info.yaml. Bisa di-call ulang untuk override.""" global AGENT_CHARACTER, PERSONALITY_NAME, PERSONALITY_CODENAME, AGENT_SKILLS if char_override: AGENT_CHARACTER = char_override.strip().lower() if not AGENT_CHARACTER: return info_yaml_path = CHARACTERS_DIR / AGENT_CHARACTER / "info.yaml" if not info_yaml_path.is_file(): return try: data = yaml.safe_load(info_yaml_path.read_text(encoding="utf-8")) or {} if not isinstance(data, dict): data = {} except Exception as _e: print(f"[config] Warning: gagal load info.yaml untuk '{AGENT_CHARACTER}': {_e}", flush=True) data = {} if not data: return name = str(data.get("name", "")).strip() codename = str(data.get("codename", "")).strip() if name: PERSONALITY_NAME = name if codename: PERSONALITY_CODENAME = codename elif name: PERSONALITY_CODENAME = name # Skill: sumber utama adalah karakter (info.yaml), config.yaml hanya fallback char_skills = _normalize_skills(data.get("skill", data.get("mode"))) if char_skills: AGENT_SKILLS = char_skills SERVICE_YAML_PATH = None # Path service.yaml milik karakter aktif, jika ada def _reset_service_from_config(): """Reset konfigurasi service (xmpp/telegram) dari config.yaml global.""" global XMPP_ENABLED, XMPP_USERNAME, XMPP_PASSWORD, XMPP_MUC_ROOMS global XMPP_NICKNAME, XMPP_DEBUG, XMPP_SELECTIVE_RESPONSE global TELEGRAM_ENABLED, TELEGRAM_TOKEN, TELEGRAM_ALLOWED_GROUP_IDS global TELEGRAM_SELECTIVE_RESPONSE, SERVICE_YAML_PATH XMPP_USERNAME = _yaml_get("xmpp", "username", default="") XMPP_PASSWORD = _yaml_get("xmpp", "password", default="") XMPP_ENABLED = str(_yaml_get("xmpp", "enabled", default="false")).strip().lower() in ("true", "1", "yes") XMPP_MUC_ROOMS = _as_list(_yaml_get("xmpp", "muc_rooms", default=[])) XMPP_NICKNAME = _yaml_get("xmpp", "nickname", default="").strip() XMPP_DEBUG = str(_yaml_get("xmpp", "debug", default="false")).strip().lower() in ("true", "1", "yes") XMPP_SELECTIVE_RESPONSE = str(_yaml_get("xmpp", "selective_response", default="true")).strip().lower() in ("true", "1", "yes") TELEGRAM_ENABLED = str(_yaml_get("telegram", "enabled", default="false")).strip().lower() in ("true", "1", "yes") TELEGRAM_TOKEN = _yaml_get("telegram", "token", default="") TELEGRAM_ALLOWED_GROUP_IDS = _as_list(_yaml_get("telegram", "allowed_group_ids", default=[])) TELEGRAM_SELECTIVE_RESPONSE = str(_yaml_get("telegram", "selective_response", default="true")).strip().lower() in ("true", "1", "yes") SERVICE_YAML_PATH = None def load_service_config(char_override=None): """Override konfigurasi service (xmpp/telegram) dari service.yaml milik karakter aktif, jika file tersebut ada di agent/characters//. Memanggil load_character() bila char_override diberikan.""" global XMPP_ENABLED, XMPP_USERNAME, XMPP_PASSWORD, XMPP_MUC_ROOMS global XMPP_NICKNAME, XMPP_DEBUG, XMPP_SELECTIVE_RESPONSE global TELEGRAM_ENABLED, TELEGRAM_TOKEN, TELEGRAM_ALLOWED_GROUP_IDS global TELEGRAM_SELECTIVE_RESPONSE, SERVICE_YAML_PATH _reset_service_from_config() if char_override: load_character(char_override=char_override) if not AGENT_CHARACTER: return service_yaml_path = CHARACTERS_DIR / AGENT_CHARACTER / "service.yaml" if not service_yaml_path.is_file(): return try: data = yaml.safe_load(service_yaml_path.read_text(encoding="utf-8")) or {} if not isinstance(data, dict): data = {} except Exception as _e: print(f"[config] Warning: gagal load service.yaml untuk '{AGENT_CHARACTER}': {_e}", flush=True) data = {} if not data: return SERVICE_YAML_PATH = service_yaml_path xmpp_data = data.get("xmpp") or {} if "enabled" in xmpp_data: XMPP_ENABLED = str(xmpp_data["enabled"]).strip().lower() in ("true", "1", "yes") if "username" in xmpp_data: XMPP_USERNAME = str(xmpp_data["username"]).strip() if "password" in xmpp_data: XMPP_PASSWORD = str(xmpp_data["password"]).strip() if "muc_rooms" in xmpp_data: XMPP_MUC_ROOMS = _as_list(xmpp_data["muc_rooms"]) if "nickname" in xmpp_data: XMPP_NICKNAME = str(xmpp_data["nickname"]).strip() if "debug" in xmpp_data: XMPP_DEBUG = str(xmpp_data["debug"]).strip().lower() in ("true", "1", "yes") if "selective_response" in xmpp_data: XMPP_SELECTIVE_RESPONSE = str(xmpp_data["selective_response"]).strip().lower() in ("true", "1", "yes") telegram_data = data.get("telegram") or {} if "enabled" in telegram_data: TELEGRAM_ENABLED = str(telegram_data["enabled"]).strip().lower() in ("true", "1", "yes") if "token" in telegram_data: TELEGRAM_TOKEN = str(telegram_data["token"]).strip() if "allowed_group_ids" in telegram_data: TELEGRAM_ALLOWED_GROUP_IDS = _as_list(telegram_data["allowed_group_ids"]) if "selective_response" in telegram_data: TELEGRAM_SELECTIVE_RESPONSE = str(telegram_data["selective_response"]).strip().lower() in ("true", "1", "yes") # Load character & service dari config.yaml saat import time load_character() load_service_config()