import os, sys import argparse import threading, time, signal import config from tools import coder, carrack, ragroleplay from services.xmpp_client import XMPPClient from services.telegram_client import TelegramClient from services.llm_client import LLMClient from lib import gadget, personality import lib.ragroleplay as ragroleplay_lib from interfaces.tui import HendrikTUI tools_definition = [ gadget.tools_mapping( schema = coder.schema_read_file, handler = coder.read_file ), gadget.tools_mapping( schema = coder.schema_write_file, handler = coder.write_file ), gadget.tools_mapping( schema = coder.schema_edit_file, handler = coder.edit_file ), gadget.tools_mapping( schema = coder.schema_run_bash, handler = coder.run_bash ), gadget.tools_mapping( schema = coder.schema_search_code, handler = coder.search_code ), gadget.tools_mapping( schema = coder.schema_git_operation, handler = coder.git_operation ), gadget.tools_mapping( schema = carrack.schema_sendhttprequest, handler = carrack.sendhttprequest ), # Rag Roleplay Tools gadget.tools_mapping( schema = ragroleplay.schema_users_store, handler = ragroleplay.users_store ), gadget.tools_mapping( schema = ragroleplay.schema_users_filter, handler = ragroleplay.users_filter ), gadget.tools_mapping( schema = ragroleplay.schema_users_update, handler = ragroleplay.users_update ), gadget.tools_mapping( schema = ragroleplay.schema_users_delete, handler = ragroleplay.users_delete ), gadget.tools_mapping( schema = ragroleplay.schema_memories_check, handler = ragroleplay.memories_check ), gadget.tools_mapping( schema = ragroleplay.schema_memories_store, handler = ragroleplay.memories_store ), gadget.tools_mapping( schema = ragroleplay.schema_memories_filter, handler = ragroleplay.memories_filter ), gadget.tools_mapping( schema = ragroleplay.schema_memories_summarize, handler = ragroleplay.memories_summarize ), gadget.tools_mapping( schema = ragroleplay.schema_memories_latest, handler = ragroleplay.memories_latest ), gadget.tools_mapping( schema = ragroleplay.schema_memories_update, handler = ragroleplay.memories_update ), gadget.tools_mapping( schema = ragroleplay.schema_memories_delete, handler = ragroleplay.memories_delete ), gadget.tools_mapping( schema = ragroleplay.schema_worlds_store, handler = ragroleplay.worlds_store ), gadget.tools_mapping( schema = ragroleplay.schema_worlds_filter, handler = ragroleplay.worlds_filter ), gadget.tools_mapping( schema = ragroleplay.schema_worlds_update, handler = ragroleplay.worlds_update ), gadget.tools_mapping( schema = ragroleplay.schema_worlds_delete, handler = ragroleplay.worlds_delete ), gadget.tools_mapping( schema = ragroleplay.schema_objects_store, handler = ragroleplay.objects_store ), gadget.tools_mapping( schema = ragroleplay.schema_objects_filter, handler = ragroleplay.objects_filter ), gadget.tools_mapping( schema = ragroleplay.schema_objects_update, handler = ragroleplay.objects_update ), gadget.tools_mapping( schema = ragroleplay.schema_objects_delete, handler = ragroleplay.objects_delete ), gadget.tools_mapping( schema = ragroleplay.schema_outfits_store, handler = ragroleplay.outfits_store ), gadget.tools_mapping( schema = ragroleplay.schema_outfits_filter, handler = ragroleplay.outfits_filter ), gadget.tools_mapping( schema = ragroleplay.schema_outfits_update, handler = ragroleplay.outfits_update ), gadget.tools_mapping( schema = ragroleplay.schema_outfits_delete, handler = ragroleplay.outfits_delete ), gadget.tools_mapping( schema = ragroleplay.schema_todos_store, handler = ragroleplay.todos_store ), gadget.tools_mapping( schema = ragroleplay.schema_todos_filter, handler = ragroleplay.todos_filter ), gadget.tools_mapping( schema = ragroleplay.schema_todos_update, handler = ragroleplay.todos_update ), gadget.tools_mapping( schema = ragroleplay.schema_todos_delete, handler = ragroleplay.todos_delete ), gadget.tools_mapping( schema = ragroleplay.schema_end_session, handler = ragroleplay.end_session ), ] TOOLS = gadget.tool_schemas (tools_definition) TOOL_HANDLERS = gadget.tool_handlers (tools_definition) def main(): parser = argparse.ArgumentParser(description="Hendrik AI Agent") parser.add_argument('--char', type=str, default=None, help='Character name (overrides config.yaml)') parser.add_argument('-w', '--workspace', type=str, default=None, help='Working directory') args = parser.parse_args() if args.char: config.load_character(char_override=args.char) if args.workspace: resolved = os.path.abspath(args.workspace) if not os.path.isdir(resolved): print(f"Error: '{resolved}' is not a valid directory", flush=True) sys.exit(1) os.chdir(resolved) llm_client = LLMClient(config.llm_baseurl, config.llm_model, config.llm_api_key, config.llm_timeout) ragroleplay_lib.init_db(config.ragroleplay_db_path, config.ragroleplay_vector_size) services = [] if config.XMPP_ENABLED: muc_rooms = [] if config.XMPP_MUC_ROOMS.strip(): muc_rooms = [r.strip() for r in config.XMPP_MUC_ROOMS.split(',') if r.strip()] client = XMPPClient( jid = config.XMPP_USERNAME, password = config.XMPP_PASSWORD, llm_client = llm_client, tools_definition = tools_definition, TOOLS = TOOLS, TOOL_HANDLERS = TOOL_HANDLERS, build_system_prompt = personality.build_system_prompt, agent_max_iterations = config.AGENT_MAX_ITERATIONS, muc_rooms = muc_rooms, ) services.append(client) if config.TELEGRAM_ENABLED: allowed_ids = [] if config.TELEGRAM_ALLOWED_GROUP_IDS.strip(): allowed_ids = [r.strip() for r in config.TELEGRAM_ALLOWED_GROUP_IDS.split(',') if r.strip()] tg = TelegramClient( token = config.TELEGRAM_TOKEN, llm_client = llm_client, tools_definition = tools_definition, TOOLS = TOOLS, TOOL_HANDLERS = TOOL_HANDLERS, build_system_prompt = personality.build_system_prompt, agent_max_iterations = config.AGENT_MAX_ITERATIONS, allowed_group_ids = allowed_ids, ) services.append(tg) if services: threads = [] for svc in services: t = threading.Thread(target=svc.start, daemon=True) t.start() threads.append(t) _shutdown = False def _handle_sig(signum, frame): nonlocal _shutdown print("\nShutting down...", flush=True) for svc in services: svc.stop() _shutdown = True signal.signal(signal.SIGTERM, _handle_sig) # Handling interupt signal.signal(signal.SIGINT, _handle_sig) # Handling terminate try: while not _shutdown: time.sleep(1) except KeyboardInterrupt: _shutdown = True print("Exiting.", flush=True) else: HendrikTUI( llm_client = llm_client, tools_definition = tools_definition, TOOLS = TOOLS, TOOL_HANDLERS = TOOL_HANDLERS, build_system_prompt = personality.build_system_prompt, agent_max_iterations = config.AGENT_MAX_ITERATIONS, ).run() if __name__ == "__main__": main()