agent.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import time
  2. from typing import TypedDict
  3. from opendevin.controller.agent import Agent
  4. from opendevin.controller.state.state import State
  5. from opendevin.events.action import (
  6. Action,
  7. AddTaskAction,
  8. AgentFinishAction,
  9. AgentRejectAction,
  10. BrowseInteractiveAction,
  11. BrowseURLAction,
  12. CmdRunAction,
  13. FileReadAction,
  14. FileWriteAction,
  15. MessageAction,
  16. ModifyTaskAction,
  17. )
  18. from opendevin.events.observation import (
  19. CmdOutputObservation,
  20. FileReadObservation,
  21. FileWriteObservation,
  22. NullObservation,
  23. Observation,
  24. )
  25. from opendevin.events.serialization.event import event_to_dict
  26. from opendevin.llm.llm import LLM
  27. """
  28. FIXME: There are a few problems this surfaced
  29. * FileWrites seem to add an unintended newline at the end of the file
  30. * Browser not working
  31. """
  32. ActionObs = TypedDict(
  33. 'ActionObs', {'action': Action, 'observations': list[Observation]}
  34. )
  35. class DummyAgent(Agent):
  36. VERSION = '1.0'
  37. """
  38. The DummyAgent is used for e2e testing. It just sends the same set of actions deterministically,
  39. without making any LLM calls.
  40. """
  41. def __init__(self, llm: LLM):
  42. super().__init__(llm)
  43. self.steps: list[ActionObs] = [
  44. {
  45. 'action': AddTaskAction(parent='0', goal='check the current directory'),
  46. 'observations': [NullObservation('')],
  47. },
  48. {
  49. 'action': AddTaskAction(parent='0.0', goal='run ls'),
  50. 'observations': [NullObservation('')],
  51. },
  52. {
  53. 'action': ModifyTaskAction(task_id='0.0', state='in_progress'),
  54. 'observations': [NullObservation('')],
  55. },
  56. {
  57. 'action': MessageAction('Time to get started!'),
  58. 'observations': [NullObservation('')],
  59. },
  60. {
  61. 'action': CmdRunAction(command='echo "foo"'),
  62. 'observations': [
  63. CmdOutputObservation('foo', command_id=-1, command='echo "foo"')
  64. ],
  65. },
  66. {
  67. 'action': FileWriteAction(
  68. content='echo "Hello, World!"', path='hello.sh'
  69. ),
  70. 'observations': [FileWriteObservation('', path='hello.sh')],
  71. },
  72. {
  73. 'action': FileReadAction(path='hello.sh'),
  74. 'observations': [
  75. FileReadObservation('echo "Hello, World!"\n', path='hello.sh')
  76. ],
  77. },
  78. {
  79. 'action': CmdRunAction(command='bash hello.sh'),
  80. 'observations': [
  81. CmdOutputObservation(
  82. 'Hello, World!', command_id=-1, command='bash hello.sh'
  83. )
  84. ],
  85. },
  86. {
  87. 'action': BrowseURLAction(url='https://google.com'),
  88. 'observations': [
  89. # BrowserOutputObservation('<html></html>', url='https://google.com', screenshot=""),
  90. ],
  91. },
  92. {
  93. 'action': BrowseInteractiveAction(
  94. browser_actions='goto("https://google.com")'
  95. ),
  96. 'observations': [
  97. # BrowserOutputObservation('<html></html>', url='https://google.com', screenshot=""),
  98. ],
  99. },
  100. {
  101. 'action': AgentFinishAction(),
  102. 'observations': [],
  103. },
  104. {
  105. 'action': AgentRejectAction(),
  106. 'observations': [],
  107. },
  108. ]
  109. def step(self, state: State) -> Action:
  110. time.sleep(0.1)
  111. if state.iteration > 0:
  112. prev_step = self.steps[state.iteration - 1]
  113. # a step is (action, observations list)
  114. if 'observations' in prev_step:
  115. # one obs, at most
  116. expected_observations = prev_step['observations']
  117. # check if the history matches the expected observations
  118. hist_events = state.history.get_last_events(len(expected_observations))
  119. for i in range(len(expected_observations)):
  120. hist_obs = event_to_dict(hist_events[i])
  121. expected_obs = event_to_dict(expected_observations[i])
  122. if (
  123. 'command_id' in hist_obs['extras']
  124. and hist_obs['extras']['command_id'] != -1
  125. ):
  126. del hist_obs['extras']['command_id']
  127. hist_obs['content'] = ''
  128. if (
  129. 'command_id' in expected_obs['extras']
  130. and expected_obs['extras']['command_id'] != -1
  131. ):
  132. del expected_obs['extras']['command_id']
  133. expected_obs['content'] = ''
  134. assert (
  135. hist_obs == expected_obs
  136. ), f'Expected observation {expected_obs}, got {hist_obs}'
  137. return self.steps[state.iteration]['action']