mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 12:15:10 +00:00
Possibility to load modes and contexts by name from $HOME/.serena
This commit is contained in:
1 parent
cad4df59cc
commit
e596319492
4 files changed
+60
-21
No files matched your search
+2
-2
@@ -21,7 +21,7 @@ from sensai.util.logging import LogTime
|
||||
from serena import serena_version
|
||||
from serena.analytics import RegisteredTokenCountEstimator, ToolUsageStats
|
||||
from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode
|
||||
from serena.config.serena_config import SerenaConfig, ToolSet, get_serena_managed_dir
|
||||
from serena.config.serena_config import SerenaConfig, ToolSet, get_serena_managed_in_project_dir
|
||||
from serena.constants import (
|
||||
SERENA_LOG_FORMAT,
|
||||
)
|
||||
@@ -62,7 +62,7 @@ class LinesRead:
|
||||
|
||||
class MemoriesManager:
|
||||
def __init__(self, project_root: str):
|
||||
self._memory_dir = Path(get_serena_managed_dir(project_root)) / "memories"
|
||||
self._memory_dir = Path(get_serena_managed_in_project_dir(project_root)) / "memories"
|
||||
self._memory_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def _get_memory_file_path(self, name: str) -> Path:
|
||||
|
||||
@@ -14,7 +14,15 @@ from sensai.util import logging
|
||||
from sensai.util.string import ToStringMixin
|
||||
|
||||
from serena.config.serena_config import ToolInclusionDefinition
|
||||
from serena.constants import CONTEXT_YAMLS_DIR, DEFAULT_CONTEXT, DEFAULT_MODES, INTERNAL_MODE_YAMLS_DIR, MODE_YAMLS_DIR
|
||||
from serena.constants import (
|
||||
DEFAULT_CONTEXT,
|
||||
DEFAULT_MODES,
|
||||
INTERNAL_MODE_YAMLS_DIR,
|
||||
SERENAS_OWN_CONTEXT_YAMLS_DIR,
|
||||
SERENAS_OWN_MODE_YAMLS_DIR,
|
||||
USER_CONTEXT_YAMLS_DIR,
|
||||
USER_MODE_YAMLS_DIR,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
@@ -64,13 +72,19 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
|
||||
@classmethod
|
||||
def from_name(cls, name: str) -> Self:
|
||||
"""Load a registered Serena mode."""
|
||||
yaml_path = os.path.join(MODE_YAMLS_DIR, f"{name}.yml")
|
||||
if not os.path.exists(yaml_path):
|
||||
fname = f"{name}.yml"
|
||||
custom_mode_path = os.path.join(USER_MODE_YAMLS_DIR, fname)
|
||||
if os.path.exists(custom_mode_path):
|
||||
log.info(f"Loading custom mode {name} from {custom_mode_path}")
|
||||
return cls.from_yaml(custom_mode_path)
|
||||
|
||||
own_yaml_path = os.path.join(SERENAS_OWN_MODE_YAMLS_DIR, fname)
|
||||
if not os.path.exists(own_yaml_path):
|
||||
raise FileNotFoundError(
|
||||
f"Mode {name} not found in {MODE_YAMLS_DIR}. You can load custom modes by using from_yaml() instead. "
|
||||
f"Mode {name} not found in {USER_MODE_YAMLS_DIR} or in {SERENAS_OWN_MODE_YAMLS_DIR}. You can load custom modes by using from_yaml() instead. "
|
||||
f"Available modes: {cls.list_registered_mode_names()}"
|
||||
)
|
||||
return cls.from_yaml(yaml_path)
|
||||
return cls.from_yaml(own_yaml_path)
|
||||
|
||||
@classmethod
|
||||
def from_name_internal(cls, name: str) -> Self:
|
||||
@@ -81,9 +95,12 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
|
||||
return cls.from_yaml(yaml_path)
|
||||
|
||||
@classmethod
|
||||
def list_registered_mode_names(cls) -> list[str]:
|
||||
def list_registered_mode_names(cls, include_user_modes: bool = True) -> list[str]:
|
||||
"""Names of all registered modes (from the corresponding YAML files in the serena repo)."""
|
||||
return sorted([f.stem for f in Path(MODE_YAMLS_DIR).glob("*.yml")])
|
||||
modes = [f.stem for f in Path(SERENAS_OWN_MODE_YAMLS_DIR).glob("*.yml")]
|
||||
if include_user_modes:
|
||||
modes += [f.stem for f in Path(USER_MODE_YAMLS_DIR).glob("*.yml")]
|
||||
return sorted(set(modes))
|
||||
|
||||
@classmethod
|
||||
def load_default_modes(cls) -> list[Self]:
|
||||
@@ -134,13 +151,20 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
@classmethod
|
||||
def from_name(cls, name: str) -> Self:
|
||||
"""Load a registered Serena context."""
|
||||
yaml_path = os.path.join(CONTEXT_YAMLS_DIR, f"{name}.yml")
|
||||
if not os.path.exists(yaml_path):
|
||||
fname = f"{name}.yml"
|
||||
custom_context_path = os.path.join(USER_CONTEXT_YAMLS_DIR, fname)
|
||||
if os.path.exists(custom_context_path):
|
||||
log.info(f"Loading custom context {name} from {custom_context_path}")
|
||||
return cls.from_yaml(custom_context_path)
|
||||
|
||||
own_yaml_path = os.path.join(SERENAS_OWN_CONTEXT_YAMLS_DIR, fname)
|
||||
if not os.path.exists(own_yaml_path):
|
||||
raise FileNotFoundError(
|
||||
f"Context {Path(yaml_path).stem} not found in {CONTEXT_YAMLS_DIR}. You can load a custom context by using from_yaml() instead.\n"
|
||||
f"Context {Path(own_yaml_path).stem} not found in {USER_CONTEXT_YAMLS_DIR} or in {SERENAS_OWN_CONTEXT_YAMLS_DIR}. "
|
||||
f"You can load a custom context by using from_yaml() instead.\n"
|
||||
f"Available contexts:\n{cls.list_registered_context_names()}"
|
||||
)
|
||||
return cls.from_yaml(yaml_path)
|
||||
return cls.from_yaml(own_yaml_path)
|
||||
|
||||
@classmethod
|
||||
def load(cls, name_or_path: str | Path) -> Self:
|
||||
@@ -151,14 +175,17 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
return cls.from_yaml(name_or_path)
|
||||
except FileNotFoundError as e:
|
||||
raise FileNotFoundError(
|
||||
f"Context {name_or_path} not found in {CONTEXT_YAMLS_DIR}. You can load a custom context by using from_yaml() instead.\n"
|
||||
f"Context {name_or_path} not found in {SERENAS_OWN_CONTEXT_YAMLS_DIR}. You can load a custom context by using from_yaml() instead.\n"
|
||||
f"Available contexts:\n{cls.list_registered_context_names()}"
|
||||
) from e
|
||||
|
||||
@classmethod
|
||||
def list_registered_context_names(cls) -> list[str]:
|
||||
def list_registered_context_names(cls, include_user_contexts: bool = True) -> list[str]:
|
||||
"""Names of all registered contexts (from the corresponding YAML files in the serena repo)."""
|
||||
return sorted([f.stem for f in Path(CONTEXT_YAMLS_DIR).glob("*.yml")])
|
||||
contexts = [f.stem for f in Path(SERENAS_OWN_CONTEXT_YAMLS_DIR).glob("*.yml")]
|
||||
if include_user_contexts:
|
||||
contexts += [f.stem for f in Path(USER_CONTEXT_YAMLS_DIR).glob("*.yml")]
|
||||
return sorted(set(contexts))
|
||||
|
||||
@classmethod
|
||||
def load_default(cls) -> Self:
|
||||
|
||||
@@ -21,6 +21,7 @@ from serena.constants import (
|
||||
PROJECT_TEMPLATE_FILE,
|
||||
REPO_ROOT,
|
||||
SELENA_CONFIG_TEMPLATE_FILE,
|
||||
SERENA_MANAGED_DIR_IN_HOME,
|
||||
SERENA_MANAGED_DIR_NAME,
|
||||
)
|
||||
from serena.util.general import load_yaml, save_yaml
|
||||
@@ -113,7 +114,7 @@ class SerenaConfigError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_serena_managed_dir(project_root: str | Path) -> str:
|
||||
def get_serena_managed_in_project_dir(project_root: str | Path) -> str:
|
||||
return os.path.join(project_root, SERENA_MANAGED_DIR_NAME)
|
||||
|
||||
|
||||
@@ -293,7 +294,7 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
|
||||
if is_running_in_docker():
|
||||
return os.path.join(REPO_ROOT, cls.CONFIG_FILE_DOCKER)
|
||||
else:
|
||||
config_path = str(Path.home() / SERENA_MANAGED_DIR_NAME / cls.CONFIG_FILE)
|
||||
config_path = os.path.join(SERENA_MANAGED_DIR_IN_HOME, cls.CONFIG_FILE)
|
||||
|
||||
# if the config file does not exist, check if we can migrate it from the old location
|
||||
if not os.path.exists(config_path):
|
||||
|
||||
+14
-3
@@ -3,15 +3,26 @@ from pathlib import Path
|
||||
_repo_root_path = Path(__file__).parent.parent.parent.resolve()
|
||||
_serena_pkg_path = Path(__file__).parent.resolve()
|
||||
|
||||
SERENA_MANAGED_DIR_NAME = ".serena"
|
||||
_serena_in_home_managed_dir = Path.home() / ".serena"
|
||||
|
||||
SERENA_MANAGED_DIR_IN_HOME = str(_serena_in_home_managed_dir)
|
||||
|
||||
REPO_ROOT = str(_repo_root_path)
|
||||
PROMPT_TEMPLATES_DIR = str(_serena_pkg_path / "resources" / "config" / "prompt_templates")
|
||||
CONTEXT_YAMLS_DIR = str(_serena_pkg_path / "resources" / "config" / "contexts")
|
||||
MODE_YAMLS_DIR = str(_serena_pkg_path / "resources" / "config" / "modes")
|
||||
SERENAS_OWN_CONTEXT_YAMLS_DIR = str(_serena_pkg_path / "resources" / "config" / "contexts")
|
||||
"""The contexts that are shipped with the Serena package, i.e. the default contexts."""
|
||||
USER_CONTEXT_YAMLS_DIR = str(_serena_in_home_managed_dir / "contexts")
|
||||
"""Contexts defined by the user. If a name of a context matches a name of a context in SERENAS_OWN_CONTEXT_YAMLS_DIR, the user context will override the default one."""
|
||||
SERENAS_OWN_MODE_YAMLS_DIR = str(_serena_pkg_path / "resources" / "config" / "modes")
|
||||
"""The modes that are shipped with the Serena package, i.e. the default modes."""
|
||||
USER_MODE_YAMLS_DIR = str(_serena_in_home_managed_dir / "modes")
|
||||
"""Modes defined by the user. If a name of a mode matches a name of a mode in SERENAS_OWN_MODE_YAMLS_DIR, the user mode will override the default one."""
|
||||
INTERNAL_MODE_YAMLS_DIR = str(_serena_pkg_path / "resources" / "config" / "internal_modes")
|
||||
"""Internal modes, never overridden by user modes."""
|
||||
SERENA_DASHBOARD_DIR = str(_serena_pkg_path / "resources" / "dashboard")
|
||||
SERENA_ICON_DIR = str(_serena_pkg_path / "resources" / "icons")
|
||||
|
||||
SERENA_MANAGED_DIR_NAME = ".serena"
|
||||
|
||||
DEFAULT_ENCODING = "utf-8"
|
||||
DEFAULT_CONTEXT = "desktop-app"
|
||||
|
||||
Reference in new issue
Block a user