Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add lint frontend and lint all to Makefile. #1354

Merged
merged 4 commits into from May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion Makefile
Expand Up @@ -148,10 +148,18 @@ install-precommit-hooks:
@poetry run pre-commit install --config $(PRECOMMIT_CONFIG_PATH)
@echo "$(GREEN)Pre-commit hooks installed successfully.$(RESET)"

lint:
lint-backend:
@echo "$(YELLOW)Running linters...$(RESET)"
@poetry run pre-commit run --files $$(git diff --name-only $$(git merge-base main $$(git branch --show-current)) $$(git branch --show-current) | tr '\n' ' ') --show-diff-on-failure --config $(PRECOMMIT_CONFIG_PATH)

lint-frontend:
@echo "$(YELLOW)Running linters for frontend...$(RESET)"
@cd frontend && npm run lint

lint:
@$(MAKE) -s lint-frontend
@$(MAKE) -s lint-backend

build-frontend:
@echo "$(YELLOW)Building frontend...$(RESET)"
@cd frontend && npm run build
Expand Down
20 changes: 20 additions & 0 deletions docs/modules/python/opendevin/controller/agent_controller.md
Expand Up @@ -9,6 +9,26 @@ title: opendevin.controller.agent_controller
class AgentController()
```

#### \_\_init\_\_

```python
def __init__(agent: Agent,
sid: str = 'default',
max_iterations: int = MAX_ITERATIONS,
max_chars: int = MAX_CHARS,
callbacks: List[Callable] = [])
```

Initializes a new instance of the AgentController class.

**Arguments**:

- `agent` - The agent instance to control.
- `sid` - The session ID of the agent.
- `max_iterations` - The maximum number of iterations the agent can run.
- `max_chars` - The maximum number of characters the agent can output.
- `callbacks` - A list of callback functions to run after each action.

#### setup\_task

```python
Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Expand Up @@ -43,7 +43,7 @@
"preview": "vite preview",
"make-i18n": "node scripts/make-i18n-translations.cjs",
"prelint": "npm run make-i18n",
"lint": "eslint src/**/*.ts* && prettier --check src/**/*.ts*",
"lint": "eslint src --ext .ts,.tsx,.js && prettier --check src/**/*.{ts,tsx}",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat unrelated but do we want a lint-fix as well?

