fix occurrences of MicroAgent to the standard "Microagent" (#7791)

This commit is contained in:
Robert Brennan
2025-04-10 11:23:19 -04:00
committed by GitHub
parent 7910a90522
commit d924e7cea5
16 changed files with 127 additions and 127 deletions

View File

@@ -21,8 +21,8 @@ OpenHandsがリポジトリで動作する際:
``` ```
--- ---
name: <Microagentの名前> name: <Microagentの名前>
type: <MicroAgentのタイプ> type: <Microagentのタイプ>
version: <MicroAgentのバージョン> version: <Microagentのバージョン>
agent: <エージェントのタイプ (通常はCodeActAgent)> agent: <エージェントのタイプ (通常はCodeActAgent)>
triggers: triggers:
- <オプション: microagentをトリガーするキーワード。トリガーを削除すると、常に含まれるようになります> - <オプション: microagentをトリガーするキーワード。トリガーを削除すると、常に含まれるようになります>

View File

@@ -21,8 +21,8 @@ Todos os microagentes usam arquivos markdown com frontmatter YAML que possuem in
``` ```
--- ---
name: <Nome do microagente> name: <Nome do microagente>
type: <Tipo do MicroAgent> type: <Tipo do Microagent>
version: <Versão do MicroAgent> version: <Versão do Microagent>
agent: <O tipo de agente (normalmente CodeActAgent)> agent: <O tipo de agente (normalmente CodeActAgent)>
triggers: triggers:
- <Palavras-chave opcionais que acionam o microagente. Se os gatilhos forem removidos, ele sempre será incluído> - <Palavras-chave opcionais que acionam o microagente. Se os gatilhos forem removidos, ele sempre será incluído>

View File

@@ -4961,19 +4961,19 @@
"tr": "Gezinme tamamlandı" "tr": "Gezinme tamamlandı"
}, },
"OBSERVATION_MESSAGE$RECALL": { "OBSERVATION_MESSAGE$RECALL": {
"en": "MicroAgent Activated", "en": "Microagent Activated",
"ja": "マイクロエージェントが有効化されました", "ja": "マイクロエージェントが有効化されました",
"zh-CN": "微代理已激活", "zh-CN": "微代理已激活",
"zh-TW": "微代理已啟動", "zh-TW": "微代理已啟動",
"ko-KR": "마이크로에이전트 활성화됨", "ko-KR": "마이크로에이전트 활성화됨",
"no": "MikroAgent aktivert", "no": "MikroAgent aktivert",
"it": "MicroAgent attivato", "it": "Microagent attivato",
"pt": "MicroAgent ativado", "pt": "Microagent ativado",
"es": "MicroAgent activado", "es": "Microagent activado",
"ar": "تم تنشيط الوكيل المصغر", "ar": "تم تنشيط الوكيل المصغر",
"fr": "MicroAgent activé", "fr": "Microagent activé",
"tr": "MikroAjan Etkinleştirildi", "tr": "MikroAjan Etkinleştirildi",
"de": "MicroAgent aktiviert" "de": "Microagent aktiviert"
}, },
"EXPANDABLE_MESSAGE$SHOW_DETAILS": { "EXPANDABLE_MESSAGE$SHOW_DETAILS": {
"en": "Show details", "en": "Show details",
@@ -6089,4 +6089,4 @@
"tr": "belgelendirme", "tr": "belgelendirme",
"de": "Dokumentation" "de": "Dokumentation"
} }
} }

View File

@@ -1,6 +1,6 @@
# OpenHands MicroAgents # OpenHands Microagents
MicroAgents are specialized prompts that enhance OpenHands with domain-specific knowledge and task-specific workflows. They help developers by providing expert guidance, automating common tasks, and ensuring consistent practices across projects. Each microagent is designed to excel in a specific area, from Git operations to code review processes. Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge and task-specific workflows. They help developers by providing expert guidance, automating common tasks, and ensuring consistent practices across projects. Each microagent is designed to excel in a specific area, from Git operations to code review processes.
## Sources of Microagents ## Sources of Microagents
@@ -49,7 +49,7 @@ When OpenHands works with a repository, it:
2. Loads relevant knowledge agents based on keywords in conversations 2. Loads relevant knowledge agents based on keywords in conversations
3. Enable task agent if user select one of them 3. Enable task agent if user select one of them
## Types of MicroAgents ## Types of Microagents
Most microagents use markdown files with YAML frontmatter. For repository agents (repo.md), the frontmatter is optional - if not provided, the file will be loaded with default settings as a repository agent. Most microagents use markdown files with YAML frontmatter. For repository agents (repo.md), the frontmatter is optional - if not provided, the file will be loaded with default settings as a repository agent.

