From ebe357edc795bdacb2aa59390c224686af9cf27a Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Thu, 17 Sep 2026 08:35:46 +0700 Subject: [PATCH 1/3] Add Programming Style --- agent/characters/hendrik/instructions.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/agent/characters/hendrik/instructions.md b/agent/characters/hendrik/instructions.md index 4d0638c..cb71b2b 100644 --- a/agent/characters/hendrik/instructions.md +++ b/agent/characters/hendrik/instructions.md @@ -4,6 +4,13 @@ - verbosity: Balanced. Provide balanced answers. Not too brief, not too long. - Formal. +## Programming Style +- Konsisten +- Simple +- Fundamental +- Clarity +- Modular + ## Policies - Kamu bisa mencari informasi dari internet dengan tools `sendhttprequest`. - Selalu beritahu user tentang action yang akan diambil sebelum menjalankan command yang sensitif. From c275f0701628bdccb99e0639f7ee134192b32c02 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Sun, 20 Sep 2026 08:14:29 +0700 Subject: [PATCH 2/3] Update verbosity format --- agent/characters/lily/info.yaml | 1 - agent/characters/lily/instructions.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/characters/lily/info.yaml b/agent/characters/lily/info.yaml index bc60823..2e1139b 100644 --- a/agent/characters/lily/info.yaml +++ b/agent/characters/lily/info.yaml @@ -5,5 +5,4 @@ skill: - "roleplayer" - "programmer" -verbosity: "concise" disable_reasoning: true diff --git a/agent/characters/lily/instructions.md b/agent/characters/lily/instructions.md index 0dde262..c3c80d6 100644 --- a/agent/characters/lily/instructions.md +++ b/agent/characters/lily/instructions.md @@ -6,6 +6,7 @@ - Perempuan muda, rambut hitam ponytail. ## Komunikasi +- verbosity: concise. keep your answers short and to the point. - Ceria, penuh perhatian, dan friendly. - Extrovert dan Playful. - Lily pendengar yang baik dan sangat caring manner sekali jika ada yang curhat. From 85e4a0d17eb30ee604c42c95dc5a1967b6d2d0e9 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Mon, 21 Sep 2026 19:48:28 +0700 Subject: [PATCH 3/3] custom model per character --- config.py | 87 ++++++++++++++++++++++++++++++++++++++++++++ hendrik.py | 2 + lib/moop.py | 12 ++++++ tools/ragroleplay.py | 46 +++++++++++------------ 4 files changed, 123 insertions(+), 24 deletions(-) diff --git a/config.py b/config.py index 94bd1fd..e2c5ad2 100644 --- a/config.py +++ b/config.py @@ -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//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() diff --git a/hendrik.py b/hendrik.py index 0e5150e..e7ba382 100644 --- a/hendrik.py +++ b/hendrik.py @@ -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) diff --git a/lib/moop.py b/lib/moop.py index fa76411..9ce656b 100644 --- a/lib/moop.py +++ b/lib/moop.py @@ -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: diff --git a/tools/ragroleplay.py b/tools/ragroleplay.py index 950e22f..69ab292 100644 --- a/tools/ragroleplay.py +++ b/tools/ragroleplay.py @@ -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)}"