Skip to content

Commit fa75b22

Browse files
mindflow-cnjianchuanlixingyaoww
authored
Enhanced llm editor (#9174)
Co-authored-by: jianchuanli <[email protected]> Co-authored-by: Xingyao Wang <[email protected]>
1 parent 8aeb4dd commit fa75b22

File tree

4 files changed

+112
-10
lines changed

4 files changed

+112
-10
lines changed

config.template.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ model = "gpt-4o"
200200
# https://github.com/All-Hands-AI/OpenHands/pull/4711
201201
#native_tool_calling = None
202202

203+
203204
# Safety settings for models that support them (e.g., Mistral AI, Gemini)
204205
# Example for Mistral AI:
205206
# safety_settings = [
@@ -218,6 +219,9 @@ model = "gpt-4o"
218219
# ]
219220
#safety_settings = []
220221

222+
[llm.draft_editor]
223+
# The number of times llm_editor tries to fix an error when editing.
224+
correct_num = 5
221225

222226
[llm.gpt4o-mini]
223227
api_key = ""
@@ -335,6 +339,9 @@ classpath = "my_package.my_module.MyCustomAgent"
335339
# Enable GPU support in the runtime
336340
#enable_gpu = false
337341

342+
# When there are multiple cards, you can specify the GPU by ID
343+
#cuda_visible_devices = ''
344+
338345
# Additional Docker runtime kwargs
339346
#docker_runtime_kwargs = {}
340347

openhands/core/config/sandbox_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class SandboxConfig(BaseModel):
8888
description="Volume mounts in the format 'host_path:container_path[:mode]', e.g. '/my/host/dir:/workspace:rw'. Multiple mounts can be specified using commas, e.g. '/path1:/workspace/path1,/path2:/workspace/path2:ro'",
8989
)
9090

91+
cuda_visible_devices: str | None = Field(default=None)
9192
model_config = ConfigDict(extra='forbid')
9293

9394
@classmethod

openhands/runtime/impl/docker/docker_runtime.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,21 @@ def init_container(self) -> None:
360360
)
361361

362362
command = self.get_action_execution_server_startup_command()
363-
363+
if self.config.sandbox.enable_gpu:
364+
gpu_ids = self.config.sandbox.cuda_visible_devices
365+
if gpu_ids is None:
366+
device_requests = [
367+
docker.types.DeviceRequest(capabilities=[['gpu']], count=-1)
368+
]
369+
else:
370+
device_requests = [
371+
docker.types.DeviceRequest(
372+
capabilities=[['gpu']],
373+
device_ids=[str(i) for i in gpu_ids.split(',')],
374+
)
375+
]
376+
else:
377+
device_requests = None
364378
try:
365379
if self.runtime_container_image is None:
366380
raise ValueError('Runtime container image is not set')
@@ -376,11 +390,7 @@ def init_container(self) -> None:
376390
detach=True,
377391
environment=environment,
378392
volumes=volumes, # type: ignore
379-
device_requests=(
380-
[docker.types.DeviceRequest(capabilities=[['gpu']], count=-1)]
381-
if self.config.sandbox.enable_gpu
382-
else None
383-
),
393+
device_requests=device_requests,
384394
**(self.config.sandbox.docker_runtime_kwargs or {}),
385395
)
386396
self.log('debug', f'Container started. Server url: {self.api_url}')

