custom model per character

This commit is contained in:
Dita Aji Pratama 2026-09-21 19:48:28 +07:00
parent b052fccf0f
commit 85e4a0d17e
4 changed files with 123 additions and 24 deletions

View File

@ -237,6 +237,93 @@ def load_service_config(char_override=None):
TELEGRAM_SELECTIVE_RESPONSE = str(telegram_data["selective_response"]).strip().lower() in ("true", "1", "yes")
# ─── Model Config (model.yaml per karakter) ────────────────────────────────────
MODEL_YAML_PATH = None # Path model.yaml milik karakter aktif, jika ada
CHARACTER_MODELS = {} # {type_name: model_set_name} dari model.yaml
def _reset_model_config():
"""Reset konfigurasi model dari model.yaml."""
global MODEL_YAML_PATH, CHARACTER_MODELS
MODEL_YAML_PATH = None
CHARACTER_MODELS = {}
def load_model_config(char_override=None):
"""Terapkan model.yaml milik karakter aktif ke default moop (sqlite).
Jika file agent/characters/<char>/model.yaml ada, tiap tipe yang terisi
(llm/embedding/imagegen/imagevision) akan dijadikan default model set
untuk tipe tersebut. Tipe kosong/missing tidak diubah; karakter tanpa
model.yaml akan memakai default yang sudah ada.
Mengubah default DB (moop.set_default) sehingga berlaku persist, sama
seperti aksi 'set as default' di TUI. import moop dilakukan lazy untuk
menghindari circular import (moop meng-import config).
"""
global MODEL_YAML_PATH, CHARACTER_MODELS
_reset_model_config()
if char_override:
cfg_char = char_override.strip().lower()
else:
cfg_char = AGENT_CHARACTER
if not cfg_char:
return
model_yaml_path = CHARACTERS_DIR / cfg_char / "model.yaml"
if not model_yaml_path.is_file():
return
try:
data = yaml.safe_load(model_yaml_path.read_text(encoding="utf-8")) or {}
if not isinstance(data, dict):
data = {}
except Exception as _e:
print(f"[config] Warning: gagal load model.yaml untuk '{cfg_char}': {_e}", flush=True)
return
if not data:
return
from lib import moop # lazy import
applied = []
for tname in moop.MODEL_TYPES:
value = data.get(tname)
if value is None:
continue
set_name = str(value).strip()
if not set_name:
continue
s = moop.get_set_by_name(None, set_name)
if not s:
print(
f"[config] Warning: model.yaml '{cfg_char}' merujuk set "
f"'{set_name}' untuk tipe '{tname}', tapi set tidak ditemukan. Diabaikan.",
flush=True,
)
continue
chain = moop.resolve_chain(None, s["id"])
if not chain:
print(
f"[config] Warning: model.yaml '{cfg_char}' merujuk set "
f"'{set_name}' untuk tipe '{tname}', tapi set tidak punya model aktif. Diabaikan.",
flush=True,
)
continue
moop.set_default(None, tname, s["id"])
CHARACTER_MODELS[tname] = set_name
applied.append(f"{tname}={set_name}")
if applied:
MODEL_YAML_PATH = model_yaml_path
print(f"[config] Model config dari model.yaml: {', '.join(applied)}", flush=True)
# Load character & service dari config.yaml saat import time
load_character()
load_service_config()

View File

@ -78,6 +78,8 @@ def main():
# Override char configuration from args
if args.char:
config.load_service_config(char_override=args.char)
# Terapkan model.yaml milik karakter aktif (jika ada) ke default moop
config.load_model_config()
# Set workspace from args
if args.workspace:
resolved = os.path.abspath(args.workspace)

View File

@ -235,6 +235,18 @@ def get_set(path=None, set_id=None):
conn.close()
def get_set_by_name(path=None, name=""):
"""Cari model set berdasarkan nama (case-insensitive). Returns None jika tidak ada."""
name = (name or "").strip()
if not name:
return None
target = name.lower()
for s in list_sets(path):
if (s.get("name") or "").strip().lower() == target:
return s
return None
def create_set(path=None, name=""):
conn = _connect(path)
try:

View File