View File

@@ -222,14 +222,14 @@ class BrowserUnavailableException(Exception):
# ============================================ # ============================================
class MicroAgentError(Exception): class MicroagentError(Exception):
"""Base exception for all microagent errors.""" """Base exception for all microagent errors."""
pass pass
class MicroAgentValidationError(MicroAgentError): class MicroagentValidationError(MicroagentError):
"""Raised when there's a validation error in microagent metadata.""" """Raised when there's a validation error in microagent metadata."""
def __init__(self, message: str = 'Micro agent validation failed') -> None: def __init__(self, message: str = 'Microagent validation failed') -> None:
super().__init__(message) super().__init__(message)

View File

@@ -18,7 +18,7 @@ from openhands.events.event import Event
from openhands.integrations.provider import ProviderToken, ProviderType, SecretStore from openhands.integrations.provider import ProviderToken, ProviderType, SecretStore
from openhands.llm.llm import LLM from openhands.llm.llm import LLM
from openhands.memory.memory import Memory from openhands.memory.memory import Memory
from openhands.microagent.microagent import BaseMicroAgent from openhands.microagent.microagent import BaseMicroagent
from openhands.runtime import get_runtime_cls from openhands.runtime import get_runtime_cls
from openhands.runtime.base import Runtime from openhands.runtime.base import Runtime
from openhands.security import SecurityAnalyzer, options from openhands.security import SecurityAnalyzer, options
@@ -160,7 +160,7 @@ def create_memory(
memory.set_runtime_info(runtime) memory.set_runtime_info(runtime)
# loads microagents from repo/.openhands/microagents # loads microagents from repo/.openhands/microagents
microagents: list[BaseMicroAgent] = runtime.get_microagents_from_selected_repo( microagents: list[BaseMicroagent] = runtime.get_microagents_from_selected_repo(
selected_repository selected_repository
) )
memory.load_user_workspace_microagents(microagents) memory.load_user_workspace_microagents(microagents)

View File

@@ -15,9 +15,9 @@ from openhands.events.observation.agent import (
from openhands.events.observation.empty import NullObservation from openhands.events.observation.empty import NullObservation
from openhands.events.stream import EventStream, EventStreamSubscriber from openhands.events.stream import EventStream, EventStreamSubscriber
from openhands.microagent import ( from openhands.microagent import (
BaseMicroAgent, BaseMicroagent,
KnowledgeMicroAgent, KnowledgeMicroagent,
RepoMicroAgent, RepoMicroagent,
load_microagents_from_dir, load_microagents_from_dir,
) )
from openhands.runtime.base import Runtime from openhands.runtime.base import Runtime
@@ -58,8 +58,8 @@ class Memory:
) )
# Additional placeholders to store user workspace microagents # Additional placeholders to store user workspace microagents
self.repo_microagents: dict[str, RepoMicroAgent] = {} self.repo_microagents: dict[str, RepoMicroagent] = {}
self.knowledge_microagents: dict[str, KnowledgeMicroAgent] = {} self.knowledge_microagents: dict[str, KnowledgeMicroagent] = {}
# Store repository / runtime info to send them to the templating later # Store repository / runtime info to send them to the templating later
self.repository_info: RepositoryInfo | None = None self.repository_info: RepositoryInfo | None = None
@@ -229,7 +229,7 @@ class Memory:
return recalled_content return recalled_content
def load_user_workspace_microagents( def load_user_workspace_microagents(
self, user_microagents: list[BaseMicroAgent] self, user_microagents: list[BaseMicroagent]
) -> None: ) -> None:
""" """
This method loads microagents from a user's cloned repo or workspace directory. This method loads microagents from a user's cloned repo or workspace directory.
@@ -240,9 +240,9 @@ class Memory:
'Loading user workspace microagents: %s', [m.name for m in user_microagents] 'Loading user workspace microagents: %s', [m.name for m in user_microagents]
) )
for user_microagent in user_microagents: for user_microagent in user_microagents:
if isinstance(user_microagent, KnowledgeMicroAgent): if isinstance(user_microagent, KnowledgeMicroagent):
self.knowledge_microagents[user_microagent.name] = user_microagent self.knowledge_microagents[user_microagent.name] = user_microagent
elif isinstance(user_microagent, RepoMicroAgent): elif isinstance(user_microagent, RepoMicroagent):
self.repo_microagents[user_microagent.name] = user_microagent self.repo_microagents[user_microagent.name] = user_microagent
def _load_global_microagents(self) -> None: def _load_global_microagents(self) -> None:
@@ -253,10 +253,10 @@ class Memory:
GLOBAL_MICROAGENTS_DIR GLOBAL_MICROAGENTS_DIR
) )
for name, agent in knowledge_agents.items(): for name, agent in knowledge_agents.items():
if isinstance(agent, KnowledgeMicroAgent): if isinstance(agent, KnowledgeMicroagent):
self.knowledge_microagents[name] = agent self.knowledge_microagents[name] = agent
for name, agent in repo_agents.items(): for name, agent in repo_agents.items():
if isinstance(agent, RepoMicroAgent): if isinstance(agent, RepoMicroagent):
self.repo_microagents[name] = agent self.repo_microagents[name] = agent
def set_repository_info(self, repo_name: str, repo_directory: str) -> None: def set_repository_info(self, repo_name: str, repo_directory: str) -> None:

