Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f347b39
feat: add opencode tools
sdevare-nv Jan 27, 2026
6f1172a
feat: make opencode default
sdevare-nv Jan 27, 2026
9b39a38
feat: add opencode tools
sdevare-nv Jan 27, 2026
74a50cc
fix: tool in memory
sdevare-nv Jan 27, 2026
e1cfd99
feat: add filewrite to conversation memory
sdevare-nv Jan 27, 2026
d1dfccb
feat: ingest custom prompts
sdevare-nv Jan 28, 2026
c5eb9e9
feat: removed string replace
sdevare-nv Jan 28, 2026
1ff4ccb
Merge remote-tracking branch 'origin/sdd/profiling' into sdd/opencode
sdevare-nv Feb 4, 2026
1608cec
refactor: wip
sdevare-nv Feb 13, 2026
508d80d
feat: observations
sdevare-nv Feb 13, 2026
d05dbde
feat: register opecodeagentt
sdevare-nv Feb 13, 2026
6d9c94d
add opencode to run infer
sdevare-nv Feb 13, 2026
17d6486
feat: update action exec
sdevare-nv Feb 13, 2026
b9349ed
feat: add conv memory
sdevare-nv Feb 13, 2026
0ba28a4
feat: add obs
sdevare-nv Feb 13, 2026
f9ad463
fix: grep
sdevare-nv Feb 13, 2026
3083be3
feat: match bash tool name
sdevare-nv Feb 13, 2026
767b8b5
feat: remove think for opencode
sdevare-nv Feb 13, 2026
322f5f8
feat: init
sdevare-nv Feb 14, 2026
582dc55
feat: add CodexAgent to runinfer
sdevare-nv Feb 14, 2026
650b4e2
feat: is_input to shell command
sdevare-nv Feb 14, 2026
54d2368
fix: grep tool
sdevare-nv Feb 14, 2026
342aabd
feat: update openhands
sdevare-nv Feb 15, 2026
da98f41
fix: codex tools
sdevare-nv Feb 16, 2026
a3b2b9b
fix: grep regex
sdevare-nv Feb 16, 2026
be171b0
feat: add system prompt override
sdevare-nv Feb 16, 2026
e7b8e87
feat: FunctionCallNotExistsError handling
sdevare-nv Feb 16, 2026
bdec6bc
feat: add custom user responses
sdevare-nv Feb 19, 2026
2a90b7c
feat: terminus init
sdevare-nv Feb 20, 2026
41c8c1a
fix: user prompt
sdevare-nv Feb 20, 2026
5c3116f
feat: reg action
sdevare-nv Feb 20, 2026
42ad23b
fix: traj formation
sdevare-nv Feb 20, 2026
177ac7d
feat: add prefix based on state
sdevare-nv Feb 20, 2026
d7d1e0e
feat: terminate confirmation
sdevare-nv Feb 20, 2026
bc6f071
fix: duplicate user msg
sdevare-nv Feb 20, 2026
f5daa81
feat: multiple command output
sdevare-nv Feb 20, 2026
0fa3e47
fix: keystroke bug
sdevare-nv Feb 20, 2026
65959d7
Merge pull request #13 from sdevare-nv/sdd/terminus
sdevare-nv Mar 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ share/python-wheels/
*.egg
MANIFEST
requirements.txt

temp/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down
100 changes: 83 additions & 17 deletions evaluation/benchmarks/swe_bench/run_infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
assert_and_raise,
check_maximum_retries_exceeded,
codeact_user_response,
codex_user_response,
opencode_user_response,
terminus_2_user_response,
get_default_sandbox_config_for_eval,
get_metrics,
get_openhands_config_for_eval,
Expand Down Expand Up @@ -104,6 +107,9 @@ def set_dataset_type(dataset_name: str) -> str:

AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
'CodeActAgent': codeact_user_response,
'OpenCodeAgent': opencode_user_response,
'CodexAgent': codex_user_response,
'Terminus2Agent': terminus_2_user_response,
}


Expand All @@ -120,27 +126,37 @@ def get_instruction(instance: pd.Series, metadata: EvalMetadata) -> MessageActio
mode = metadata.details['mode']
llm_model = metadata.llm_config.model

