hendrik/config.py

127 lines
6.9 KiB
Python

import os
from pathlib import Path
import yaml
from dotenv import load_dotenv
load_dotenv()
# ─── YAML Config Loader ────────────────────────────────────────────────────────
_CONFIG_PATH = Path(__file__).resolve().parent / "config.yaml"
_yaml: dict = {}
if _CONFIG_PATH.is_file():
with open(_CONFIG_PATH, "r", encoding="utf-8") as f:
_yaml = yaml.safe_load(f) or {}
def _yaml_get(*keys, default=None):
"""Navigate nested yaml dict, return default if any key missing."""
d = _yaml
for k in keys:
if isinstance(d, dict) and k in d:
d = d[k]
else:
return default
return d if d is not None else default
# ─── Credential / Secret (hanya dari .env) ─────────────────────────────────────
llm_baseurl = os.getenv("LLM_BASE_URL", default="http://localhost:11434/v1")
llm_model = os.getenv("LLM_MODEL", default="granite4.1:8b")
llm_api_key = os.getenv("LLM_API_KEY", default="ollama")
llm_timeout = int(os.getenv("LLM_TIMEOUT", default="600"))
XMPP_USERNAME = os.getenv("XMPP_USERNAME", default="")
XMPP_PASSWORD = os.getenv("XMPP_PASSWORD", default="")
# ─── Agent Config (YAML, bisa di-override dari .env) ────────────────────────────
AGENT_MAX_ITERATIONS = int(os.getenv("AGENT_MAX_ITERATIONS", default=_yaml_get("agent", "max_iterations", default="30")))
AGENT_MAX_TOOL_OUTPUT = int(os.getenv("AGENT_MAX_TOOL_OUTPUT", default=_yaml_get("agent", "max_tool_output", default="40000")))
# ─── Persona / Mode (YAML, bisa di-override dari .env) ──────────────────────────
PERSONA_MODE = os.getenv("PERSONA_MODE", default=_yaml_get("persona", "mode", default="programmer")).strip().lower()
PERSONA_NAME = os.getenv("PERSONA_NAME", default=_yaml_get("persona", "name", default="OWL")).strip() or "OWL"
PERSONA_AGE = os.getenv("PERSONA_AGE", default=_yaml_get("persona", "age", default="")).strip()
PERSONA_GENDER = os.getenv("PERSONA_GENDER", default=_yaml_get("persona", "gender", default="")).strip()
PERSONA_TONE = os.getenv("PERSONA_TONE", default=_yaml_get("persona", "tone", default="casual")).strip().lower() or "casual"
PERSONA_VERBOSITY = os.getenv("PERSONA_VERBOSITY", default=_yaml_get("persona", "verbosity", default="balanced")).strip().lower() or "balanced"
PERSONA_HUMOR = os.getenv("PERSONA_HUMOR", default=_yaml_get("persona", "humor", default="light")).strip().lower() or "light"
PERSONA_LANGUAGE = os.getenv("PERSONA_LANGUAGE", default=_yaml_get("persona", "language", default="id")).strip().lower() or "id"
PERSONA_MOOD = os.getenv("PERSONA_MOOD", default=_yaml_get("persona", "mood", default="cheerful")).strip().lower() or "cheerful"
PERSONA_CATCHPHRASES = os.getenv("PERSONA_CATCHPHRASES", default=_yaml_get("persona", "catchphrases", default="")).strip()
# ─── Character & Skills (YAML, bisa di-override dari .env) ─────────────────────
AGENT_CHARACTER = os.getenv("AGENT_CHARACTER", default=_yaml_get("character", "preset", default="")).strip().lower()
AGENT_SKILLS = os.getenv("AGENT_SKILLS", default=_yaml_get("character", "skills", default="")).strip().lower()
# ─── XMPP (non-credential dari YAML, credential dari .env) ─────────────────────
XMPP_ENABLED = os.getenv("XMPP_ENABLED", default=str(_yaml_get("xmpp", "enabled", default="false"))).strip().lower() in ("true", "1", "yes")
XMPP_MUC_ROOMS = os.getenv("XMPP_MUC_ROOMS", default=_yaml_get("xmpp", "muc_rooms", default="")).strip()
XMPP_NICKNAME = os.getenv("XMPP_NICKNAME", default=_yaml_get("xmpp", "nickname", default="")).strip()
XMPP_SELECTIVE_RESPONSE = os.getenv("XMPP_SELECTIVE_RESPONSE", default=str(_yaml_get("xmpp", "selective_response", default="true"))).strip().lower() in ("true", "1", "yes")
# ─── RAG (YAML) ─────────────────────────────────────────────────────────────────
RAG_PERSIST_DIR = os.getenv("RAG_PERSIST_DIR", default=_yaml_get("rag", "persist_dir", default="chroma_db"))
# ─── Humanize Delay (YAML) ─────────────────────────────────────────────────────
READ_DELAY_MIN = float(os.getenv("READ_DELAY_MIN", default=_yaml_get("delay", "read_min", default="1.0")))
READ_DELAY_MAX = float(os.getenv("READ_DELAY_MAX", default=_yaml_get("delay", "read_max", default="2.0")))
TYPING_SPEED = float(os.getenv("TYPING_SPEED", default=_yaml_get("delay", "typing_speed", default="15.0")))
TYPING_MAX = float(os.getenv("TYPING_MAX", default=_yaml_get("delay", "typing_max", default="10.0")))
# ─── Character Preset Override ──────────────────────────────────────────────────
# Jika AGENT_CHARACTER di-set, baca character.md dari agent/characters/<preset>/
# dan override nilai persona yang relevan.
ENV_CHARACTERS_DIR = Path(__file__).resolve().parent / "agent" / "characters"
ENV_CHARACTER_CONFIG_PATH = ENV_CHARACTERS_DIR / AGENT_CHARACTER / "character.md" if AGENT_CHARACTER else None
if ENV_CHARACTER_CONFIG_PATH and ENV_CHARACTER_CONFIG_PATH.is_file():
_character_env: dict[str, str] = {}
for line in ENV_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()
_character_overrides = {
"PERSONA_MODE": PERSONA_MODE,
"PERSONA_NAME": PERSONA_NAME,
"PERSONA_AGE": PERSONA_AGE,
"PERSONA_GENDER": PERSONA_GENDER,
"PERSONA_TONE": PERSONA_TONE,
"PERSONA_VERBOSITY": PERSONA_VERBOSITY,
"PERSONA_HUMOR": PERSONA_HUMOR,
"PERSONA_LANGUAGE": PERSONA_LANGUAGE,
"PERSONA_MOOD": PERSONA_MOOD,
"PERSONA_CATCHPHRASES": PERSONA_CATCHPHRASES,
}
for key, fallback in _character_overrides.items():
value = _character_env.get(key, fallback).strip()
if key in {"PERSONA_MODE", "PERSONA_TONE", "PERSONA_VERBOSITY", "PERSONA_HUMOR", "PERSONA_LANGUAGE", "PERSONA_MOOD"}:
value = value.lower() or fallback
if key == "PERSONA_NAME" and not value:
value = fallback
_character_overrides[key] = value
for key, value in _character_overrides.items():
globals()[key] = value
os.environ[key] = value