main.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import asyncio
  2. import os
  3. import sys
  4. from typing import Callable, Optional, Type
  5. import agenthub # noqa F401 (we import this to get the agents registered)
  6. from opendevin.controller import AgentController
  7. from opendevin.controller.agent import Agent
  8. from opendevin.controller.state.state import State
  9. from opendevin.core.config import args, get_llm_config_arg
  10. from opendevin.core.logger import opendevin_logger as logger
  11. from opendevin.core.schema import AgentState
  12. from opendevin.events import EventSource, EventStream, EventStreamSubscriber
  13. from opendevin.events.action import MessageAction
  14. from opendevin.events.event import Event
  15. from opendevin.events.observation import AgentStateChangedObservation
  16. from opendevin.llm.llm import LLM
  17. from opendevin.runtime.sandbox import Sandbox
  18. from opendevin.runtime.server.runtime import ServerRuntime
  19. def read_task_from_file(file_path: str) -> str:
  20. """Read task from the specified file."""
  21. with open(file_path, 'r', encoding='utf-8') as file:
  22. return file.read()
  23. def read_task_from_stdin() -> str:
  24. """Read task from stdin."""
  25. return sys.stdin.read()
  26. async def main(
  27. task_str: str = '',
  28. exit_on_message: bool = False,
  29. fake_user_response_fn: Optional[Callable[[Optional[State]], str]] = None,
  30. sandbox: Optional[Sandbox] = None,
  31. runtime_tools_config: Optional[dict] = None,
  32. ) -> Optional[State]:
  33. """Main coroutine to run the agent controller with task input flexibility.
  34. It's only used when you launch opendevin backend directly via cmdline.
  35. Args:
  36. task_str: The task to run.
  37. exit_on_message: quit if agent asks for a message from user (optional)
  38. fake_user_response_fn: An optional function that receives the current state (could be None) and returns a fake user response.
  39. sandbox: An optional sandbox to run the agent in.
  40. """
  41. # Determine the task source
  42. if task_str:
  43. task = task_str
  44. elif args.file:
  45. task = read_task_from_file(args.file)
  46. elif args.task:
  47. task = args.task
  48. elif not sys.stdin.isatty():
  49. task = read_task_from_stdin()
  50. else:
  51. raise ValueError('No task provided. Please specify a task through -t, -f.')
  52. # only one of model_name or llm_config is required
  53. if args.llm_config:
  54. # --llm_config
  55. # llm_config can contain any of the attributes of LLMConfig
  56. llm_config = get_llm_config_arg(args.llm_config)
  57. if llm_config is None:
  58. raise ValueError(f'Invalid toml file, cannot read {args.llm_config}')
  59. logger.info(
  60. f'Running agent {args.agent_cls} (model: {llm_config.model}, llm_config: {args.llm_config}) with task: "{task}"'
  61. )
  62. # create LLM instance with the given config
  63. llm = LLM(llm_config=llm_config)
  64. else:
  65. # --model-name model_name
  66. logger.info(
  67. f'Running agent {args.agent_cls} (model: {args.model_name}), with task: "{task}"'
  68. )
  69. llm = LLM(args.model_name)
  70. AgentCls: Type[Agent] = Agent.get_cls(args.agent_cls)
  71. agent = AgentCls(llm=llm)
  72. event_stream = EventStream('main')
  73. controller = AgentController(
  74. agent=agent,
  75. max_iterations=args.max_iterations,
  76. max_budget_per_task=args.max_budget_per_task,
  77. max_chars=args.max_chars,
  78. event_stream=event_stream,
  79. )
  80. runtime = ServerRuntime(event_stream=event_stream, sandbox=sandbox)
  81. runtime.init_sandbox_plugins(controller.agent.sandbox_plugins)
  82. runtime.init_runtime_tools(
  83. controller.agent.runtime_tools,
  84. is_async=False,
  85. runtime_tools_config=runtime_tools_config,
  86. )
  87. # browser eval specific
  88. # TODO: move to a better place
  89. if runtime.browser and runtime.browser.eval_dir:
  90. logger.info(f'Evaluation directory: {runtime.browser.eval_dir}')
  91. with open(
  92. os.path.join(runtime.browser.eval_dir, 'goal.txt'), 'r', encoding='utf-8'
  93. ) as f:
  94. task = f.read()
  95. logger.info(f'Dynamic Eval task: {task}')
  96. await event_stream.add_event(MessageAction(content=task), EventSource.USER)
  97. async def on_event(event: Event):
  98. if isinstance(event, AgentStateChangedObservation):
  99. if event.agent_state == AgentState.AWAITING_USER_INPUT:
  100. if exit_on_message:
  101. message = '/exit'
  102. elif fake_user_response_fn is None:
  103. message = input('Request user input >> ')
  104. else:
  105. message = fake_user_response_fn(controller.get_state())
  106. action = MessageAction(content=message)
  107. await event_stream.add_event(action, EventSource.USER)
  108. event_stream.subscribe(EventStreamSubscriber.MAIN, on_event)
  109. while controller.get_agent_state() not in [
  110. AgentState.FINISHED,
  111. AgentState.REJECTED,
  112. AgentState.ERROR,
  113. AgentState.PAUSED,
  114. AgentState.STOPPED,
  115. ]:
  116. await asyncio.sleep(1) # Give back control for a tick, so the agent can run
  117. await controller.close()
  118. runtime.close()
  119. return controller.get_state()
  120. if __name__ == '__main__':
  121. asyncio.run(main())