# Determine the template file based on mode and LLM
if metadata.instruction_template_name:
template_name = metadata.instruction_template_name
elif mode.startswith('swt'):
template_name = 'swt.j2'
elif mode == 'swe':
if 'gpt-4.1' in llm_model:
template_name = 'swe_gpt4.j2'
else:
template_name = (
'swe_default.j2' # Default for 'swe' mode (regular swe-bench)
)
# Check for custom instruction template path (absolute path takes precedence)
custom_instruction_template_path = metadata.details.get('instruction_template_path')

if custom_instruction_template_path and os.path.isfile(custom_instruction_template_path):
# Use custom instruction template from provided path
prompts_dir = os.path.dirname(custom_instruction_template_path)
template_name = os.path.basename(custom_instruction_template_path)
logger.info(f'Using custom instruction template: {custom_instruction_template_path}')
else:
# Fallback or error handling if mode is unexpected
logger.error(f'Unexpected evaluation mode: {mode}. Falling back to default.')
template_name = 'swe_default.j2'
# Determine the template file based on mode and LLM
if metadata.instruction_template_name:
template_name = metadata.instruction_template_name
elif mode.startswith('swt'):
template_name = 'swt.j2'
elif mode == 'swe':
if 'gpt-4.1' in llm_model:
template_name = 'swe_gpt4.j2'
else:
template_name = (
'swe_default.j2' # Default for 'swe' mode (regular swe-bench)
)
else:
# Fallback or error handling if mode is unexpected
logger.error(f'Unexpected evaluation mode: {mode}. Falling back to default.')
template_name = 'swe_default.j2'

# Default prompts directory
prompts_dir = os.path.join(os.path.dirname(__file__), 'prompts')

logger.debug(f'Using instruction template file: {template_name}')
# Set up Jinja2 environment
# Assuming templates are in 'evaluation/benchmarks/swe_bench/prompts' relative to this script
prompts_dir = os.path.join(os.path.dirname(__file__), 'prompts')
env = Environment(loader=FileSystemLoader(prompts_dir))
template = env.get_template(template_name)

