hendrik/hendrik.py

176 lines
8.5 KiB
Python
Raw Normal View History

import os, sys
import argparse
import threading, time, signal
2026-06-16 22:44:10 +07:00
2026-05-02 21:08:42 +07:00
import config
2026-07-24 19:02:52 +07:00
from tools import coder, carrack, ragroleplay, vision
2026-06-22 11:37:23 +07:00
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
2026-06-23 11:23:04 +07:00
from interfaces.tui import HendrikTUI
2026-05-12 08:52:57 +07:00
2026-05-02 21:08:42 +07:00
tools_definition = [
2026-07-16 10:46:35 +07:00
# Coder Tools
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 ),
2026-07-16 10:46:35 +07:00
2026-07-18 01:52:38 +07:00
gadget.tools_mapping( schema = coder.schema_workspace_change, handler = coder.workspace_change ),
2026-07-16 10:46:35 +07:00
# Carrack Tools
gadget.tools_mapping( schema = carrack.schema_sendhttprequest, handler = carrack.sendhttprequest ),
2026-07-24 19:02:52 +07:00
# Vision Tools
gadget.tools_mapping( schema = vision.schema_describe_image, handler = vision.describe_image ),
2026-07-07 07:08:30 +07:00
# Rag Roleplay Tools
gadget.tools_mapping( schema = ragroleplay.schema_users_store, handler = ragroleplay.users_store ),
gadget.tools_mapping( schema = ragroleplay.schema_user_load, handler = ragroleplay.user_load ), # s_load
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_summarize, handler = ragroleplay.memories_summarize ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_store, handler = ragroleplay.memories_store ),
gadget.tools_mapping( schema = ragroleplay.schema_memories_latest, handler = ragroleplay.memories_latest ), # _load_latest
gadget.tools_mapping( schema = ragroleplay.schema_memories_filter, handler = ragroleplay.memories_filter ), # _load
gadget.tools_mapping( schema = ragroleplay.schema_memories_check, handler = ragroleplay.memories_check ), # ?
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 ),
2026-06-10 15:12:50 +07:00
2026-05-02 21:08:42 +07:00
]
TOOLS = gadget.tool_schemas (tools_definition)
TOOL_HANDLERS = gadget.tool_handlers (tools_definition)
2026-05-02 21:08:42 +07:00
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()
2026-06-08 02:41:29 +07:00
2026-07-12 14:51:58 +07:00
if args.char:
config.load_character(char_override=args.char)
if args.workspace:
resolved = os.path.abspath(args.workspace)
2026-05-08 18:05:53 +07:00
if not os.path.isdir(resolved):
2026-06-18 08:12:59 +07:00
print(f"Error: '{resolved}' is not a valid directory", flush=True)
2026-05-08 18:05:53 +07:00
sys.exit(1)
2026-07-18 01:52:38 +07:00
coder.set_current_workspace(resolved)
2026-05-08 18:05:53 +07:00
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)
2026-06-16 22:44:10 +07:00
services = []
2026-06-08 02:41:29 +07:00
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(
2026-06-10 15:12:50 +07:00
jid = config.XMPP_USERNAME,
password = config.XMPP_PASSWORD,
llm_client = llm_client,
tools_definition = tools_definition,
TOOLS = TOOLS,
TOOL_HANDLERS = TOOL_HANDLERS,
2026-06-23 11:23:04 +07:00
build_system_prompt = personality.build_system_prompt,
2026-06-10 15:12:50 +07:00
agent_max_iterations = config.AGENT_MAX_ITERATIONS,
muc_rooms = muc_rooms,
2026-06-08 02:41:29 +07:00
)
2026-06-16 22:44:10 +07:00
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,
2026-06-23 11:23:04 +07:00
build_system_prompt = personality.build_system_prompt,
2026-06-16 22:44:10 +07:00
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
2026-06-18 08:12:59 +07:00
print("\nShutting down...", flush=True)
2026-06-16 22:44:10 +07:00
for svc in services:
svc.stop()
_shutdown = True
2026-06-22 11:37:23 +07:00
signal.signal(signal.SIGTERM, _handle_sig) # Handling interupt
signal.signal(signal.SIGINT, _handle_sig) # Handling terminate
2026-06-16 22:44:10 +07:00
try:
while not _shutdown:
time.sleep(1)
except KeyboardInterrupt:
_shutdown = True
2026-06-18 08:12:59 +07:00
print("Exiting.", flush=True)
2026-06-22 11:37:23 +07:00
2026-06-08 02:41:29 +07:00
else:
HendrikTUI(
llm_client = llm_client,
tools_definition = tools_definition,
TOOLS = TOOLS,
TOOL_HANDLERS = TOOL_HANDLERS,
2026-06-23 11:23:04 +07:00
build_system_prompt = personality.build_system_prompt,
2026-06-08 02:41:29 +07:00
agent_max_iterations = config.AGENT_MAX_ITERATIONS,
).run()
2026-06-16 22:44:10 +07:00
2026-05-02 21:08:42 +07:00
if __name__ == "__main__":
main()