Set granite4.1 as default model.

This commit is contained in:
Dita Aji Pratama 2026-05-08 18:05:53 +07:00
parent 6db78809f4
commit a83198303f
4 changed files with 164 additions and 37 deletions

View File

@ -5,7 +5,7 @@ load_dotenv()
# LLM Configuration # LLM Configuration
LLM_BASE_URL = os.getenv("LLM_BASE_URL", default="http://localhost:11434/v1") LLM_BASE_URL = os.getenv("LLM_BASE_URL", default="http://localhost:11434/v1")
LLM_MODEL = os.getenv("LLM_MODEL", default="deepseek-r1:8b") LLM_MODEL = os.getenv("LLM_MODEL", default="granite4.1:8b")
LLM_API_KEY = os.getenv("LLM_API_KEY", default="ollama") LLM_API_KEY = os.getenv("LLM_API_KEY", default="ollama")
LLM_TIMEOUT = int(os.getenv("LLM_TIMEOUT", default="600")) LLM_TIMEOUT = int(os.getenv("LLM_TIMEOUT", default="600"))
# Agent Configuration # Agent Configuration

View File

@ -1,4 +1,4 @@
import os, sys, json import os, sys, json, tty, termios
import config import config
from llm_client import LLMClient from llm_client import LLMClient
from tools import coder from tools import coder
@ -13,25 +13,79 @@ tools_definition = [
gadget.tools_mapping( coder.schema_git_operation, coder.git_operation ), gadget.tools_mapping( coder.schema_git_operation, coder.git_operation ),
] ]
TOOLS = [t["schema"] for t in tools_definition] # Schemas TOOLS = gadget.tool_schemas(tools_definition)
TOOL_HANDLERS = {t["name"]: t["handler"] for t in tools_definition} # Map TOOL_HANDLERS = gadget.tool_handlers(tools_definition)
SYSTEM_PROMPT = """You are a coding agent that assists with software engineering tasks. You have access to the following tools:
1. read_file: Read file contents with line numbers def interactive_input():
2. write_file: Write content to a file (overwrites existing) fd = sys.stdin.fileno()
3. edit_file: Replace text in a file old = termios.tcgetattr(fd)
4. run_bash: Execute bash commands
5. search_code: Search for files (glob) or file contents (regex)
6. git_operation: Run git commands
Use tools by returning tool calls when needed. After receiving tool results, continue your reasoning. When you have the final answer, return it as plain text without tool calls.""" print()
print("\u2500" * 50)
print("Hendrik AI Agent - Interactive Mode")
print("\u2500" * 50)
print(f"Workspace: {os.getcwd()}")
print("\u2500" * 50)
print("[Ctrl+W] Change workspace | :workspace <dir> | [Ctrl+D] Submit")
print("\u2500" * 50)
def agent_loop(user_query, llm_client): buffer = bytearray()
messages = [ try:
{"role": "system" , "content": SYSTEM_PROMPT }, tty.setraw(fd)
{"role": "user" , "content": user_query } while True:
] ch = os.read(fd, 1)
if ch == b'\x03': # Ctrl+C → exit
termios.tcsetattr(fd, termios.TCSADRAIN, old)
print("\r\nExiting.")
sys.exit(0)
elif ch == b'\x04': # Ctrl+D → submit
break
elif ch == b'\x17': # Ctrl+W → change workspace
termios.tcsetattr(fd, termios.TCSADRAIN, old)
print("\r\n", end="")
ws = input("Workspace directory: ").strip()
if ws:
resolved = os.path.abspath(ws)
if not os.path.isdir(resolved):
print(f"Error: '{resolved}' is not a valid directory")
else:
os.chdir(resolved)
print(f"\u2192 Workspace changed to {os.getcwd()}")
return interactive_input()
elif ch in (b'\r', b'\n'): # Enter
buffer.extend(b'\n')
sys.stdout.buffer.write(b'\r\n')
sys.stdout.flush()
elif ch == b'\x7f': # Backspace
if buffer:
buffer.pop()
sys.stdout.buffer.write(b'\b \b')
sys.stdout.flush()
elif ch >= b' ': # Printable characters
buffer.extend(ch)
sys.stdout.buffer.write(ch)
sys.stdout.flush()
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
full_query = buffer.decode('utf-8', errors='replace').strip()
if full_query.startswith(':workspace '):
ws = full_query[11:].strip()
resolved = os.path.abspath(ws)
if not os.path.isdir(resolved):
print(f"Error: '{resolved}' is not a valid directory")
return interactive_input()
os.chdir(resolved)
print(f"\u2192 Workspace changed to {os.getcwd()}")
return interactive_input()
return full_query
def agent_loop(user_query, messages, llm_client):
messages.append({"role": "user", "content": user_query})
for _ in range(config.AGENT_MAX_ITERATIONS): for _ in range(config.AGENT_MAX_ITERATIONS):
response = llm_client.chat(messages, tools=TOOLS) response = llm_client.chat(messages, tools=TOOLS)
if response.tool_calls: if response.tool_calls:
@ -48,6 +102,8 @@ def agent_loop(user_query, llm_client):
if not handler: if not handler:
result = f"Tool {tool_name} not found" result = f"Tool {tool_name} not found"
else: else:
args_display = ", ".join(f"{k}={v!r}" for k, v in tool_args.items())
print(f" \u2192 {tool_name}({args_display})")
try: try:
if tool_name == "search_code": if tool_name == "search_code":
result = handler( result = handler(
@ -67,23 +123,67 @@ def agent_loop(user_query, llm_client):
"content": str(result) "content": str(result)
}) })
else: else:
return response.content messages.append({"role": "assistant", "content": response.content})
return "Max iterations reached without final answer." return response.content, messages
msg = "Max iterations reached without final answer."
messages.append({"role": "assistant", "content": msg})
return msg, messages
def main(): def main():
llm_client = LLMClient(base_url=config.LLM_BASE_URL, model=config.LLM_MODEL, api_key=config.LLM_API_KEY) workspace = None
if len(sys.argv) > 1: query_parts = []
user_query = " ".join(sys.argv[1:]) i = 1
while i < len(sys.argv):
if sys.argv[i] in ('-w', '--workspace') and i + 1 < len(sys.argv):
workspace = sys.argv[i + 1]
i += 2
else: else:
print("Enter your query (Ctrl+D to submit):") query_parts.append(sys.argv[i])
user_query = sys.stdin.read().strip() i += 1
if workspace:
resolved = os.path.abspath(workspace)
if not os.path.isdir(resolved):
print(f"Error: '{resolved}' is not a valid directory")
sys.exit(1)
os.chdir(resolved)
llm_client = LLMClient(
base_url=config.LLM_BASE_URL,
model=config.LLM_MODEL,
api_key=config.LLM_API_KEY
)
messages = None
if query_parts:
user_query = ' '.join(query_parts)
if not user_query: if not user_query:
print("No query provided.") print("No query provided.")
return return
messages = [{"role": "system", "content": gadget.build_system_prompt(tools_definition)}]
print("Thinking...") print("Thinking...")
final_answer = agent_loop(user_query, llm_client) final_answer, messages = agent_loop(user_query, messages, llm_client)
print("\nFinal Answer:")
print(final_answer)
return
while True:
user_query = interactive_input()
if not user_query:
break
if user_query.lower() in ('/exit', '/quit'):
break
if messages is None:
messages = [{"role": "system", "content": gadget.build_system_prompt(tools_definition)}]
print("Thinking...")
final_answer, messages = agent_loop(user_query, messages, llm_client)
print("\nFinal Answer:") print("\nFinal Answer:")
print(final_answer) print(final_answer)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,6 +0,0 @@
Buatkan aku permainan menebak angka dadu dengan python dengan nama `dice.py`.
Pemain dapat melihat angka dadu di awal permainan.
Pemain akan menebak apakah angka selanjutnya lebih rendah atau lebih tinggi.
Jika tebakan pemain benar maka score bertambah 10. Jika salah maka score di reset jadi 0.
Tampilkan info `Score` dan `Dice`. Input hanya ada `low`, `high`, dan `quit`.
Permainan akan terus berlanjut sampai pemain keluar dari permainan.

View File

@ -1,4 +1,37 @@
import os
def tools_mapping(schema, handler, name=None): def tools_mapping(schema, handler, name=None):
tool_name = name or schema["function"]["name"] tool_name = name or schema["function"]["name"]
return {"name": tool_name, "schema": schema, "handler": handler} return {"name": tool_name, "schema": schema, "handler": handler}
def tool_schemas(tools_definition):
return [t["schema"] for t in tools_definition]
def tool_handlers(tools_definition):
return {t["name"]: t["handler"] for t in tools_definition}
def build_system_prompt(tools_definition):
lines = [
"You are a coding agent that assists with software engineering tasks. "
"You have access to the following tools:",
""
]
for i, tool in enumerate(tools_definition, 1):
name = tool["name"]
desc = tool["schema"]["function"]["description"]
lines.append(f"{i}. {name}: {desc}")
lines.extend([
"",
"Use tools by returning tool calls when needed. After receiving tool "
"results, continue your reasoning. When you have the final answer, "
"return it as plain text without tool calls.",
"",
f"Your workspace directory is: {os.getcwd()}. "
"All file operations are relative to this directory."
])
return "\n".join(lines)