215 lines
8.6 KiB
Python
215 lines
8.6 KiB
Python
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
|
|
|
|
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
|
|
ragroleplay_model_url = _yaml_get("ragroleplay", "model_url", default="http://localhost:11434/api/embed" )
|
|
ragroleplay_model_name = _yaml_get("ragroleplay", "model_name", default="nomic-embed-text" ) # Need to download model from local ollama
|
|
|
|
llm_timeout = int(_yaml_get("llm", "timeout", default=600))
|
|
_providers = _yaml_get("llm", "providers", default=[])
|
|
MODELS_ITEMS = []
|
|
|
|
for prov in _providers:
|
|
pname = prov.get("name", "")
|
|
base_url = prov.get("base_url", "").rstrip("/")
|
|
api_key = prov.get("api_key", "")
|
|
models = prov.get("models", [])
|
|
for m in models:
|
|
model_name = m.get("name", "")
|
|
is_default = m.get("default", False)
|
|
MODELS_ITEMS.append({
|
|
"model" : model_name,
|
|
"provider" : pname,
|
|
"base_url" : base_url,
|
|
"api_key" : api_key,
|
|
"default" : is_default,
|
|
})
|
|
|
|
|
|
def resolve_provider(base_url: str, model: str) -> str | None:
|
|
base_url = base_url.rstrip("/")
|
|
for item in MODELS_ITEMS:
|
|
if item["base_url"] == base_url and item["model"] == model:
|
|
return item["provider"]
|
|
return None
|
|
|
|
|
|
# Cari model default — pertama yg marked default: true, fallback ke yg pertama
|
|
llm_baseurl = ""
|
|
llm_model = ""
|
|
llm_api_key = ""
|
|
for item in MODELS_ITEMS:
|
|
if item.get("default"):
|
|
llm_baseurl = item["base_url" ]
|
|
llm_model = item["model" ]
|
|
llm_api_key = item["api_key" ]
|
|
break
|
|
if not llm_model and MODELS_ITEMS:
|
|
llm_baseurl = MODELS_ITEMS[0]["base_url" ]
|
|
llm_model = MODELS_ITEMS[0]["model" ]
|
|
llm_api_key = MODELS_ITEMS[0]["api_key" ]
|
|
|
|
|
|
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"))
|
|
|
|
AGENT_SKILL = "programmer"
|
|
PERSONALITY_NAME = "Hendrik"
|
|
PERSONALITY_AGE = ""
|
|
PERSONALITY_GENDER = ""
|
|
PERSONALITY_TONE = "casual"
|
|
PERSONALITY_VERBOSITY = "balanced"
|
|
PERSONALITY_HUMOR = "light"
|
|
PERSONALITY_LANGUAGE = "id"
|
|
PERSONALITY_MOOD = "cheerful"
|
|
PERSONALITY_CATCHPHRASES = ""
|
|
|
|
|
|
# ─── 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 = _yaml_get("xmpp", "muc_rooms", default="").strip()
|
|
XMPP_NICKNAME = _yaml_get("xmpp", "nickname", default="").strip()
|
|
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 = _yaml_get("telegram", "allowed_group_ids", default="").strip()
|
|
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" ) )
|
|
|
|
|
|
# ─── Character Loader ──────────────────────────────────────────────────────────
|
|
|
|
CHARACTERS_DIR = Path(__file__).resolve().parent / "agent" / "characters"
|
|
|
|
_yaml_to_config_key = {
|
|
"AGENT_SKILL": "skill",
|
|
"PERSONALITY_NAME": "name",
|
|
"PERSONALITY_AGE": "age",
|
|
"PERSONALITY_GENDER": "gender",
|
|
"PERSONALITY_TONE": "tone",
|
|
"PERSONALITY_VERBOSITY": "verbosity",
|
|
"PERSONALITY_HUMOR": "humor",
|
|
"PERSONALITY_LANGUAGE": "language",
|
|
"PERSONALITY_MOOD": "mood",
|
|
"PERSONALITY_CATCHPHRASES": "catchphrases",
|
|
}
|
|
|
|
_lowercase_keys = {"AGENT_SKILL", "PERSONALITY_TONE", "PERSONALITY_VERBOSITY", "PERSONALITY_HUMOR", "PERSONALITY_LANGUAGE", "PERSONALITY_MOOD"}
|
|
|
|
|
|
def load_character(char_override=None):
|
|
"""Load personality dari character directory. Bisa di-call ulang untuk override."""
|
|
global AGENT_CHARACTER, AGENT_SKILL, PERSONALITY_NAME, PERSONALITY_AGE, PERSONALITY_GENDER
|
|
global PERSONALITY_TONE, PERSONALITY_VERBOSITY, PERSONALITY_HUMOR, PERSONALITY_LANGUAGE
|
|
global PERSONALITY_MOOD, PERSONALITY_CATCHPHRASES
|
|
|
|
if char_override:
|
|
AGENT_CHARACTER = char_override.strip().lower()
|
|
|
|
if not AGENT_CHARACTER:
|
|
return
|
|
|
|
personality_yaml_path = CHARACTERS_DIR / AGENT_CHARACTER / "personality.yaml"
|
|
character_config_path = CHARACTERS_DIR / AGENT_CHARACTER / "character.md"
|
|
|
|
if personality_yaml_path.is_file():
|
|
try:
|
|
_character_env = yaml.safe_load(personality_yaml_path.read_text(encoding="utf-8")) or {}
|
|
if not isinstance(_character_env, dict):
|
|
_character_env = {}
|
|
except Exception as _e:
|
|
print(f"[config] Warning: gagal load personality.yaml untuk '{AGENT_CHARACTER}': {_e}", flush=True)
|
|
_character_env = {}
|
|
elif character_config_path.is_file():
|
|
_character_env = {}
|
|
for line in character_config_path.read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
if "=" in line:
|
|
key, value = line.split("=", 1)
|
|
_character_env[key.strip()] = value.strip()
|
|
else:
|
|
return
|
|
|
|
if not _character_env:
|
|
return
|
|
|
|
_defaults = {
|
|
"AGENT_SKILL": AGENT_SKILL,
|
|
"PERSONALITY_NAME": PERSONALITY_NAME,
|
|
"PERSONALITY_AGE": PERSONALITY_AGE,
|
|
"PERSONALITY_GENDER": PERSONALITY_GENDER,
|
|
"PERSONALITY_TONE": PERSONALITY_TONE,
|
|
"PERSONALITY_VERBOSITY": PERSONALITY_VERBOSITY,
|
|
"PERSONALITY_HUMOR": PERSONALITY_HUMOR,
|
|
"PERSONALITY_LANGUAGE": PERSONALITY_LANGUAGE,
|
|
"PERSONALITY_MOOD": PERSONALITY_MOOD,
|
|
"PERSONALITY_CATCHPHRASES": PERSONALITY_CATCHPHRASES,
|
|
}
|
|
|
|
for key, fallback in _defaults.items():
|
|
yaml_key = _yaml_to_config_key.get(key, key.lower())
|
|
raw = _character_env.get(yaml_key, _character_env.get(key, fallback))
|
|
value = str(raw).strip() if raw is not None else ""
|
|
if key in _lowercase_keys:
|
|
value = value.lower() or fallback
|
|
if key == "PERSONALITY_NAME" and not value:
|
|
value = fallback
|
|
_defaults[key] = value
|
|
|
|
for key, value in _defaults.items():
|
|
globals()[key] = value
|
|
|
|
|
|
# Load character dari config.yaml saat import time
|
|
load_character()
|