"prepare": "cd .. && husky install frontend/.husky"
},
"husky": {
Expand All @@ -52,7 +52,7 @@
}
},
"lint-staged": {
"src/**/*.ts*": [
"src/**/*.{ts,tsx,js}": [
"eslint --fix",
"prettier --write"
]
Expand Down
18 changes: 6 additions & 12 deletions opendevin/controller/action_manager.py
Expand Up @@ -20,28 +20,22 @@ class ActionManager:
sandbox: Sandbox

def __init__(
self,
sid: str,
self,
sid: str = 'default',
):
sandbox_type = config.get(ConfigType.SANDBOX_TYPE).lower()
if sandbox_type == 'exec':
self.sandbox = DockerExecBox(
sid=(sid or 'default'),
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
sid=(sid or 'default'), timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
elif sandbox_type == 'local':
self.sandbox = LocalBox(
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
self.sandbox = LocalBox(timeout=config.get(ConfigType.SANDBOX_TIMEOUT))
elif sandbox_type == 'ssh':
self.sandbox = DockerSSHBox(
sid=(sid or 'default'),
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
sid=(sid or 'default'), timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
elif sandbox_type == 'e2b':
self.sandbox = E2BBox(
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
self.sandbox = E2BBox(timeout=config.get(ConfigType.SANDBOX_TIMEOUT))
else:
raise ValueError(f'Invalid sandbox type: {sandbox_type}')

Expand Down
62 changes: 47 additions & 15 deletions opendevin/controller/agent_controller.py
Expand Up @@ -53,12 +53,20 @@ class AgentController:
def __init__(
self,
agent: Agent,
inputs: dict = {},
sid: str = 'default',
max_iterations: int = MAX_ITERATIONS,
max_chars: int = MAX_CHARS,
callbacks: List[Callable] = [],
):
"""Initializes a new instance of the AgentController class.

Args:
agent: The agent instance to control.
sid: The session ID of the agent.
max_iterations: The maximum number of iterations the agent can run.
max_chars: The maximum number of characters the agent can output.
callbacks: A list of callback functions to run after each action.
"""
self.id = sid
self.agent = agent
self.max_iterations = max_iterations
Expand All @@ -68,8 +76,12 @@ def __init__(
# Initialize agent-required plugins for sandbox (if any)
self.action_manager.init_sandbox_plugins(agent.sandbox_plugins)

if isinstance(agent, CodeActAgent) and not isinstance(self.action_manager.sandbox, DockerSSHBox):
logger.warning('CodeActAgent requires DockerSSHBox as sandbox! Using other sandbox that are not stateful (LocalBox, DockerExecBox) will not work properly.')
if isinstance(agent, CodeActAgent) and not isinstance(
self.action_manager.sandbox, DockerSSHBox
):
logger.warning(
'CodeActAgent requires DockerSSHBox as sandbox! Using other sandbox that are not stateful (LocalBox, DockerExecBox) will not work properly.'
)

self._await_user_message_queue: asyncio.Queue = asyncio.Queue()

Expand Down Expand Up @@ -114,7 +126,10 @@ async def _run(self):
except Exception:
logger.error('Error in loop', exc_info=True)
await self._run_callbacks(
AgentErrorObservation('Oops! Something went wrong while completing your task. You can check the logs for more info.'))
AgentErrorObservation(
'Oops! Something went wrong while completing your task. You can check the logs for more info.'
)
)
await self.set_task_state_to(TaskState.STOPPED)
break

Expand All @@ -134,14 +149,15 @@ async def _run(self):

if self._is_stuck():
logger.info('Loop detected, stopping task')
observation = AgentErrorObservation('I got stuck into a loop, the task has stopped.')
observation = AgentErrorObservation(
'I got stuck into a loop, the task has stopped.'
)
await self._run_callbacks(observation)
await self.set_task_state_to(TaskState.STOPPED)
break

async def setup_task(self, task: str, inputs: dict = {}):
"""Sets up the agent controller with a task.
"""
"""Sets up the agent controller with a task."""
self._task_state = TaskState.RUNNING
await self.notify_task_state_changed()
self.state = State(Plan(task))
Expand Down Expand Up @@ -198,14 +214,19 @@ async def add_user_message(self, message: UserMessageObservation):
self.add_history(NullAction(), message)

else:
raise ValueError(f'Task (state: {self._task_state}) is not in a state to add user message')
raise ValueError(
f'Task (state: {self._task_state}) is not in a state to add user message'
)

async def wait_for_user_input(self) -> UserMessageObservation:
self._task_state = TaskState.AWAITING_USER_INPUT
await self.notify_task_state_changed()
# wait for the next user message
if len(self.callbacks) == 0:
logger.info('Use STDIN to request user message as no callbacks are registered', extra={'msg_type': 'INFO'})
logger.info(
'Use STDIN to request user message as no callbacks are registered',
extra={'msg_type': 'INFO'},
)
message = input('Request user input [type /exit to stop interaction] >> ')
user_message_observation = UserMessageObservation(message)
else:
Expand Down Expand Up @@ -307,22 +328,33 @@ def get_state(self):
return self.state

def _is_stuck(self):
if self.state is None or self.state.history is None or len(self.state.history) < 3:
if (
self.state is None
or self.state.history is None
or len(self.state.history) < 3
):
return False

# if the last three (Action, Observation) tuples are too repetitive
# the agent got stuck in a loop
if all(
[self.state.history[-i][0] == self.state.history[-3][0] for i in range(1, 3)]
[
self.state.history[-i][0] == self.state.history[-3][0]
for i in range(1, 3)
]
):
# it repeats same action, give it a chance, but not if:
if (all
(isinstance(self.state.history[-i][1], NullObservation) for i in range(1, 4))):
if all(
isinstance(self.state.history[-i][1], NullObservation)
for i in range(1, 4)
):
# same (Action, NullObservation): like 'think' the same thought over and over
logger.debug('Action, NullObservation loop detected')
return True
elif (all
(isinstance(self.state.history[-i][1], AgentErrorObservation) for i in range(1, 4))):
elif all(
isinstance(self.state.history[-i][1], AgentErrorObservation)
for i in range(1, 4)
):
# (NullAction, AgentErrorObservation): errors coming from an exception
# (Action, AgentErrorObservation): the same action getting an error, even if not necessarily the same error
logger.debug('Action, AgentErrorObservation loop detected')
Expand Down
3 changes: 2 additions & 1 deletion opendevin/sandbox/plugins/requirement.py
Expand Up @@ -4,9 +4,10 @@
@dataclass
class PluginRequirement:
"""Requirement for a plugin."""

name: str
# FOLDER/FILES to be copied to the sandbox
host_src: str
sandbox_dest: str
# NOTE: bash_script_path shoulds be relative to the `sandbox_dest` path
# NOTE: bash_script_path should be relative to the `sandbox_dest` path
bash_script_path: str