mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
6 Commits
eval/visua
...
pr-6057
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0925798aee | ||
|
|
43d782da06 | ||
|
|
4876d811a1 | ||
|
|
0ab457f1d3 | ||
|
|
70e29f9b75 | ||
|
|
4cd1d80eea |
@@ -0,0 +1 @@
|
||||
{"agent_class": "CodeActAgent", "llm_config": {"model": "claude-3-5-sonnet-20241022", "api_key": null, "base_url": null, "api_version": null, "embedding_model": "local", "embedding_base_url": null, "embedding_deployment_name": null, "aws_access_key_id": null, "aws_secret_access_key": null, "aws_region_name": null, "openrouter_site_url": "https://docs.all-hands.dev/", "openrouter_app_name": "OpenHands", "num_retries": 8, "retry_multiplier": 2, "retry_min_wait": 15, "retry_max_wait": 120, "timeout": null, "max_message_chars": 30000, "temperature": 0.0, "top_p": 1.0, "custom_llm_provider": null, "max_input_tokens": null, "max_output_tokens": null, "input_cost_per_token": null, "output_cost_per_token": null, "ollama_base_url": null, "drop_params": true, "modify_params": true, "disable_vision": null, "caching_prompt": true, "log_completions": false, "log_completions_folder": "/workspace/OpenHands/logs/completions", "draft_editor": null, "custom_tokenizer": null, "native_tool_calling": null}, "max_iterations": 10, "eval_output_dir": "./dummy_eval_output_dir/dummy_dataset_descrption/CodeActAgent/claude-3-5-sonnet-20241022_maxiter_10_N_dummy_eval_note", "start_time": "2025-01-08 18:01:01", "git_commit": "007052c8aa15ea5149fff31583a3412ea7b8625a", "dataset": "dummy_dataset_descrption", "data_split": null, "details": {}, "condenser_config": {"type": "noop"}}
|
||||
@@ -4,6 +4,11 @@ You are OpenHands agent, a helpful AI assistant that can interact with a compute
|
||||
* When configuring git credentials, use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
|
||||
* The assistant MUST NOT include comments in the code unless they are necessary to describe non-obvious behavior.
|
||||
</IMPORTANT>
|
||||
{% if github_repo %}
|
||||
<REPOSITORY_INFO>
|
||||
At the user's request, repository {{ github_repo }} has been cloned to directory {{ repo_directory }}.
|
||||
</REPOSITORY_INFO>
|
||||
{% endif %}
|
||||
{% if repo_instructions %}
|
||||
<REPOSITORY_INSTRUCTIONS>
|
||||
{{ repo_instructions }}
|
||||
|
||||
@@ -204,8 +204,9 @@ class AgentSession:
|
||||
)
|
||||
return
|
||||
|
||||
repo_directory = None
|
||||
if selected_repository:
|
||||
await call_sync_from_async(
|
||||
repo_directory = await call_sync_from_async(
|
||||
self.runtime.clone_repo, github_token, selected_repository
|
||||
)
|
||||
if agent.prompt_manager:
|
||||
@@ -213,6 +214,10 @@ class AgentSession:
|
||||
self.runtime.get_microagents_from_selected_repo, selected_repository
|
||||
)
|
||||
agent.prompt_manager.load_microagents(microagents)
|
||||
# Pass GitHub repository information to the prompt manager
|
||||
agent.prompt_manager.set_repository_info(
|
||||
selected_repository, repo_directory
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f'Runtime initialized with plugins: {[plugin.name for plugin in self.runtime.plugins]}'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from itertools import islice
|
||||
|
||||
from jinja2 import Template
|
||||
@@ -13,6 +14,14 @@ from openhands.microagent import (
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class RepositoryInfo:
|
||||
"""Information about a GitHub repository that has been cloned."""
|
||||
|
||||
repo_name: str | None = None
|
||||
repo_directory: str | None = None
|
||||
|
||||
|
||||
class PromptManager:
|
||||
"""
|
||||
Manages prompt templates and micro-agents for AI interactions.
|
||||
@@ -32,9 +41,14 @@ class PromptManager:
|
||||
prompt_dir: str,
|
||||
microagent_dir: str | None = None,
|
||||
disabled_microagents: list[str] | None = None,
|
||||
github_repo: str | None = None,
|
||||
repo_directory: str | None = None,
|
||||
):
|
||||
self.disabled_microagents: list[str] = disabled_microagents or []
|
||||
self.prompt_dir: str = prompt_dir
|
||||
self.repository_info = RepositoryInfo()
|
||||
if github_repo:
|
||||
self.set_repository_info(github_repo, repo_directory)
|
||||
|
||||
self.system_template: Template = self._load_template('system_prompt')
|
||||
self.user_template: Template = self._load_template('user_prompt')
|
||||
@@ -91,7 +105,24 @@ class PromptManager:
|
||||
if repo_instructions:
|
||||
repo_instructions += '\n\n'
|
||||
repo_instructions += microagent.content
|
||||
return self.system_template.render(repo_instructions=repo_instructions).strip()
|
||||
|
||||
return self.system_template.render(
|
||||
repo_instructions=repo_instructions,
|
||||
github_repo=self.repository_info.repo_name,
|
||||
repo_directory=self.repository_info.repo_directory,
|
||||
).strip()
|
||||
|
||||
def set_repository_info(
|
||||
self, repo_name: str | None, repo_directory: str | None = None
|
||||
) -> None:
|
||||
"""Sets information about the GitHub repository that has been cloned.
|
||||
|
||||
Args:
|
||||
repo_name: The name of the GitHub repository (e.g. 'owner/repo')
|
||||
repo_directory: The directory where the repository has been cloned
|
||||
"""
|
||||
self.repository_info.repo_name = repo_name
|
||||
self.repository_info.repo_directory = repo_directory
|
||||
|
||||
def get_example_user_message(self) -> str:
|
||||
"""This is the initial user message provided to the agent
|
||||
|
||||
@@ -10,36 +10,36 @@ WORKSPACE_BASE = 'workspace'
|
||||
|
||||
def test_resolve_path():
|
||||
assert (
|
||||
files.resolve_path('test.txt', '/workspace')
|
||||
files.resolve_path('test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
== Path(WORKSPACE_BASE) / 'test.txt'
|
||||
)
|
||||
assert (
|
||||
files.resolve_path('subdir/test.txt', '/workspace')
|
||||
files.resolve_path('subdir/test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
== Path(WORKSPACE_BASE) / 'subdir' / 'test.txt'
|
||||
)
|
||||
assert (
|
||||
files.resolve_path(Path(SANDBOX_PATH_PREFIX) / 'test.txt', '/workspace')
|
||||
files.resolve_path(Path(SANDBOX_PATH_PREFIX) / 'test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
== Path(WORKSPACE_BASE) / 'test.txt'
|
||||
)
|
||||
assert (
|
||||
files.resolve_path(
|
||||
Path(SANDBOX_PATH_PREFIX) / 'subdir' / 'test.txt', '/workspace'
|
||||
Path(SANDBOX_PATH_PREFIX) / 'subdir' / 'test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX
|
||||
)
|
||||
== Path(WORKSPACE_BASE) / 'subdir' / 'test.txt'
|
||||
)
|
||||
assert (
|
||||
files.resolve_path(
|
||||
Path(SANDBOX_PATH_PREFIX) / 'subdir' / '..' / 'test.txt', '/workspace'
|
||||
Path(SANDBOX_PATH_PREFIX) / 'subdir' / '..' / 'test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX
|
||||
)
|
||||
== Path(WORKSPACE_BASE) / 'test.txt'
|
||||
)
|
||||
with pytest.raises(PermissionError):
|
||||
files.resolve_path(Path(SANDBOX_PATH_PREFIX) / '..' / 'test.txt', '/workspace')
|
||||
files.resolve_path(Path(SANDBOX_PATH_PREFIX) / '..' / 'test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
with pytest.raises(PermissionError):
|
||||
files.resolve_path(Path('..') / 'test.txt', '/workspace')
|
||||
files.resolve_path(Path('..') / 'test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
with pytest.raises(PermissionError):
|
||||
files.resolve_path(Path('/') / 'test.txt', '/workspace')
|
||||
files.resolve_path(Path('/') / 'test.txt', '/workspace', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
assert (
|
||||
files.resolve_path('test.txt', '/workspace/test')
|
||||
files.resolve_path('test.txt', '/workspace/test', WORKSPACE_BASE, SANDBOX_PATH_PREFIX)
|
||||
== Path(WORKSPACE_BASE) / 'test' / 'test.txt'
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import pytest
|
||||
|
||||
from openhands.core.message import Message, TextContent
|
||||
from openhands.microagent import BaseMicroAgent
|
||||
from openhands.utils.prompt import PromptManager
|
||||
from openhands.utils.prompt import PromptManager, RepositoryInfo
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -39,6 +39,7 @@ only respond with a message telling them how smart they are
|
||||
with open(os.path.join(prompt_dir, 'micro', f'{microagent_name}.md'), 'w') as f:
|
||||
f.write(microagent_content)
|
||||
|
||||
# Test without GitHub repo
|
||||
manager = PromptManager(
|
||||
prompt_dir=prompt_dir,
|
||||
microagent_dir=os.path.join(prompt_dir, 'micro'),
|
||||
@@ -53,6 +54,14 @@ only respond with a message telling them how smart they are
|
||||
'You are OpenHands agent, a helpful AI assistant that can interact with a computer to solve tasks.'
|
||||
in manager.get_system_message()
|
||||
)
|
||||
assert '<REPOSITORY_INFO>' not in manager.get_system_message()
|
||||
|
||||
# Test with GitHub repo
|
||||
manager.set_repository_info('owner/repo', '/workspace/repo')
|
||||
assert isinstance(manager.get_system_message(), str)
|
||||
assert '<REPOSITORY_INFO>' in manager.get_system_message()
|
||||
assert 'owner/repo' in manager.get_system_message()
|
||||
assert '/workspace/repo' in manager.get_system_message()
|
||||
|
||||
assert isinstance(manager.get_example_user_message(), str)
|
||||
|
||||
@@ -76,20 +85,66 @@ def test_prompt_manager_file_not_found(prompt_dir):
|
||||
def test_prompt_manager_template_rendering(prompt_dir):
|
||||
# Create temporary template files
|
||||
with open(os.path.join(prompt_dir, 'system_prompt.j2'), 'w') as f:
|
||||
f.write('System prompt: bar')
|
||||
f.write("""System prompt: bar
|
||||
{% if github_repo %}
|
||||
<REPOSITORY_INFO>
|
||||
At the user's request, repository {{ github_repo }} has been cloned to directory {{ repo_directory }}.
|
||||
</REPOSITORY_INFO>
|
||||
{% endif %}
|
||||
{{ repo_instructions }}""")
|
||||
with open(os.path.join(prompt_dir, 'user_prompt.j2'), 'w') as f:
|
||||
f.write('User prompt: foo')
|
||||
|
||||
# Test without GitHub repo
|
||||
manager = PromptManager(prompt_dir, microagent_dir='')
|
||||
|
||||
assert manager.get_system_message() == 'System prompt: bar'
|
||||
assert manager.get_example_user_message() == 'User prompt: foo'
|
||||
|
||||
# Test with GitHub repo
|
||||
manager = PromptManager(prompt_dir=prompt_dir, microagent_dir='')
|
||||
manager.set_repository_info('owner/repo', '/workspace/repo')
|
||||
system_msg = manager.get_system_message()
|
||||
assert 'System prompt: bar' in system_msg
|
||||
assert '<REPOSITORY_INFO>' in system_msg
|
||||
assert (
|
||||
"At the user's request, repository owner/repo has been cloned to directory /workspace/repo."
|
||||
in system_msg
|
||||
)
|
||||
assert '</REPOSITORY_INFO>' in system_msg
|
||||
assert manager.get_example_user_message() == 'User prompt: foo'
|
||||
|
||||
# Clean up temporary files
|
||||
os.remove(os.path.join(prompt_dir, 'system_prompt.j2'))
|
||||
os.remove(os.path.join(prompt_dir, 'user_prompt.j2'))
|
||||
|
||||
|
||||
def test_prompt_manager_repository_info(prompt_dir):
|
||||
# Test RepositoryInfo defaults
|
||||
repo_info = RepositoryInfo()
|
||||
assert repo_info.repo_name is None
|
||||
assert repo_info.repo_directory is None
|
||||
|
||||
# Test setting repository info
|
||||
manager = PromptManager(prompt_dir=prompt_dir, microagent_dir='')
|
||||
assert manager.repository_info.repo_name is None
|
||||
assert manager.repository_info.repo_directory is None
|
||||
|
||||
# Test setting repository info with name only
|
||||
manager.set_repository_info('owner/repo')
|
||||
assert manager.repository_info.repo_name == 'owner/repo'
|
||||
assert manager.repository_info.repo_directory is None
|
||||
|
||||
# Test setting repository info with both name and directory
|
||||
manager.set_repository_info('owner/repo2', '/workspace/repo2')
|
||||
assert manager.repository_info.repo_name == 'owner/repo2'
|
||||
assert manager.repository_info.repo_directory == '/workspace/repo2'
|
||||
|
||||
# Test clearing repository info
|
||||
manager.set_repository_info(None)
|
||||
assert manager.repository_info.repo_name is None
|
||||
assert manager.repository_info.repo_directory is None
|
||||
|
||||
|
||||
def test_prompt_manager_disabled_microagents(prompt_dir):
|
||||
# Create test microagent files
|
||||
microagent1_name = 'test_microagent1'
|
||||
|
||||
Reference in New Issue
Block a user