Expand Down Expand Up @@ -214,6 +230,9 @@ def get_instance_docker_image(
return (docker_image_prefix.rstrip('/') + '/' + image_name).lower()





def get_config(
instance: pd.Series,
metadata: EvalMetadata,
Expand Down Expand Up @@ -273,6 +292,8 @@ def get_config(
system_prompt_filename=metadata.agent_config.system_prompt_filename
if metadata.agent_config
else 'system_prompt.j2',
system_prompt_path=SYSTEM_PROMPT_PATH,
system_prompt_long_horizon_path=SYSTEM_PROMPT_LONG_HORIZON_PATH,
)
config.set_agent_config(agent_config)

Expand Down Expand Up @@ -867,6 +888,13 @@ def filter_dataset(


if __name__ == '__main__':
# Declare globals at the start of the block
global SYSTEM_PROMPT_PATH, SYSTEM_PROMPT_LONG_HORIZON_PATH

# Module-level variables to store custom prompt paths
SYSTEM_PROMPT_PATH = None
SYSTEM_PROMPT_LONG_HORIZON_PATH = None

parser = get_evaluation_parser()
parser.add_argument(
'--dataset',
Expand Down Expand Up @@ -899,6 +927,24 @@ def filter_dataset(
default=None,
help='Path to a JSON file containing instance data to use instead of loading from HuggingFace (e.g., \'{"instance_id": "...", "repo": "...", ...}\')',
)
parser.add_argument(
'--instruction-template-path',
type=str,
default=None,
help='Path to a custom instruction template file (overrides swe_default.j2)',
)
parser.add_argument(
'--system-prompt-path',
type=str,
default=None,
help='Path to a custom system_prompt.j2 file',
)
parser.add_argument(
'--system-prompt-long-horizon-path',
type=str,
default=None,
help='Path to a custom system_prompt_long_horizon.j2 file',
)

args, _ = parser.parse_known_args()

Expand Down Expand Up @@ -996,7 +1042,27 @@ def filter_dataset(
if args.agent_config:
agent_config = get_agent_config_arg(args.agent_config, args.config_file)

# Set up custom system prompt paths if provided
if args.system_prompt_path:
if os.path.isfile(args.system_prompt_path):
SYSTEM_PROMPT_PATH = args.system_prompt_path
logger.info(f'Using custom system_prompt.j2: {SYSTEM_PROMPT_PATH}')
else:
raise ValueError(f'System prompt file does not exist: {args.system_prompt_path}')

if args.system_prompt_long_horizon_path:
if os.path.isfile(args.system_prompt_long_horizon_path):
SYSTEM_PROMPT_LONG_HORIZON_PATH = args.system_prompt_long_horizon_path
logger.info(f'Using custom system_prompt_long_horizon.j2: {SYSTEM_PROMPT_LONG_HORIZON_PATH}')
else:
raise ValueError(f'System prompt long horizon file does not exist: {args.system_prompt_long_horizon_path}')

# Build details dict with custom prompt paths
details = {'mode': args.mode}
if args.instruction_template_path:
details['instruction_template_path'] = args.instruction_template_path
logger.info(f'Custom instruction template path: {args.instruction_template_path}')

_agent_cls = openhands.agenthub.Agent.get_cls(args.agent_cls)

dataset_description = (
Expand Down
26 changes: 24 additions & 2 deletions evaluation/benchmarks/swe_bench/scripts/run_infer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ EVAL_OUTPUT_DIR=${9}
SELECTED_ID=${10}
INSTANCE_DICT_PATH=${11}
CONFIG_FILE=${12}
N_RUNS=${13}
MODE=${14}
INSTRUCTION_TEMPLATE_PATH=${13}
SYSTEM_PROMPT_PATH=${14}
SYSTEM_PROMPT_LONG_HORIZON_PATH=${15}
N_RUNS=${16}
MODE=${17}


if [ -z "$NUM_WORKERS" ]; then
NUM_WORKERS=1
Expand Down Expand Up @@ -133,6 +137,9 @@ echo "EVAL_CONDENSER: $EVAL_CONDENSER"
echo "EVAL_OUTPUT_DIR: $EVAL_OUTPUT_DIR"
echo "SELECTED_ID: $SELECTED_ID"
echo "INSTANCE_DICT_PATH: $INSTANCE_DICT_PATH"
echo "INSTRUCTION_TEMPLATE_PATH: $INSTRUCTION_TEMPLATE_PATH"
echo "SYSTEM_PROMPT_PATH: $SYSTEM_PROMPT_PATH"
echo "SYSTEM_PROMPT_LONG_HORIZON_PATH: $SYSTEM_PROMPT_LONG_HORIZON_PATH"
echo "TMUX_MEMORY_LIMIT: $TMUX_MEMORY_LIMIT"
echo "COMMAND_EXEC_TIMEOUT: $COMMAND_EXEC_TIMEOUT"

Expand Down Expand Up @@ -199,6 +206,21 @@ function run_eval() {
COMMAND="$COMMAND --config-file $CONFIG_FILE"
fi

if [ -n "$INSTRUCTION_TEMPLATE_PATH" ]; then
echo "INSTRUCTION_TEMPLATE_PATH: $INSTRUCTION_TEMPLATE_PATH"
COMMAND="$COMMAND --instruction-template-path $INSTRUCTION_TEMPLATE_PATH"
fi

if [ -n "$SYSTEM_PROMPT_PATH" ]; then
echo "SYSTEM_PROMPT_PATH: $SYSTEM_PROMPT_PATH"
COMMAND="$COMMAND --system-prompt-path $SYSTEM_PROMPT_PATH"
fi

if [ -n "$SYSTEM_PROMPT_LONG_HORIZON_PATH" ]; then
echo "SYSTEM_PROMPT_LONG_HORIZON_PATH: $SYSTEM_PROMPT_LONG_HORIZON_PATH"
COMMAND="$COMMAND --system-prompt-long-horizon-path $SYSTEM_PROMPT_LONG_HORIZON_PATH"
fi

# Run the command
eval $COMMAND
}
Expand Down
144 changes: 144 additions & 0 deletions evaluation/utils/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,150 @@ def codeact_user_response(
return msg


def opencode_user_response(
state: State,
encapsulate_solution: bool = False,
try_parse: Callable[[Action], str] | None = None,
) -> str:
encaps_str = (
(
'Your final answer MUST be encapsulated within <solution> and </solution>.\n'
'For example: The answer to the question is <solution> 42 </solution>.\n'
)
if encapsulate_solution
else ''
)
msg = (
'Please address this message and continue with your tasks.\n'
'If you have completed the task, use the finish tool to signal completion '
'with a clear summary of actions taken and their results.\n'
f'{encaps_str}'
'IMPORTANT: YOU SHOULD NEVER ASK FOR HUMAN HELP.\n'
)

if state.history:
if try_parse is not None:
last_action = next(
(
event
for event in reversed(state.history)
if isinstance(event, Action)
),
None,
)
ans = try_parse(last_action)
if ans is not None:
return '/exit'

user_msgs = [
event
for event in state.history
if isinstance(event, MessageAction) and event.source == 'user'
]
if len(user_msgs) >= 2:
return (
msg
+ 'If you want to give up, use the "finish" tool to finish the interaction.\n'
)
return msg


def codex_user_response(
state: State,
encapsulate_solution: bool = False,
try_parse: Callable[[Action], str] | None = None,
) -> str:
encaps_str = (
(
'Your final answer MUST be encapsulated within <solution> and </solution>.\n'
'For example: The answer to the question is <solution> 42 </solution>.\n'
)
if encapsulate_solution
else ''
)
msg = (
'Please keep going until the task is completely resolved. '
'Only terminate when you are sure that the problem is solved.\n'
'Autonomously resolve the task to the best of your ability before finishing.\n'
f'{encaps_str}'
'IMPORTANT: YOU SHOULD NEVER ASK FOR HUMAN HELP. Do NOT guess or make up an answer.\n'
)

if state.history:
if try_parse is not None:
last_action = next(
(
event
for event in reversed(state.history)
if isinstance(event, Action)
),
None,
)
ans = try_parse(last_action)
if ans is not None:
return '/exit'

user_msgs = [
event
for event in state.history
if isinstance(event, MessageAction) and event.source == 'user'
]
if len(user_msgs) >= 2:
return (
msg
+ 'If you want to give up, use the "finish" tool to finish the interaction.\n'
)
return msg


def terminus_2_user_response(
state: State,
encapsulate_solution: bool = False,
try_parse: Callable[[Action], str] | None = None,
) -> str:
encaps_str = (
(
'Your final answer MUST be encapsulated within <solution> and </solution>.\n'
'For example: The answer to the question is <solution> 42 </solution>.\n'
)
if encapsulate_solution
else ''
)
msg = (
'Please continue working on the task. '
'Analyze the terminal output and issue the next batch of commands.\n'
'When the task is fully complete, set "task_complete": true in your JSON response.\n'
f'{encaps_str}'
'IMPORTANT: YOU SHOULD NEVER ASK FOR HUMAN HELP.\n'
)

if state.history:
if try_parse is not None:
last_action = next(
(
event
for event in reversed(state.history)
if isinstance(event, Action)
),
None,
)
ans = try_parse(last_action)
if ans is not None:
return '/exit'

user_msgs = [
event
for event in state.history
if isinstance(event, MessageAction) and event.source == 'user'
]
if len(user_msgs) >= 2:
return (
msg
+ 'If you want to give up, set "task_complete": true in your JSON response.\n'
)
return msg


def cleanup():
print('Cleaning up child processes...')
for process in mp.active_children():
Expand Down
6 changes: 6 additions & 0 deletions openhands/agenthub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
from openhands.agenthub import ( # noqa: E402
browsing_agent,
codeact_agent,
codex_agent,
dummy_agent,
loc_agent,
opencode_agent,
readonly_agent,
terminus_2_agent,
visualbrowsing_agent,
)
from openhands.controller.agent import Agent # noqa: E402
Expand All @@ -21,4 +24,7 @@
'visualbrowsing_agent',
'readonly_agent',
'loc_agent',
'opencode_agent',
'codex_agent',
'terminus_2_agent',
]
Loading
Loading