Skip to content

Commit d1482e6

Browse files
committed
Mostly prompt enhancement for the new analysis tools approach
1 parent f6e8828 commit d1482e6

File tree

4 files changed

+48
-30
lines changed

4 files changed

+48
-30
lines changed

openhands/agenthub/codeact_agent/codeact_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def replay_phase_changed(self, phase: ReplayDebuggingPhase) -> None:
337337
codeact_replay_phase=phase,
338338
)
339339
logger.debug(
340-
f'TOOLS loaded for CodeActAgent (replay phase {phase}): {json.dumps(self.tools, indent=2)}'
340+
f'[REPLAY] CodeActAgent.replay_phase_changed({phase}). New tools: {json.dumps(self.tools, indent=2)}'
341341
)
342342

343343
def step(self, state: State) -> Action:

openhands/agenthub/codeact_agent/function_calling.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
type='function',
4343
function=ChatCompletionToolParamFunctionChunk(
4444
name='inspect-data',
45-
description=_REPLAY_INSPECT_DATA_DESCRIPTION,
45+
description=_REPLAY_INSPECT_DATA_DESCRIPTION.strip(),
4646
parameters={
4747
'type': 'object',
4848
'properties': {
@@ -52,7 +52,7 @@
5252
},
5353
'point': {
5454
'type': 'string',
55-
'description': 'The point at which to inspect the runtime. The first point comes from the `INITIAL ANALYSIS`.',
55+
'description': 'The point at which to inspect the runtime. The first point comes from the `thisPoint` in the Initial analysis.',
5656
},
5757
},
5858
'required': ['expression', 'point'],
@@ -72,7 +72,7 @@
7272
type='function',
7373
function=ChatCompletionToolParamFunctionChunk(
7474
name='inspect-point',
75-
description=_REPLAY_INSPECT_POINT_DESCRIPTION,
75+
description=_REPLAY_INSPECT_POINT_DESCRIPTION.strip(),
7676
parameters={
7777
'type': 'object',
7878
'properties': {
@@ -97,7 +97,7 @@
9797
type='function',
9898
function=ChatCompletionToolParamFunctionChunk(
9999
name='submit-hypothesis',
100-
description=_REPLAY_SUBMIT_HYPOTHESIS_DESCRIPTION,
100+
description=_REPLAY_SUBMIT_HYPOTHESIS_DESCRIPTION.strip(),
101101
parameters={
102102
'type': 'object',
103103
'properties': {

openhands/events/action/replay.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class ReplayCmdRunAction(Action):
3939

4040
@property
4141
def message(self) -> str:
42-
return f'[REPLAY] {self.command_name} {json.dumps(self.command_args)}'
42+
return f'[REPLAY] {json.dumps({"command": self.command_name, "args": self.command_args})}'
4343

4444
def __str__(self) -> str:
4545
ret = f'**ReplayCmdRunAction (source={self.source})**\n'

openhands/events/replay.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ class AnalysisToolMetadata(TypedDict, total=False):
8484
class AnnotateResult(TypedDict, total=False):
8585
point: str
8686
commentText: str | None
87-
annotatedRepo: str
88-
annotatedLocations: list[AnnotatedLocation]
87+
annotatedRepo: str | None
88+
annotatedLocations: list[AnnotatedLocation] | None
8989
pointLocation: str | None
9090
metadata: AnalysisToolMetadata | None
9191

@@ -97,6 +97,23 @@ def safe_parse_json(text: str) -> dict[str, Any] | None:
9797
return None
9898

9999

100+
def split_metadata(result):
101+
if 'metadata' not in result:
102+
return {}, result
103+
metadata = result['metadata']
104+
data = dict(result)
105+
del data['metadata']
106+
return metadata, data
107+
108+
109+
def enhance_prompt(user_message: MessageAction, prefix: str, suffix: str | None = None):
110+
if prefix != '':
111+
user_message.content = f'{prefix}\n\n{user_message.content}'
112+
if suffix is not None:
113+
user_message.content = f'{user_message.content}\n\n{suffix}'
114+
logger.info(f'[REPLAY] Enhanced user prompt:\n{user_message.content}')
115+
116+
100117
def handle_replay_observation(
101118
state: State, observation: ReplayCmdOutputObservation
102119
) -> AnalysisToolMetadata | None:
@@ -121,11 +138,17 @@ def handle_replay_observation(
121138
result: AnnotateResult = cast(
122139
AnnotateResult, safe_parse_json(observation.content)
123140
)
124-
if result and result.get('metadata'):
125-
metadata = result.get('metadata')
141+
142+
if result and 'metadata' in result:
143+
metadata, data = split_metadata(result)
144+
intro = 'This bug had a timetravel debugger recording which has been analyzed. Use the analysis results and the timetravel debugger `inspect-*` tools to find the bug. Once found `submit-hypothesis`, so your results can be used to implement the solution.\n'
145+
enhance_prompt(
146+
user_message,
147+
intro,
148+
f'## Initial Analysis\n{json.dumps(data, indent=2)}',
149+
)
126150
return metadata
127151
elif result and result.get('annotatedRepo'):
128-
original_prompt = user_message.content
129152
annotated_repo_path = result.get('annotatedRepo', '')
130153
comment_text = result.get('commentText', '')
131154
react_component_name = result.get('reactComponentName', '')
@@ -136,30 +159,25 @@ def handle_replay_observation(
136159
# TODO: Move this to a prompt template file.
137160
if comment_text:
138161
if react_component_name:
139-
enhancement = f'There is a change needed to the {react_component_name} component.\n'
162+
intro = f'There is a change needed to the {react_component_name} component.\n'
140163
else:
141-
enhancement = (
142-
f'There is a change needed in {annotated_repo_path}:\n'
143-
)
144-
enhancement += f'{comment_text}\n\n'
164+
intro = f'There is a change needed in {annotated_repo_path}:\n'
165+
intro += f'{comment_text}\n\n'
145166
elif console_error:
146-
enhancement = f'There is a change needed in {annotated_repo_path} to fix a console error that has appeared unexpectedly:\n'
147-
enhancement += f'{console_error}\n\n'
148-
149-
enhancement += '<IMPORTANT>\n'
150-
enhancement += 'Information about a reproduction of the problem is available in source comments.\n'
151-
enhancement += 'You must search for these comments and use them to get a better understanding of the problem.\n'
152-
enhancement += f'The first reproduction comment to search for is named {start_name}. Start your investigation there.\n'
153-
enhancement += '</IMPORTANT>\n'
154-
155-
# Enhance:
156-
user_message.content = f'{enhancement}\n\n{original_prompt}'
157-
# user_message.content = enhancement
158-
logger.info(f'[REPLAY] Enhanced user prompt:\n{user_message.content}')
167+
intro = f'There is a change needed in {annotated_repo_path} to fix a console error that has appeared unexpectedly:\n'
168+
intro += f'{console_error}\n\n'
169+
170+
intro += '<IMPORTANT>\n'
171+
intro += 'Information about a reproduction of the problem is available in source comments.\n'
172+
intro += 'You must search for these comments and use them to get a better understanding of the problem.\n'
173+
intro += f'The first reproduction comment to search for is named {start_name}. Start your investigation there.\n'
174+
intro += '</IMPORTANT>\n'
175+
176+
enhance_prompt(user_message, intro)
159177
return None
160178
else:
161179
logger.warning(
162-
f'DDBG Replay command did not return an interpretable result. Observed: {str(observation.content)}'
180+
f'[REPLAY] Replay observation cannot be interpreted. Observed content: {str(observation.content)}'
163181
)
164182

165183
return None

0 commit comments

Comments
 (0)