# agent.py — Agent loop dan tool execution. # submit() adalah entry point: membaca input buffer, mengirim ke LLM, # memproses tool calls, dan menampilkan hasil di log. import json from datetime import datetime from .render import draw from scripts import ntro def log(app, role, text): # Simpan item ke app.log untuk di-render oleh render.py app.log.append({ "role": role, "text": text, "time": datetime.now().strftime("%H:%M"), }) def submit(app, stdscr): # Kirim query dari input buffer ke LLM. # Loop sampai LLM mengembalikan final answer (tanpa tool_calls) # atau mencapai max_iterations. query = "\n".join(app.input_buffer).strip() if not query: return log(app, "user", query) # Reset input buffer app.input_buffer = [""] app.input_line = 0 app.input_col = 0 app.scroll = 999999 # scroll ke paling bawah app.processing = True draw(app, stdscr) stdscr.refresh() app.messages.append({"role": "user", "content": query}) stamp = ntro.start() for step in range(app.agent_max_iterations): stamp_step = ntro.start() log(app, "system", f" step {step + 1} \u2014 Thinking...") app.scroll = 999999 draw(app, stdscr) stdscr.refresh() response = app.llm.chat(app.messages, tools=app.TOOLS) # Hapus "step N — LLM..." log, ganti dengan hasil aktual app.log.pop() if response.tool_calls: # LLM meminta menjalankan tool(s) amsg = { "role": "assistant", "content": response.content, "tool_calls": response.tool_calls, } app.messages.append(amsg) if response.content and response.content.strip(): log(app, "ai", response.content) app.scroll = 999999 draw(app, stdscr) stdscr.refresh() for tc in response.tool_calls: tname = tc["function"]["name"] log(app, "system", f" \u2192 {tname}") app.scroll = 999999 draw(app, stdscr) stdscr.refresh() execute_tool(app, tc) else: # Final answer — tidak ada tool_calls if response.content: app.messages.append({ "role": "assistant", "content": response.content, }) log(app, "ai", response.content) log(app, "sep", "") app.processing = False app.scroll = 999999 draw(app, stdscr) stdscr.refresh() ntro.end(stamp) return ntro.end(stamp_step) # Timeout — max iterations tercapai tanpa final answer log(app, "error", "Max iterations reached without final answer.") app.messages.append({"role": "assistant", "content": "Max iterations reached without final answer."}) app.processing = False ntro.end(stamp) def execute_tool(app, tool_call): # Dispatch tool_call ke handler yang terdaftar di TOOL_HANDLERS. # search_code dan git_operation butuh penanganan argumen khusus. 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)}" # Hasil tool disimpan ke messages agar bisa dikirim balik ke LLM app.messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": str(result), })