View File

@@ -1,19 +1,19 @@
from .microagent import ( from .microagent import (
BaseMicroAgent, BaseMicroagent,
KnowledgeMicroAgent, KnowledgeMicroagent,
RepoMicroAgent, RepoMicroagent,
TaskMicroAgent, TaskMicroagent,
load_microagents_from_dir, load_microagents_from_dir,
) )
from .types import MicroAgentMetadata, MicroAgentType, TaskInput from .types import MicroagentMetadata, MicroagentType, TaskInput
__all__ = [ __all__ = [
'BaseMicroAgent', 'BaseMicroagent',
'KnowledgeMicroAgent', 'KnowledgeMicroagent',
'RepoMicroAgent', 'RepoMicroagent',
'TaskMicroAgent', 'TaskMicroagent',
'MicroAgentMetadata', 'MicroagentMetadata',
'MicroAgentType', 'MicroagentType',
'TaskInput', 'TaskInput',
'load_microagents_from_dir', 'load_microagents_from_dir',
] ]

View File

@@ -6,25 +6,25 @@ import frontmatter
from pydantic import BaseModel from pydantic import BaseModel
from openhands.core.exceptions import ( from openhands.core.exceptions import (
MicroAgentValidationError, MicroagentValidationError,
) )
from openhands.core.logger import openhands_logger as logger from openhands.core.logger import openhands_logger as logger
from openhands.microagent.types import MicroAgentMetadata, MicroAgentType from openhands.microagent.types import MicroagentMetadata, MicroagentType
class BaseMicroAgent(BaseModel): class BaseMicroagent(BaseModel):
"""Base class for all microagents.""" """Base class for all microagents."""
name: str name: str
content: str content: str
metadata: MicroAgentMetadata metadata: MicroagentMetadata
source: str # path to the file source: str # path to the file
type: MicroAgentType type: MicroagentType
@classmethod @classmethod
def load( def load(
cls, path: Union[str, Path], file_content: str | None = None cls, path: Union[str, Path], file_content: str | None = None
) -> 'BaseMicroAgent': ) -> 'BaseMicroagent':
"""Load a microagent from a markdown file with frontmatter.""" """Load a microagent from a markdown file with frontmatter."""
path = Path(path) if isinstance(path, str) else path path = Path(path) if isinstance(path, str) else path
@@ -35,12 +35,12 @@ class BaseMicroAgent(BaseModel):
# Legacy repo instructions are stored in .openhands_instructions # Legacy repo instructions are stored in .openhands_instructions
if path.name == '.openhands_instructions': if path.name == '.openhands_instructions':
return RepoMicroAgent( return RepoMicroagent(
name='repo_legacy', name='repo_legacy',
content=file_content, content=file_content,
metadata=MicroAgentMetadata(name='repo_legacy'), metadata=MicroagentMetadata(name='repo_legacy'),
source=str(path), source=str(path),
type=MicroAgentType.REPO_KNOWLEDGE, type=MicroagentType.REPO_KNOWLEDGE,
) )
file_io = io.StringIO(file_content) file_io = io.StringIO(file_content)
@@ -51,15 +51,15 @@ class BaseMicroAgent(BaseModel):
metadata_dict = loaded.metadata or {} metadata_dict = loaded.metadata or {}
try: try:
metadata = MicroAgentMetadata(**metadata_dict) metadata = MicroagentMetadata(**metadata_dict)
except Exception as e: except Exception as e:
raise MicroAgentValidationError(f'Error loading metadata: {e}') from e raise MicroagentValidationError(f'Error loading metadata: {e}') from e
# Create appropriate subclass based on type # Create appropriate subclass based on type
subclass_map = { subclass_map = {
MicroAgentType.KNOWLEDGE: KnowledgeMicroAgent, MicroagentType.KNOWLEDGE: KnowledgeMicroagent,
MicroAgentType.REPO_KNOWLEDGE: RepoMicroAgent, MicroagentType.REPO_KNOWLEDGE: RepoMicroagent,
MicroAgentType.TASK: TaskMicroAgent, MicroagentType.TASK: TaskMicroagent,
} }
if metadata.type not in subclass_map: if metadata.type not in subclass_map:
raise ValueError(f'Unknown microagent type: {metadata.type}') raise ValueError(f'Unknown microagent type: {metadata.type}')
@@ -74,7 +74,7 @@ class BaseMicroAgent(BaseModel):
) )
class KnowledgeMicroAgent(BaseMicroAgent): class KnowledgeMicroagent(BaseMicroagent):
"""Knowledge micro-agents provide specialized expertise that's triggered by keywords in conversations. They help with: """Knowledge micro-agents provide specialized expertise that's triggered by keywords in conversations. They help with:
- Language best practices - Language best practices
- Framework guidelines - Framework guidelines
@@ -84,8 +84,8 @@ class KnowledgeMicroAgent(BaseMicroAgent):
def __init__(self, **data): def __init__(self, **data):
super().__init__(**data) super().__init__(**data)
if self.type != MicroAgentType.KNOWLEDGE: if self.type != MicroagentType.KNOWLEDGE:
raise ValueError('KnowledgeMicroAgent must have type KNOWLEDGE') raise ValueError('KnowledgeMicroagent must have type KNOWLEDGE')
def match_trigger(self, message: str) -> str | None: def match_trigger(self, message: str) -> str | None:
"""Match a trigger in the message. """Match a trigger in the message.
@@ -103,10 +103,10 @@ class KnowledgeMicroAgent(BaseMicroAgent):
return self.metadata.triggers return self.metadata.triggers
class RepoMicroAgent(BaseMicroAgent): class RepoMicroagent(BaseMicroagent):
"""MicroAgent specialized for repository-specific knowledge and guidelines. """Microagent specialized for repository-specific knowledge and guidelines.
RepoMicroAgents are loaded from `.openhands/microagents/repo.md` files within repositories RepoMicroagents are loaded from `.openhands/microagents/repo.md` files within repositories
and contain private, repository-specific instructions that are automatically loaded when and contain private, repository-specific instructions that are automatically loaded when
working with that repository. They are ideal for: working with that repository. They are ideal for:
- Repository-specific guidelines - Repository-specific guidelines
@@ -117,23 +117,23 @@ class RepoMicroAgent(BaseMicroAgent):
def __init__(self, **data): def __init__(self, **data):
super().__init__(**data) super().__init__(**data)
if self.type != MicroAgentType.REPO_KNOWLEDGE: if self.type != MicroagentType.REPO_KNOWLEDGE:
raise ValueError('RepoMicroAgent must have type REPO_KNOWLEDGE') raise ValueError('RepoMicroagent must have type REPO_KNOWLEDGE')
class TaskMicroAgent(BaseMicroAgent): class TaskMicroagent(BaseMicroagent):
"""MicroAgent specialized for task-based operations.""" """Microagent specialized for task-based operations."""
def __init__(self, **data): def __init__(self, **data):
super().__init__(**data) super().__init__(**data)
if self.type != MicroAgentType.TASK: if self.type != MicroagentType.TASK:
raise ValueError('TaskMicroAgent must have type TASK') raise ValueError('TaskMicroagent must have type TASK')
def load_microagents_from_dir( def load_microagents_from_dir(
microagent_dir: Union[str, Path], microagent_dir: Union[str, Path],
) -> tuple[ ) -> tuple[
dict[str, RepoMicroAgent], dict[str, KnowledgeMicroAgent], dict[str, TaskMicroAgent] dict[str, RepoMicroagent], dict[str, KnowledgeMicroagent], dict[str, TaskMicroagent]
]: ]:
"""Load all microagents from the given directory. """Load all microagents from the given directory.
@@ -161,12 +161,12 @@ def load_microagents_from_dir(
if file.name == 'README.md': if file.name == 'README.md':
continue continue
try: try:
agent = BaseMicroAgent.load(file) agent = BaseMicroagent.load(file)
if isinstance(agent, RepoMicroAgent): if isinstance(agent, RepoMicroagent):
repo_agents[agent.name] = agent repo_agents[agent.name] = agent
elif isinstance(agent, KnowledgeMicroAgent): elif isinstance(agent, KnowledgeMicroagent):
knowledge_agents[agent.name] = agent knowledge_agents[agent.name] = agent
elif isinstance(agent, TaskMicroAgent): elif isinstance(agent, TaskMicroagent):
task_agents[agent.name] = agent task_agents[agent.name] = agent
logger.debug(f'Loaded agent {agent.name} from {file}') logger.debug(f'Loaded agent {agent.name} from {file}')
except Exception as e: except Exception as e:

View File

@@ -3,7 +3,7 @@ from enum import Enum
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
class MicroAgentType(str, Enum): class MicroagentType(str, Enum):
"""Type of microagent.""" """Type of microagent."""
KNOWLEDGE = 'knowledge' KNOWLEDGE = 'knowledge'
@@ -11,11 +11,11 @@ class MicroAgentType(str, Enum):
TASK = 'task' TASK = 'task'
class MicroAgentMetadata(BaseModel): class MicroagentMetadata(BaseModel):
"""Metadata for all microagents.""" """Metadata for all microagents."""
name: str = 'default' name: str = 'default'
type: MicroAgentType = Field(default=MicroAgentType.REPO_KNOWLEDGE) type: MicroagentType = Field(default=MicroagentType.REPO_KNOWLEDGE)
version: str = Field(default='1.0.0') version: str = Field(default='1.0.0')
agent: str = Field(default='CodeActAgent') agent: str = Field(default='CodeActAgent')
triggers: list[str] = [] # optional, only exists for knowledge microagents triggers: list[str] = [] # optional, only exists for knowledge microagents

View File

@@ -50,7 +50,7 @@ from openhands.integrations.provider import (
) )
from openhands.integrations.service_types import Repository from openhands.integrations.service_types import Repository
from openhands.microagent import ( from openhands.microagent import (
BaseMicroAgent, BaseMicroagent,
load_microagents_from_dir, load_microagents_from_dir,
) )
from openhands.runtime.plugins import ( from openhands.runtime.plugins import (
@@ -414,13 +414,13 @@ class Runtime(FileEditRuntimeMixin):
def get_microagents_from_selected_repo( def get_microagents_from_selected_repo(
self, selected_repository: str | None self, selected_repository: str | None
) -> list[BaseMicroAgent]: ) -> list[BaseMicroagent]:
"""Load microagents from the selected repository. """Load microagents from the selected repository.
If selected_repository is None, load microagents from the current workspace. If selected_repository is None, load microagents from the current workspace.
This is the main entry point for loading microagents. This is the main entry point for loading microagents.
""" """
loaded_microagents: list[BaseMicroAgent] = [] loaded_microagents: list[BaseMicroagent] = []
workspace_root = Path(self.config.workspace_mount_path_in_sandbox) workspace_root = Path(self.config.workspace_mount_path_in_sandbox)
microagents_dir = workspace_root / '.openhands' / 'microagents' microagents_dir = workspace_root / '.openhands' / 'microagents'
repo_root = None repo_root = None
@@ -450,7 +450,7 @@ class Runtime(FileEditRuntimeMixin):
if isinstance(obs, FileReadObservation): if isinstance(obs, FileReadObservation):
self.log('info', 'openhands_instructions microagent loaded.') self.log('info', 'openhands_instructions microagent loaded.')
loaded_microagents.append( loaded_microagents.append(
BaseMicroAgent.load( BaseMicroagent.load(
path='.openhands_instructions', file_content=obs.content path='.openhands_instructions', file_content=obs.content
) )
) )

View File

@@ -19,7 +19,7 @@ from openhands.events.stream import EventStream
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE, ProviderHandler from openhands.integrations.provider import PROVIDER_TOKEN_TYPE, ProviderHandler
from openhands.integrations.service_types import Repository from openhands.integrations.service_types import Repository
from openhands.memory.memory import Memory from openhands.memory.memory import Memory
from openhands.microagent.microagent import BaseMicroAgent from openhands.microagent.microagent import BaseMicroagent
from openhands.runtime import get_runtime_cls from openhands.runtime import get_runtime_cls
from openhands.runtime.base import Runtime from openhands.runtime.base import Runtime
from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime
@@ -407,7 +407,7 @@ class AgentSession:
memory.set_runtime_info(self.runtime) memory.set_runtime_info(self.runtime)
# loads microagents from repo/.openhands/microagents # loads microagents from repo/.openhands/microagents
microagents: list[BaseMicroAgent] = await call_sync_from_async( microagents: list[BaseMicroagent] = await call_sync_from_async(
self.runtime.get_microagents_from_selected_repo, self.runtime.get_microagents_from_selected_repo,
selected_repository.full_name if selected_repository else None, selected_repository.full_name if selected_repository else None,
) )

View File

@@ -7,7 +7,7 @@ from conftest import (
_load_runtime, _load_runtime,
) )
from openhands.microagent import KnowledgeMicroAgent, RepoMicroAgent, TaskMicroAgent from openhands.microagent import KnowledgeMicroagent, RepoMicroagent, TaskMicroagent
def _create_test_microagents(test_dir: str): def _create_test_microagents(test_dir: str):
@@ -85,10 +85,10 @@ def test_load_microagents_with_trailing_slashes(
# Verify all agents are loaded # Verify all agents are loaded
knowledge_agents = [ knowledge_agents = [
a for a in loaded_agents if isinstance(a, KnowledgeMicroAgent) a for a in loaded_agents if isinstance(a, KnowledgeMicroagent)
] ]
repo_agents = [a for a in loaded_agents if isinstance(a, RepoMicroAgent)] repo_agents = [a for a in loaded_agents if isinstance(a, RepoMicroagent)]
task_agents = [a for a in loaded_agents if isinstance(a, TaskMicroAgent)] task_agents = [a for a in loaded_agents if isinstance(a, TaskMicroagent)]
# Check knowledge agents # Check knowledge agents
assert len(knowledge_agents) == 1 assert len(knowledge_agents) == 1
@@ -128,10 +128,10 @@ def test_load_microagents_with_selected_repo(temp_dir, runtime_cls, run_as_openh
# Verify all agents are loaded # Verify all agents are loaded
knowledge_agents = [ knowledge_agents = [
a for a in loaded_agents if isinstance(a, KnowledgeMicroAgent) a for a in loaded_agents if isinstance(a, KnowledgeMicroagent)
] ]
repo_agents = [a for a in loaded_agents if isinstance(a, RepoMicroAgent)] repo_agents = [a for a in loaded_agents if isinstance(a, RepoMicroagent)]
task_agents = [a for a in loaded_agents if isinstance(a, TaskMicroAgent)] task_agents = [a for a in loaded_agents if isinstance(a, TaskMicroagent)]
# Check knowledge agents # Check knowledge agents
assert len(knowledge_agents) == 1 assert len(knowledge_agents) == 1
@@ -181,10 +181,10 @@ Repository-specific test instructions.
# Verify only repo agent is loaded # Verify only repo agent is loaded
knowledge_agents = [ knowledge_agents = [
a for a in loaded_agents if isinstance(a, KnowledgeMicroAgent) a for a in loaded_agents if isinstance(a, KnowledgeMicroagent)
] ]
repo_agents = [a for a in loaded_agents if isinstance(a, RepoMicroAgent)] repo_agents = [a for a in loaded_agents if isinstance(a, RepoMicroagent)]
task_agents = [a for a in loaded_agents if isinstance(a, TaskMicroAgent)] task_agents = [a for a in loaded_agents if isinstance(a, TaskMicroagent)]
assert len(knowledge_agents) == 0 assert len(knowledge_agents) == 0
assert len(repo_agents) == 1 assert len(repo_agents) == 1

View File

@@ -1,7 +1,7 @@
from pathlib import Path from pathlib import Path
from openhands.microagent.microagent import BaseMicroAgent, RepoMicroAgent from openhands.microagent.microagent import BaseMicroagent, RepoMicroagent
from openhands.microagent.types import MicroAgentType from openhands.microagent.types import MicroagentType
def test_load_markdown_without_frontmatter(): def test_load_markdown_without_frontmatter():
@@ -10,13 +10,13 @@ def test_load_markdown_without_frontmatter():
path = Path('test.md') path = Path('test.md')
# Load the agent from content # Load the agent from content
agent = BaseMicroAgent.load(path, content) agent = BaseMicroagent.load(path, content)
# Verify it's loaded as a repo agent with default values # Verify it's loaded as a repo agent with default values
assert isinstance(agent, RepoMicroAgent) assert isinstance(agent, RepoMicroagent)
assert agent.name == 'default' assert agent.name == 'default'
assert agent.content == content assert agent.content == content
assert agent.type == MicroAgentType.REPO_KNOWLEDGE assert agent.type == MicroagentType.REPO_KNOWLEDGE
assert agent.metadata.agent == 'CodeActAgent' assert agent.metadata.agent == 'CodeActAgent'
assert agent.metadata.version == '1.0.0' assert agent.metadata.version == '1.0.0'
@@ -29,16 +29,16 @@ def test_load_markdown_with_empty_frontmatter():
path = Path('test.md') path = Path('test.md')
# Load the agent from content # Load the agent from content
agent = BaseMicroAgent.load(path, content) agent = BaseMicroagent.load(path, content)
# Verify it's loaded as a repo agent with default values # Verify it's loaded as a repo agent with default values
assert isinstance(agent, RepoMicroAgent) assert isinstance(agent, RepoMicroagent)
assert agent.name == 'default' assert agent.name == 'default'
assert ( assert (
agent.content agent.content
== '# Test Content\nThis is a test markdown file with empty frontmatter.' == '# Test Content\nThis is a test markdown file with empty frontmatter.'
) )
assert agent.type == MicroAgentType.REPO_KNOWLEDGE assert agent.type == MicroagentType.REPO_KNOWLEDGE
assert agent.metadata.agent == 'CodeActAgent' assert agent.metadata.agent == 'CodeActAgent'
assert agent.metadata.version == '1.0.0' assert agent.metadata.version == '1.0.0'
@@ -53,16 +53,16 @@ This is a test markdown file with partial frontmatter."""
path = Path('test.md') path = Path('test.md')
# Load the agent from content # Load the agent from content
agent = BaseMicroAgent.load(path, content) agent = BaseMicroagent.load(path, content)
# Verify it uses provided name but default values for other fields # Verify it uses provided name but default values for other fields
assert isinstance(agent, RepoMicroAgent) assert isinstance(agent, RepoMicroagent)
assert agent.name == 'custom_name' assert agent.name == 'custom_name'
assert ( assert (
agent.content agent.content
== '# Test Content\nThis is a test markdown file with partial frontmatter.' == '# Test Content\nThis is a test markdown file with partial frontmatter.'
) )
assert agent.type == MicroAgentType.REPO_KNOWLEDGE assert agent.type == MicroagentType.REPO_KNOWLEDGE
assert agent.metadata.agent == 'CodeActAgent' assert agent.metadata.agent == 'CodeActAgent'
assert agent.metadata.version == '1.0.0' assert agent.metadata.version == '1.0.0'
@@ -80,15 +80,15 @@ This is a test markdown file with full frontmatter."""
path = Path('test.md') path = Path('test.md')
# Load the agent from content # Load the agent from content
agent = BaseMicroAgent.load(path, content) agent = BaseMicroagent.load(path, content)
# Verify all provided values are used # Verify all provided values are used
assert isinstance(agent, RepoMicroAgent) assert isinstance(agent, RepoMicroagent)
assert agent.name == 'test_agent' assert agent.name == 'test_agent'
assert ( assert (
agent.content agent.content
== '# Test Content\nThis is a test markdown file with full frontmatter.' == '# Test Content\nThis is a test markdown file with full frontmatter.'
) )
assert agent.type == MicroAgentType.REPO_KNOWLEDGE assert agent.type == MicroagentType.REPO_KNOWLEDGE
assert agent.metadata.agent == 'CustomAgent' assert agent.metadata.agent == 'CustomAgent'
assert agent.metadata.version == '2.0.0' assert agent.metadata.version == '2.0.0'

View File

@@ -5,14 +5,14 @@ from pathlib import Path
import pytest import pytest
from openhands.core.exceptions import MicroAgentValidationError from openhands.core.exceptions import MicroagentValidationError
from openhands.microagent import ( from openhands.microagent import (
BaseMicroAgent, BaseMicroagent,
KnowledgeMicroAgent, KnowledgeMicroagent,
MicroAgentMetadata, MicroagentMetadata,
MicroAgentType, MicroagentType,
RepoMicroAgent, RepoMicroagent,
TaskMicroAgent, TaskMicroagent,
load_microagents_from_dir, load_microagents_from_dir,
) )
@@ -26,11 +26,11 @@ def test_legacy_micro_agent_load(tmp_path):
legacy_file = tmp_path / '.openhands_instructions' legacy_file = tmp_path / '.openhands_instructions'
legacy_file.write_text(CONTENT) legacy_file.write_text(CONTENT)
micro_agent = BaseMicroAgent.load(legacy_file) micro_agent = BaseMicroagent.load(legacy_file)
assert isinstance(micro_agent, RepoMicroAgent) assert isinstance(micro_agent, RepoMicroagent)
assert micro_agent.name == 'repo_legacy' assert micro_agent.name == 'repo_legacy'
assert micro_agent.content == CONTENT assert micro_agent.content == CONTENT
assert micro_agent.type == MicroAgentType.REPO_KNOWLEDGE assert micro_agent.type == MicroagentType.REPO_KNOWLEDGE
@pytest.fixture @pytest.fixture
@@ -89,14 +89,14 @@ Test task content
def test_knowledge_agent(): def test_knowledge_agent():
"""Test knowledge agent functionality.""" """Test knowledge agent functionality."""
agent = KnowledgeMicroAgent( agent = KnowledgeMicroagent(
name='test', name='test',
content='Test content', content='Test content',
metadata=MicroAgentMetadata( metadata=MicroagentMetadata(
name='test', type=MicroAgentType.KNOWLEDGE, triggers=['test', 'pytest'] name='test', type=MicroagentType.KNOWLEDGE, triggers=['test', 'pytest']
), ),
source='test.md', source='test.md',
type=MicroAgentType.KNOWLEDGE, type=MicroagentType.KNOWLEDGE,
) )
assert agent.match_trigger('running a test') == 'test' assert agent.match_trigger('running a test') == 'test'
@@ -114,18 +114,18 @@ def test_load_microagents(temp_microagents_dir):
# Check knowledge agents # Check knowledge agents
assert len(knowledge_agents) == 1 assert len(knowledge_agents) == 1
agent = knowledge_agents['test_knowledge_agent'] agent = knowledge_agents['test_knowledge_agent']
assert isinstance(agent, KnowledgeMicroAgent) assert isinstance(agent, KnowledgeMicroagent)
assert 'test' in agent.triggers assert 'test' in agent.triggers
# Check repo agents # Check repo agents
assert len(repo_agents) == 1 assert len(repo_agents) == 1
agent = repo_agents['test_repo_agent'] agent = repo_agents['test_repo_agent']
assert isinstance(agent, RepoMicroAgent) assert isinstance(agent, RepoMicroagent)
# Check task agents # Check task agents
assert len(task_agents) == 1 assert len(task_agents) == 1
agent = task_agents['test_task'] agent = task_agents['test_task']
assert isinstance(agent, TaskMicroAgent) assert isinstance(agent, TaskMicroagent)
def test_invalid_agent_type(temp_microagents_dir): def test_invalid_agent_type(temp_microagents_dir):
@@ -141,8 +141,8 @@ Invalid agent content
""" """
(temp_microagents_dir / 'invalid.md').write_text(invalid_agent) (temp_microagents_dir / 'invalid.md').write_text(invalid_agent)
with pytest.raises(MicroAgentValidationError): with pytest.raises(MicroagentValidationError):
BaseMicroAgent.load(temp_microagents_dir / 'invalid.md') BaseMicroagent.load(temp_microagents_dir / 'invalid.md')
def test_load_microagents_with_nested_dirs(temp_microagents_dir): def test_load_microagents_with_nested_dirs(temp_microagents_dir):
@@ -172,7 +172,7 @@ Testing nested directory loading.
# Check that we can find the nested agent # Check that we can find the nested agent
assert len(knowledge_agents) == 2 # Original + nested assert len(knowledge_agents) == 2 # Original + nested
agent = knowledge_agents['nested_knowledge_agent'] agent = knowledge_agents['nested_knowledge_agent']
assert isinstance(agent, KnowledgeMicroAgent) assert isinstance(agent, KnowledgeMicroagent)
assert 'nested' in agent.triggers assert 'nested' in agent.triggers
@@ -203,5 +203,5 @@ Testing loading with trailing slashes.
# Check that we can find the agent despite trailing slashes # Check that we can find the agent despite trailing slashes
assert len(knowledge_agents) == 2 # Original + trailing assert len(knowledge_agents) == 2 # Original + trailing
agent = knowledge_agents['trailing_knowledge_agent'] agent = knowledge_agents['trailing_knowledge_agent']
assert isinstance(agent, KnowledgeMicroAgent) assert isinstance(agent, KnowledgeMicroagent)
assert 'trailing' in agent.triggers assert 'trailing' in agent.triggers

View File

@@ -6,7 +6,7 @@ import pytest
from openhands.controller.state.state import State from openhands.controller.state.state import State
from openhands.core.message import Message, TextContent from openhands.core.message import Message, TextContent
from openhands.events.observation.agent import MicroagentKnowledge from openhands.events.observation.agent import MicroagentKnowledge
from openhands.microagent import BaseMicroAgent from openhands.microagent import BaseMicroagent
from openhands.utils.prompt import PromptManager, RepositoryInfo, RuntimeInfo from openhands.utils.prompt import PromptManager, RepositoryInfo, RuntimeInfo
@@ -72,7 +72,7 @@ def test_prompt_manager_file_not_found(prompt_dir):
"""Test PromptManager behavior when a template file is not found.""" """Test PromptManager behavior when a template file is not found."""
# Test with a non-existent template # Test with a non-existent template
with pytest.raises(FileNotFoundError): with pytest.raises(FileNotFoundError):
BaseMicroAgent.load( BaseMicroagent.load(
os.path.join(prompt_dir, 'micro', 'non_existent_microagent.md') os.path.join(prompt_dir, 'micro', 'non_existent_microagent.md')
) )