openhands/runtime/utils/edit.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,40 @@
3939
<update_snippet>{draft_changes}</update_snippet>
4040
"""
4141

42+
CORRECT_SYS_MSG = """You are a code repair assistant. Now you have an original file content and error information from a static code checking tool (lint tool). Your task is to automatically modify and return the repaired complete code based on these error messages and refer to the current file content.
43+
44+
The following are the specific task steps you need to complete:
45+
46+
Carefully read the current file content to ensure that you fully understand its code structure.
47+
48+
According to the lint error prompt, accurately locate and analyze the cause of the problem.
49+
50+
Modify the original file content and fix all errors prompted by the lint tool.
51+
52+
Return complete, runnable, and error-fixed code, paying attention to maintaining the overall style and specifications of the original code.
53+
54+
Please note:
55+
56+
Please strictly follow the lint error prompts to make modifications and do not miss any problems.
57+
58+
The modified code must be complete and cannot introduce new errors or bugs.
59+
60+
The modified code must maintain the original code function and logic, and no changes unrelated to error repair should be made."""
61+
62+
CORRECT_USER_MSG = """
63+
THE FOLLOWING ARE THE ORIGINAL FILE CONTENTS AND THE ERROR INFORMATION REPORTED BY THE LINT TOOL
64+
65+
# CURRENT FILE CONTENT:
66+
```
67+
{file_content}
68+
```
69+
70+
# ERROR MESSAGE FROM STATIC CODE CHECKING TOOL:
71+
```
72+
{lint_error}
73+
```
74+
""".strip()
75+
4276

4377
def _extract_code(string: str) -> str | None:
4478
pattern = r'<updated_code>(.*?)</updated_code>'
@@ -196,7 +230,7 @@ def _get_lint_error(
196230
return ErrorObservation(error_message)
197231
return None
198232

199-
def llm_based_edit(self, action: FileEditAction) -> Observation:
233+
def llm_based_edit(self, action: FileEditAction, retry_num: int = 0) -> Observation:
200234
obs = self.read(FileReadAction(path=action.path))
201235
if (
202236
isinstance(obs, ErrorObservation)
@@ -253,7 +287,14 @@ def llm_based_edit(self, action: FileEditAction) -> Observation:
253287
diff,
254288
)
255289
if error_obs is not None:
256-
return error_obs
290+
self.write(
291+
FileWriteAction(path=action.path, content=updated_content)
292+
)
293+
return self.correct_edit(
294+
file_content=updated_content,
295+
error_obs=error_obs,
296+
retry_num=retry_num,
297+
)
257298

258299
obs = self.write(FileWriteAction(path=action.path, content=updated_content))
259300
return FileEditObservation(
@@ -280,7 +321,8 @@ def llm_based_edit(self, action: FileEditAction) -> Observation:
280321
error_msg = (
281322
f'[Edit error: The range of lines to edit is too long.]\n'
282323
f'[The maximum number of lines allowed to edit at once is {self.MAX_LINES_TO_EDIT}. '
283-
f'Got (L{start_idx + 1}-L{end_idx}) {length_of_range} lines.]\n' # [start_idx, end_idx), so no need to + 1
324+
f'Got (L{start_idx + 1}-L{end_idx}) {length_of_range} lines.]\n'
325+
# [start_idx, end_idx), so no need to + 1
284326
)
285327
# search for relevant ranges to hint the agent
286328
topk_chunks: list[Chunk] = get_top_k_chunk_matches(
@@ -333,7 +375,12 @@ def llm_based_edit(self, action: FileEditAction) -> Observation:
333375
)
334376
if error_obs is not None:
335377
error_obs.llm_metrics = self.draft_editor_llm.metrics
336-
return error_obs
378+
self.write(FileWriteAction(path=action.path, content=updated_content))
379+
return self.correct_edit(
380+
file_content=updated_content,
381+
error_obs=error_obs,
382+
retry_num=retry_num,
383+
)
337384

338385
obs = self.write(FileWriteAction(path=action.path, content=updated_content))
339386
ret_obs = FileEditObservation(
@@ -345,3 +392,40 @@ def llm_based_edit(self, action: FileEditAction) -> Observation:
345392
)
346393
ret_obs.llm_metrics = self.draft_editor_llm.metrics
347394
return ret_obs
395+
396+
def check_retry_num(self, retry_num):
397+
correct_num = self.draft_editor_llm.config.correct_num
398+
return correct_num < retry_num
399+
400+
def correct_edit(
401+
self, file_content: str, error_obs: ErrorObservation, retry_num: int = 0
402+
) -> Observation:
403+
import openhands.agenthub.codeact_agent.function_calling as codeact_function_calling
404+
from openhands.agenthub.codeact_agent.tools import LLMBasedFileEditTool
405+
from openhands.llm.llm_utils import check_tools
406+
407+
_retry_num = retry_num + 1
408+
if self.check_retry_num(_retry_num):
409+
return error_obs
410+
tools = check_tools([LLMBasedFileEditTool], self.draft_editor_llm.config)
411+
messages = [
412+
{'role': 'system', 'content': CORRECT_SYS_MSG},
413+
{
414+
'role': 'user',
415+
'content': CORRECT_USER_MSG.format(
416+
file_content=file_content, lint_error=error_obs.content
417+
),
418+
},
419+
]
420+
params: dict = {'messages': messages, 'tools': tools}
421+
try:
422+
response = self.draft_editor_llm.completion(**params)
423+
actions = codeact_function_calling.response_to_actions(response)
424+
if len(actions) != 1:
425+
return error_obs
426+
for action in actions:
427+
if isinstance(action, FileEditAction):
428+
return self.llm_based_edit(action, _retry_num)
429+
except Exception as e:
430+
logger.error(f'correct lint error is failed: {e}')
431+
return error_obs

0 commit comments

Comments
 (0)