Compare commits
No commits in common. "28bd27f79c3aa7af6bd1f76b07883b33ab0fd418" and "6725b7bf0cb2aad7d95eff255f2aa998453f8d01" have entirely different histories.
28bd27f79c
...
6725b7bf0c
@ -2,7 +2,7 @@ skill : programmer
|
||||
name : Hendrik
|
||||
age : 35
|
||||
gender : male
|
||||
tone : formal
|
||||
tone : casual
|
||||
verbosity : concise
|
||||
humor : none
|
||||
language : id
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
agent:
|
||||
character: hendrik # Directory name in agent/characters/<character>/
|
||||
max_iterations: 30 # step
|
||||
max_tool_output: 4000
|
||||
max_iterations: 40 # step
|
||||
max_tool_output: 40000
|
||||
|
||||
llm:
|
||||
timeout: 3000 # second
|
||||
|
||||
26
hendrik.py
26
hendrik.py
@ -1,17 +1,14 @@
|
||||
import os, sys, threading, time, signal
|
||||
import os, sys, threading, time
|
||||
import signal
|
||||
|
||||
import config
|
||||
|
||||
from services.xmpp_client import XMPPClient
|
||||
|
||||
from scripts.llm_client import LLMClient
|
||||
from tools import coder, rag, carrack
|
||||
|
||||
from services.xmpp_client import XMPPClient
|
||||
from services.telegram_client import TelegramClient
|
||||
|
||||
from services.llm_client import LLMClient
|
||||
from scripts.personality import build_system_prompt
|
||||
from scripts import gadget
|
||||
|
||||
from tui import HendrikTUI
|
||||
from scripts import gadget
|
||||
from scripts.personality import build_system_prompt
|
||||
|
||||
tools_definition = [
|
||||
|
||||
@ -49,6 +46,7 @@ def main():
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if workspace:
|
||||
resolved = os.path.abspath(workspace)
|
||||
if not os.path.isdir(resolved):
|
||||
@ -76,6 +74,8 @@ def main():
|
||||
services.append(client)
|
||||
|
||||
if config.TELEGRAM_ENABLED:
|
||||
from services.telegram_client import TelegramClient
|
||||
|
||||
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()]
|
||||
@ -108,8 +108,8 @@ def main():
|
||||
svc.stop()
|
||||
_shutdown = True
|
||||
|
||||
signal.signal(signal.SIGTERM, _handle_sig) # Handling interupt
|
||||
signal.signal(signal.SIGINT, _handle_sig) # Handling terminate
|
||||
signal.signal(signal.SIGTERM, _handle_sig)
|
||||
signal.signal(signal.SIGINT, _handle_sig)
|
||||
|
||||
try:
|
||||
while not _shutdown:
|
||||
@ -117,8 +117,8 @@ def main():
|
||||
except KeyboardInterrupt:
|
||||
_shutdown = True
|
||||
print("Exiting.", flush=True)
|
||||
|
||||
else:
|
||||
from tui import HendrikTUI
|
||||
HendrikTUI(
|
||||
llm_client = llm_client,
|
||||
tools_definition = tools_definition,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import re
|
||||
from .personality import build_system_prompt
|
||||
|
||||
def tools_mapping(schema, handler, name=None):
|
||||
tool_name = name or schema["function"]["name"]
|
||||
@ -10,25 +10,3 @@ def tool_schemas(tools_definition):
|
||||
def tool_handlers(tools_definition):
|
||||
return {t["name"]: t["handler"] for t in tools_definition}
|
||||
|
||||
def strip_thinking(text: str) -> str:
|
||||
if not text:
|
||||
return text
|
||||
# Strip XML-style thinking blocks (case-insensitive, DOTALL for multiline)
|
||||
text = re.sub(r'<think[^>]*>.*?</think>', '', text, flags=re.DOTALL | re.IGNORECASE)
|
||||
text = re.sub(r'<reasoning[^>]*>.*?</reasoning>', '', text, flags=re.DOTALL | re.IGNORECASE)
|
||||
# Strip lines starting with Thinking: / Reasoning: / Let me think...
|
||||
lines = text.splitlines()
|
||||
cleaned = []
|
||||
skip_block = False
|
||||
for line in lines:
|
||||
stripped = line.strip().lower()
|
||||
if stripped.startswith(('thinking:', 'reasoning:', 'let me thought', 'let me think')):
|
||||
skip_block = True
|
||||
continue
|
||||
if skip_block and not stripped:
|
||||
skip_block = False
|
||||
continue
|
||||
if not skip_block:
|
||||
cleaned.append(line)
|
||||
result = '\n'.join(cleaned).strip()
|
||||
return result
|
||||
|
||||
@ -1,14 +1,49 @@
|
||||
import json
|
||||
from scripts import gadget
|
||||
import re
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
|
||||
def _strip_thinking(text: str) -> str:
|
||||
"""
|
||||
Hapus semua bentuk thinking/reasoning dari response text.
|
||||
Handles:
|
||||
- <think>...</think> blocks (any case)
|
||||
- <reasoning>...</reasoning> blocks
|
||||
- "Thinking:" / "Reasoning:" inline prefixes
|
||||
"""
|
||||
if not text:
|
||||
return text
|
||||
|
||||
# Strip XML-style thinking blocks (case-insensitive, DOTALL for multiline)
|
||||
text = re.sub(r'<think[^>]*>.*?</think>', '', text, flags=re.DOTALL | re.IGNORECASE)
|
||||
text = re.sub(r'<reasoning[^>]*>.*?</reasoning>', '', text, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
# Strip lines starting with Thinking: / Reasoning: / Let me think...
|
||||
lines = text.splitlines()
|
||||
cleaned = []
|
||||
skip_block = False
|
||||
for line in lines:
|
||||
stripped = line.strip().lower()
|
||||
if stripped.startswith(('thinking:', 'reasoning:', 'let me thought', 'let me think')):
|
||||
skip_block = True
|
||||
continue
|
||||
if skip_block and not stripped:
|
||||
skip_block = False
|
||||
continue
|
||||
if not skip_block:
|
||||
cleaned.append(line)
|
||||
|
||||
result = '\n'.join(cleaned).strip()
|
||||
return result
|
||||
|
||||
|
||||
class LLMClient:
|
||||
class Message:
|
||||
def __init__(self, msg):
|
||||
raw_content = msg.get('content', '')
|
||||
# Auto-strip thinking dari content
|
||||
self.content = gadget.strip_thinking(raw_content) if isinstance(raw_content, str) else raw_content
|
||||
self.content = _strip_thinking(raw_content) if isinstance(raw_content, str) else raw_content
|
||||
self.tool_calls = msg.get('tool_calls', None)
|
||||
self.warning = None
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def _ts():
|
||||
return datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
|
||||
def execute_tool(tool_call, TOOL_HANDLERS):
|
||||
tname = tool_call['function']['name']
|
||||
targs = json.loads(tool_call['function']['arguments'])
|
||||
@ -24,6 +26,7 @@ def execute_tool(tool_call, TOOL_HANDLERS):
|
||||
except Exception as e:
|
||||
return f'Error executing tool: {str(e)}'
|
||||
|
||||
|
||||
def run_agent_loop(session, llm_client, TOOLS, TOOL_HANDLERS, max_iterations, on_tool_calls=None):
|
||||
for step in range(max_iterations):
|
||||
print(f'[{_ts()}] Step {step + 1} — calling LLM...', flush=True)
|
||||
@ -63,4 +66,3 @@ def run_agent_loop(session, llm_client, TOOLS, TOOL_HANDLERS, max_iterations, on
|
||||
'content': 'Max iterations reached without final answer.',
|
||||
})
|
||||
return None
|
||||
|
||||
@ -7,7 +7,7 @@ from datetime import datetime
|
||||
|
||||
import config
|
||||
from services.session_manager import SessionManager
|
||||
from scripts.agent_loop import run_agent_loop
|
||||
from services.agent_loop import run_agent_loop
|
||||
from scripts.personality import PERSONALITY
|
||||
from tools.roleplayer import _name_mentioned
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ from datetime import datetime
|
||||
|
||||
from slixmpp import ClientXMPP
|
||||
from services.session_manager import SessionManager
|
||||
from scripts.agent_loop import run_agent_loop
|
||||
from services.agent_loop import run_agent_loop
|
||||
|
||||
import config
|
||||
from tools.roleplayer import should_respond
|
||||
@ -436,7 +436,7 @@ class XMPPClient(ClientXMPP):
|
||||
session.start_timer(300, self._timeout_session, room, 'groupchat')
|
||||
|
||||
def _execute_tool(self, tool_call):
|
||||
from scripts.agent_loop import execute_tool
|
||||
from services.agent_loop import execute_tool
|
||||
return execute_tool(tool_call, self._TOOL_HANDLERS)
|
||||
|
||||
def _schedule_send(self, to, body, mtype='chat'):
|
||||
|
||||
39
tui/agent.py
39
tui/agent.py
@ -2,7 +2,7 @@ import json
|
||||
import threading
|
||||
from datetime import datetime
|
||||
import config
|
||||
from scripts import ntro, agent_loop
|
||||
from scripts import ntro
|
||||
|
||||
|
||||
def _add_msg(app, role, content, **kwargs):
|
||||
@ -15,14 +15,6 @@ def _add_msg(app, role, content, **kwargs):
|
||||
)
|
||||
|
||||
WELCOME_ART = """
|
||||
,--. ,--.,------.,--. ,--.,------. ,------. ,--.,--. ,--.
|
||||
| '--' || .---'| ,'.| || .-. \\ | .--. '| || .' /
|
||||
| .--. || `--, | |' ' || | \\ :| '--'.'| || . '
|
||||
| | | || `---.| | ` || '--' /| |\\ \\ | || |\\ \\
|
||||
`--' `--'`------'`--' `--'`-------' `--' '--'`--'`--' '--'
|
||||
|
||||
"""
|
||||
"""
|
||||
__ __ _______ __ _ ______ ______ ___ ___ _
|
||||
| | | || || | | || | | _ | | | | | | |
|
||||
| |_| || ___|| |_| || _ || | || | | | |_| |
|
||||
@ -43,6 +35,7 @@ WELCOME_ART = """
|
||||
╚══════════════════════════════════════════╝
|
||||
"""
|
||||
|
||||
|
||||
def log(app, role, text):
|
||||
app.log.append({
|
||||
"role": role,
|
||||
@ -151,9 +144,7 @@ def _agent_loop(app):
|
||||
"arguments": targs,
|
||||
}))
|
||||
app.scroll = 999999
|
||||
result = agent_loop.execute_tool(tc, app.TOOL_HANDLERS)
|
||||
_add_msg(app, "tool", str(result), tool_call_id=tc["id"])
|
||||
|
||||
execute_tool(app, tc)
|
||||
|
||||
# Log content AI setelah tools (jika ada)
|
||||
if response.content and response.content.strip():
|
||||
@ -174,6 +165,26 @@ def _agent_loop(app):
|
||||
app.agent_done.set()
|
||||
|
||||
|
||||
app.agent_done.set()
|
||||
ntro.end(stamp)
|
||||
def execute_tool(app, tool_call):
|
||||
tname = tool_call["function"]["name"]
|
||||
targs = json.loads(tool_call["function"]["arguments"])
|
||||
handler = app.TOOL_HANDLERS.get(tname)
|
||||
|
||||
if not handler:
|
||||
result = f"Tool {tname} not found"
|
||||
else:
|
||||
try:
|
||||
if tname == "search_code":
|
||||
result = handler(
|
||||
pattern=targs["pattern"],
|
||||
search_type=targs["search_type"],
|
||||
path=targs.get("path", "."),
|
||||
)
|
||||
elif tname == "git_operation":
|
||||
result = handler(args=targs["args"])
|
||||
else:
|
||||
result = handler(**targs)
|
||||
except Exception as e:
|
||||
result = f"Error executing tool: {str(e)}"
|
||||
|
||||
_add_msg(app, "tool", str(result), tool_call_id=tool_call["id"])
|
||||
|
||||
Loading…
Reference in New Issue
Block a user