@ -2,14 +2,17 @@ import gc, sys, uuid, lancedb
import config, lib.ragroleplay as ragroleplay_lib
from lib import personality
_emb_url, _emb_model = ragroleplay_lib.embedding_endpoint()
_EMB_GUIDANCE = "Ragroleplay butuh embedding model set. Atur dulu di TUI: Model > Manage Sets (Model Option), tipe 'embedding'."
def _get_emb():
ep = ragroleplay_lib.embedding_endpoint()
if ep:
return ep[0], ep[1]
return None, None
def _emb_ready():
if not _emb_url or not _emb_model:
return False
return True
_url, _model = _get_emb()
return bool(_url and _model)
def _uuid(val):
if isinstance(val, str):
@ -45,8 +48,7 @@ def users_store(fullname, nickname, character, id=None, alias=None, salutation=N
try:
success = ragroleplay_lib.users_store(
config.ragroleplay_db_path,
_emb_url,
_emb_model,
*_get_emb(),
fullname,
nickname,
alias,
@ -542,7 +544,7 @@ def memories_check(prompt_text, search_limit):
try:
db = lancedb.connect(config.ragroleplay_db_path)
table = db.open_table("knowledge_memories")
query_vector = ragroleplay_lib.embed_text(_emb_url, _emb_model, prompt_text)
query_vector = ragroleplay_lib.embed_text(*_get_emb(), prompt_text)
results = table.search(query_vector, vector_column_name="vector_context").limit(search_limit).to_list()
if not results:
@ -578,8 +580,7 @@ def memories_store(character, user, event, category, detail, physical, emotional
try:
success = ragroleplay_lib.memories_store(
config.ragroleplay_db_path,
_emb_url,
_emb_model,
*_get_emb(),
character,
_uuid(user),
event,
@ -622,8 +623,7 @@ def memories_summarize(prompt_text, search_limit=5):
result = ragroleplay_lib.memories_summarize(
config.ragroleplay_db_path,
prompt_text,
_emb_url,
_emb_model,
*_get_emb(),
search_limit
)
return result
@ -658,8 +658,7 @@ def memories_update(memory_id, **updates):
try:
success = ragroleplay_lib.memories_update(
config.ragroleplay_db_path,
_emb_url,
_emb_model,
*_get_emb(),
_uuid(memory_id),
**updates
)
@ -715,8 +714,7 @@ def users_update(user_id, **updates):
try:
success = ragroleplay_lib.users_update(
config.ragroleplay_db_path,
_emb_url,
_emb_model,
*_get_emb(),
_uuid(user_id),
**updates
)
@ -741,7 +739,7 @@ def users_delete(user_id):
def objects_store(keyword, when, where, name, description, shape, usage):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.objects_store(config.ragroleplay_db_path, _emb_url, _emb_model, keyword, when, where, name, description, shape, usage)
success = ragroleplay_lib.objects_store(config.ragroleplay_db_path, *_get_emb(), keyword, when, where, name, description, shape, usage)
return "Berhasil menyimpan objek baru." if success else "Gagal menyimpan objek."
except Exception as e: return f"Error: {str(e)}"
@ -758,7 +756,7 @@ def objects_filter(keyword=None, object_id=None):
def objects_update(object_id, **updates):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.objects_update(config.ragroleplay_db_path, _emb_url, _emb_model, _uuid(object_id), **updates)
success = ragroleplay_lib.objects_update(config.ragroleplay_db_path, *_get_emb(), _uuid(object_id), **updates)
return "Berhasil memperbarui objek." if success else "Gagal memperbarui objek."
except Exception as e: return f"Error: {str(e)}"
@ -772,7 +770,7 @@ def objects_delete(object_id):
def outfits_store(keyword, when, outfit):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.outfits_store(config.ragroleplay_db_path, _emb_url, _emb_model, keyword, when, outfit)
success = ragroleplay_lib.outfits_store(config.ragroleplay_db_path, *_get_emb(), keyword, when, outfit)
return "Berhasil menyimpan outfit baru." if success else "Gagal menyimpan outfit."
except Exception as e: return f"Error: {str(e)}"
@ -789,7 +787,7 @@ def outfits_filter(keyword=None, outfit_id=None):
def outfits_update(outfit_id, **updates):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.outfits_update(config.ragroleplay_db_path, _emb_url, _emb_model, _uuid(outfit_id), **updates)
success = ragroleplay_lib.outfits_update(config.ragroleplay_db_path, *_get_emb(), _uuid(outfit_id), **updates)
return "Berhasil memperbarui outfit." if success else "Gagal memperbarui outfit."
except Exception as e: return f"Error: {str(e)}"
@ -803,7 +801,7 @@ def outfits_delete(outfit_id):
def todos_store(keyword, when, do):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.todos_store(config.ragroleplay_db_path, _emb_url, _emb_model, keyword, when, do)
success = ragroleplay_lib.todos_store(config.ragroleplay_db_path, *_get_emb(), keyword, when, do)
return "Berhasil menyimpan to-do baru." if success else "Gagal menyimpan to-do."
except Exception as e: return f"Error: {str(e)}"
@ -820,7 +818,7 @@ def todos_filter(keyword=None, todo_id=None):
def todos_update(todo_id, **updates):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.todos_update(config.ragroleplay_db_path, _emb_url, _emb_model, _uuid(todo_id), **updates)
success = ragroleplay_lib.todos_update(config.ragroleplay_db_path, *_get_emb(), _uuid(todo_id), **updates)
return "Berhasil memperbarui to-do." if success else "Gagal memperbarui to-do."
except Exception as e: return f"Error: {str(e)}"
@ -834,7 +832,7 @@ def todos_delete(todo_id):
def worlds_store(category, location, description):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.worlds_store(config.ragroleplay_db_path, _emb_url, _emb_model, category, location, description)
success = ragroleplay_lib.worlds_store(config.ragroleplay_db_path, *_get_emb(), category, location, description)
return "Berhasil menyimpan lokasi dunia baru." if success else "Gagal menyimpan lokasi dunia."
except Exception as e: return f"Error: {str(e)}"
@ -851,7 +849,7 @@ def worlds_filter(category=None, location=None, world_id=None):
def worlds_update(world_id, **updates):
if not _emb_ready(): return _EMB_GUIDANCE
try:
success = ragroleplay_lib.worlds_update(config.ragroleplay_db_path, _emb_url, _emb_model, _uuid(world_id), **updates)
success = ragroleplay_lib.worlds_update(config.ragroleplay_db_path, *_get_emb(), _uuid(world_id), **updates)
return "Berhasil memperbarui lokasi dunia." if success else "Gagal memperbarui lokasi dunia."
except Exception as e: return f"Error: {str(e)}"