state.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import base64
  2. import pickle
  3. from dataclasses import dataclass, field
  4. from opendevin.controller.state.task import RootTask
  5. from opendevin.core.logger import opendevin_logger as logger
  6. from opendevin.core.metrics import Metrics
  7. from opendevin.core.schema import AgentState
  8. from opendevin.events.action import (
  9. Action,
  10. MessageAction,
  11. )
  12. from opendevin.events.observation import (
  13. CmdOutputObservation,
  14. Observation,
  15. )
  16. from opendevin.storage import get_file_store
  17. RESUMABLE_STATES = [
  18. AgentState.RUNNING,
  19. AgentState.PAUSED,
  20. AgentState.AWAITING_USER_INPUT,
  21. AgentState.FINISHED,
  22. ]
  23. @dataclass
  24. class State:
  25. root_task: RootTask = field(default_factory=RootTask)
  26. iteration: int = 0
  27. max_iterations: int = 100
  28. background_commands_obs: list[CmdOutputObservation] = field(default_factory=list)
  29. history: list[tuple[Action, Observation]] = field(default_factory=list)
  30. updated_info: list[tuple[Action, Observation]] = field(default_factory=list)
  31. inputs: dict = field(default_factory=dict)
  32. outputs: dict = field(default_factory=dict)
  33. error: str | None = None
  34. agent_state: AgentState = AgentState.LOADING
  35. resume_state: AgentState | None = None
  36. metrics: Metrics = Metrics()
  37. # root agent has level 0, and every delegate increases the level by one
  38. delegate_level: int = 0
  39. def save_to_session(self, sid: str):
  40. fs = get_file_store()
  41. pickled = pickle.dumps(self)
  42. encoded = base64.b64encode(pickled).decode('utf-8')
  43. try:
  44. fs.write(f'sessions/{sid}/agent_state.pkl', encoded)
  45. except Exception as e:
  46. logger.error(f'Failed to save state to session: {e}')
  47. raise e
  48. @staticmethod
  49. def restore_from_session(sid: str) -> 'State':
  50. fs = get_file_store()
  51. try:
  52. encoded = fs.read(f'sessions/{sid}/agent_state.pkl')
  53. pickled = base64.b64decode(encoded)
  54. state = pickle.loads(pickled)
  55. except Exception as e:
  56. logger.error(f'Failed to restore state from session: {e}')
  57. raise e
  58. if state.agent_state in RESUMABLE_STATES:
  59. state.resume_state = state.agent_state
  60. else:
  61. state.resume_state = None
  62. state.agent_state = AgentState.LOADING
  63. return state
  64. def get_current_user_intent(self):
  65. # TODO: this is used to understand the user's main goal, but it's possible
  66. # the latest message is an interruption. We should look for a space where
  67. # the agent goes to FINISHED, and then look for the next user message.
  68. for action, obs in reversed(self.history):
  69. if isinstance(action, MessageAction) and action.source == 'user':
  70. return action.content