OpenHands/agent/lib/agent.py
Robert Brennan 1eade7d188
First pass at a control loop (#35)
* initialize control loop

* add todo

* more todo

* add dockerignore

* add notes to prompt

* encourage llm to finish

* add debug env

* update prompts a bit

* fix task prompts

* add basic regression framework

* add hello-world regression case

* add hello-name test case

* fix workspace ignore

* document regression script

* add python-cli test case

* add default git config

* add help regression test

* add node rewrite test case

* add react-todo test case

* fix dockerfile

* add ability to run background commands

* add client-server test case

* update regression readme

* better support for background commands

* update tests

* fix bug in command removal
2024-03-20 18:44:50 +08:00

86 lines
2.8 KiB
Python

import select
from lib.monologue import Monologue
from lib.memory import LongTermMemory
from lib.event import Event
import lib.llm as llm
MAX_OUTPUT_LENGTH = 5000
MAX_MONOLOGUE_LENGTH = 20000
class Agent:
def __init__(self, task):
self.task = task
self.monologue = Monologue()
self.memory = LongTermMemory()
self.background_commands = []
def add_event(self, event):
self.monologue.add_event(event)
self.memory.add_event(event)
if self.monologue.get_total_length() > MAX_MONOLOGUE_LENGTH:
self.monologue.condense()
def get_next_action(self):
bg_commands = [cmd.args for cmd in self.background_commands]
action_dict = llm.request_action(self.task, self.monologue.get_thoughts(), bg_commands)
event = Event(action_dict['action'], action_dict['args'])
self.latest_action = event
self.add_event(event)
return event
def maybe_perform_latest_action(self):
if not (self.latest_action and self.latest_action.is_runnable()):
return
action = 'output'
try:
output = self.latest_action.run(self)
except Exception as e:
output = 'Error: ' + str(e)
action = 'error'
if len(output) > MAX_OUTPUT_LENGTH:
output = output[:MAX_OUTPUT_LENGTH] + '...'
out_event = Event(action, {'output': output})
self.add_event(out_event)
return out_event
def get_background_log(self, idx, cmd, stream, name):
logs = ""
while True:
readable, _, _ = select.select([stream], [], [], .1)
if not readable:
break
next = stream.readline()
if next == '':
break
logs += next
if logs == "": return
event = Event('output', {
'output': logs,
'stream':name,
'id': idx,
'command': cmd.args,
})
self.add_event(event)
return event
def get_background_logs(self):
all_events = []
for idx, cmd in enumerate(self.background_commands):
stdout_event = self.get_background_log(idx, cmd, cmd.stdout, 'stdout')
if stdout_event:
all_events.append(stdout_event)
stderr_event = self.get_background_log(idx, cmd, cmd.stderr, 'stderr')
if stderr_event:
all_events.append(stderr_event)
exit_code = cmd.poll()
if exit_code is not None:
event = Event('output', {'output': 'Background command %d exited with code %d' % (idx, exit_code)})
all_events.append(event)
self.add_event(event)
self.background_commands = [cmd for cmd in self.background_commands if cmd.poll() is None]
return all_events