mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 06:15:43 +00:00
Merge pull request #909 from cx-alberto-simoes/PR/FixIssueWithModeAndContextYamlFiles
Support --mode and --context with yaml files
This commit is contained in:
2 files changed
+76
-16
No files matched your search
@@ -41,6 +41,11 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
|
||||
It is formatted by the agent (see SerenaAgent._format_prompt()).
|
||||
"""
|
||||
description: str = ""
|
||||
_yaml_path: Path | None = field(default=None, repr=False, compare=False)
|
||||
"""
|
||||
Internal field storing the path to the YAML file this mode was loaded from.
|
||||
Used to support loading modes from arbitrary file paths.
|
||||
"""
|
||||
|
||||
def _tostring_includes(self) -> list[str]:
|
||||
return ["name"]
|
||||
@@ -54,14 +59,24 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
|
||||
@classmethod
|
||||
def from_yaml(cls, yaml_path: str | Path) -> Self:
|
||||
"""Load a mode from a YAML file."""
|
||||
with open(yaml_path, encoding=SERENA_FILE_ENCODING) as f:
|
||||
yaml_as_path = Path(yaml_path).resolve()
|
||||
with Path(yaml_as_path).open(encoding=SERENA_FILE_ENCODING) as f:
|
||||
data = yaml.safe_load(f)
|
||||
name = data.pop("name", Path(yaml_path).stem)
|
||||
return cls(name=name, **data)
|
||||
name = data.pop("name", yaml_as_path.stem)
|
||||
return cls(name=name, _yaml_path=yaml_as_path, **data)
|
||||
|
||||
@classmethod
|
||||
def get_path(cls, name: str) -> str:
|
||||
"""Get the path to the YAML file for a mode."""
|
||||
def get_path(cls, name: str, instance: Self | None = None) -> str:
|
||||
"""Get the path to the YAML file for a mode.
|
||||
|
||||
:param name: The name of the mode
|
||||
:param instance: Optional mode instance. If provided and it has a stored path, that path is returned.
|
||||
:return: The path to the mode's YAML file
|
||||
"""
|
||||
# If we have an instance with a stored path, use that
|
||||
if instance is not None and instance._yaml_path is not None:
|
||||
return str(instance._yaml_path)
|
||||
|
||||
fname = f"{name}.yml"
|
||||
custom_mode_path = os.path.join(SerenaPaths().user_modes_dir, fname)
|
||||
if os.path.exists(custom_mode_path):
|
||||
@@ -109,8 +124,16 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
|
||||
|
||||
@classmethod
|
||||
def load(cls, name_or_path: str | Path) -> Self:
|
||||
if str(name_or_path).endswith(".yml"):
|
||||
# Check if it's a file path that exists
|
||||
path = Path(name_or_path)
|
||||
if path.exists() and path.is_file():
|
||||
return cls.from_yaml(name_or_path)
|
||||
|
||||
# If it looks like a file path but doesn't exist, raise FileNotFoundError
|
||||
name_or_path_str = str(name_or_path)
|
||||
if os.sep in name_or_path_str or (os.altsep and os.altsep in name_or_path_str) or name_or_path_str.endswith((".yml", ".yaml")):
|
||||
raise FileNotFoundError(f"Mode file not found: {path.resolve()}")
|
||||
|
||||
return cls.from_name(str(name_or_path))
|
||||
|
||||
|
||||
@@ -137,6 +160,12 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
maps tool names to custom descriptions, default descriptions are extracted from the tool docstrings.
|
||||
"""
|
||||
|
||||
_yaml_path: Path | None = field(default=None, repr=False, compare=False)
|
||||
"""
|
||||
Internal field storing the path to the YAML file this context was loaded from.
|
||||
Used to support loading contexts from arbitrary file paths.
|
||||
"""
|
||||
|
||||
single_project: bool = False
|
||||
"""
|
||||
whether to assume that Serena shall only work on a single project in this context (provided that a project is given
|
||||
@@ -152,17 +181,27 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
@classmethod
|
||||
def from_yaml(cls, yaml_path: str | Path) -> Self:
|
||||
"""Load a context from a YAML file."""
|
||||
with open(yaml_path, encoding=SERENA_FILE_ENCODING) as f:
|
||||
yaml_as_path = Path(yaml_path).resolve()
|
||||
with yaml_as_path.open(encoding=SERENA_FILE_ENCODING) as f:
|
||||
data = yaml.safe_load(f)
|
||||
name = data.pop("name", Path(yaml_path).stem)
|
||||
name = data.pop("name", yaml_as_path.stem)
|
||||
# Ensure backwards compatibility for tool_description_overrides
|
||||
if "tool_description_overrides" not in data:
|
||||
data["tool_description_overrides"] = {}
|
||||
return cls(name=name, **data)
|
||||
return cls(name=name, _yaml_path=yaml_as_path, **data)
|
||||
|
||||
@classmethod
|
||||
def get_path(cls, name: str) -> str:
|
||||
"""Get the path to the YAML file for a context."""
|
||||
def get_path(cls, name: str, instance: Self | None = None) -> str:
|
||||
"""Get the path to the YAML file for a context.
|
||||
|
||||
:param name: The name of the context
|
||||
:param instance: Optional context instance. If provided and it has a stored path, that path is returned.
|
||||
:return: The path to the context's YAML file
|
||||
"""
|
||||
# If we have an instance with a stored path, use that
|
||||
if instance is not None and instance._yaml_path is not None:
|
||||
return str(instance._yaml_path)
|
||||
|
||||
fname = f"{name}.yml"
|
||||
custom_context_path = os.path.join(SerenaPaths().user_contexts_dir, fname)
|
||||
if os.path.exists(custom_context_path):
|
||||
@@ -194,8 +233,16 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
|
||||
@classmethod
|
||||
def load(cls, name_or_path: str | Path) -> Self:
|
||||
if str(name_or_path).endswith(".yml"):
|
||||
# Check if it's a file path that exists
|
||||
path = Path(name_or_path)
|
||||
if path.exists() and path.is_file():
|
||||
return cls.from_yaml(name_or_path)
|
||||
|
||||
# If it looks like a file path but doesn't exist, raise FileNotFoundError
|
||||
name_or_path_str = str(name_or_path)
|
||||
if os.sep in name_or_path_str or (os.altsep and os.altsep in name_or_path_str) or name_or_path_str.endswith((".yml", ".yaml")):
|
||||
raise FileNotFoundError(f"Context file not found: {path.resolve()}")
|
||||
|
||||
return cls.from_name(str(name_or_path))
|
||||
|
||||
@classmethod
|
||||
|
||||
+17
-4
@@ -354,12 +354,15 @@ class SerenaDashboardAPI:
|
||||
context_info = {
|
||||
"name": context.name,
|
||||
"description": context.description,
|
||||
"path": SerenaAgentContext.get_path(context.name),
|
||||
"path": SerenaAgentContext.get_path(context.name, instance=context),
|
||||
}
|
||||
|
||||
# Get active modes
|
||||
modes = self._agent.get_active_modes()
|
||||
modes_info = [{"name": mode.name, "description": mode.description, "path": SerenaAgentMode.get_path(mode.name)} for mode in modes]
|
||||
modes_info = [
|
||||
{"name": mode.name, "description": mode.description, "path": SerenaAgentMode.get_path(mode.name, instance=mode)}
|
||||
for mode in modes
|
||||
]
|
||||
active_mode_names = [mode.name for mode in modes]
|
||||
|
||||
# Get active tools
|
||||
@@ -392,11 +395,16 @@ class SerenaDashboardAPI:
|
||||
all_mode_names = SerenaAgentMode.list_registered_mode_names()
|
||||
available_modes: list[dict[str, str | bool]] = []
|
||||
for mode_name in all_mode_names:
|
||||
try:
|
||||
mode_path = SerenaAgentMode.get_path(mode_name)
|
||||
except FileNotFoundError:
|
||||
# Skip modes that can't be found (shouldn't happen for registered modes)
|
||||
continue
|
||||
available_modes.append(
|
||||
{
|
||||
"name": mode_name,
|
||||
"is_active": mode_name in active_mode_names,
|
||||
"path": SerenaAgentMode.get_path(mode_name),
|
||||
"path": mode_path,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -404,11 +412,16 @@ class SerenaDashboardAPI:
|
||||
all_context_names = SerenaAgentContext.list_registered_context_names()
|
||||
available_contexts: list[dict[str, str | bool]] = []
|
||||
for context_name in all_context_names:
|
||||
try:
|
||||
context_path = SerenaAgentContext.get_path(context_name)
|
||||
except FileNotFoundError:
|
||||
# Skip contexts that can't be found (shouldn't happen for registered contexts)
|
||||
continue
|
||||
available_contexts.append(
|
||||
{
|
||||
"name": context_name,
|
||||
"is_active": context_name == context.name,
|
||||
"path": SerenaAgentContext.get_path(context_name),
|
||||
"path": context_path,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user