mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 22:16:23 +00:00
feat: add ignored_memory_patterns config option for regex-based memory exclusion (#1145)
Co-authored-by: Bokai Yang <22804145+YouJianFengXue@users.noreply.github.com> Co-authored-by: Michael Panchenko <35432522+MischaPanch@users.noreply.github.com>
This commit is contained in:
6 files changed
+79
-2
No files matched your search
@@ -2,6 +2,13 @@
|
||||
|
||||
Status of the `main` branch. Changes prior to the next official version change will appear here.
|
||||
|
||||
* Memories:
|
||||
* Add `ignored_memory_patterns` configuration option (regex-based) to completely exclude
|
||||
matching memories from listing, reading, and writing. Configurable at both global
|
||||
(`serena_config.yml`) and project (`project.yml`) level, merged additively.
|
||||
Useful for projects with large numbers of archived memory files that inflate
|
||||
`activate_project` output.
|
||||
|
||||
* New language support:
|
||||
* Add Solidity language server support (`Language.SOLIDITY`) using the
|
||||
Nomic Foundation `@nomicfoundation/solidity-language-server`. Automatically
|
||||
|
||||
@@ -39,6 +39,22 @@ Since global memories are not versioned alongside your project files,
|
||||
it can be helpful to track global memories with git (i.e. to make `~/.serena/memories/` a git repository)
|
||||
in order to have a history of changes and the possibility to revert them if needed.
|
||||
|
||||
### Ignoring Memories
|
||||
|
||||
Projects that accumulate large numbers of archived memory files can use `ignored_memory_patterns`
|
||||
to exclude them from `list_memories` and `activate_project` output. Add regex patterns to the
|
||||
global or project-level [configuration](050_configuration):
|
||||
|
||||
```yaml
|
||||
ignored_memory_patterns: ["_archive/.*", "_episodes/.*"]
|
||||
```
|
||||
|
||||
Ignored memories are completely excluded — they cannot be accessed via `read_memory`, `write_memory`,
|
||||
or any other memory tool. To read an ignored memory file, use the `read_file` tool on the raw file path
|
||||
(e.g., `.serena/memories/_archive/2026-03/some-topic.md`).
|
||||
|
||||
Like `read_only_memory_patterns`, patterns from the global and project-level configurations are merged additively.
|
||||
|
||||
### Manually Editing Memories
|
||||
|
||||
You may edit memories directly in the file system, using your preferred text editor or IDE.
|
||||
|
||||
@@ -222,6 +222,7 @@ class SharedConfig(ModeSelectionDefinition, ToolInclusionDefinition, ToStringMix
|
||||
language_backend: LanguageBackend | None = None
|
||||
line_ending: LineEnding | None = None
|
||||
read_only_memory_patterns: list[str] = field(default_factory=list)
|
||||
ignored_memory_patterns: list[str] = field(default_factory=list)
|
||||
ls_specific_settings: dict = field(default_factory=dict)
|
||||
"""Advanced configuration option allowing to configure language server implementation specific options, see SolidLSPSettings for more info."""
|
||||
|
||||
@@ -453,6 +454,7 @@ class ProjectConfig(SharedConfig):
|
||||
included_optional_tools=data["included_optional_tools"],
|
||||
read_only=data["read_only"],
|
||||
read_only_memory_patterns=data.get("read_only_memory_patterns", []),
|
||||
ignored_memory_patterns=data.get("ignored_memory_patterns", []),
|
||||
ignore_all_files_in_gitignore=data["ignore_all_files_in_gitignore"],
|
||||
initial_prompt=data["initial_prompt"],
|
||||
encoding=data["encoding"],
|
||||
|
||||
+37
-2
@@ -35,10 +35,18 @@ class MemoriesManager:
|
||||
GLOBAL_TOPIC = "global"
|
||||
_global_memory_dir = SerenaPaths().global_memories_path
|
||||
|
||||
def __init__(self, serena_data_folder: str | Path | None, read_only_memory_patterns: Sequence[str] = ()):
|
||||
def __init__(
|
||||
self,
|
||||
serena_data_folder: str | Path | None,
|
||||
read_only_memory_patterns: Sequence[str] = (),
|
||||
ignored_memory_patterns: Sequence[str] = (),
|
||||
):
|
||||
"""
|
||||
:param serena_data_folder: the absolute path to the project's .serena data folder
|
||||
:param read_only_memory_patterns: whether to allow writing global memories in tool execution contexts
|
||||
:param ignored_memory_patterns: regex patterns for memories to completely exclude from listing, reading, and writing.
|
||||
Matching memories will not appear in list_memories or activate_project output and cannot be accessed
|
||||
via read_memory or write_memory. Use read_file on the raw path to access ignored memory files.
|
||||
"""
|
||||
self._project_memory_dir: Path | None = None
|
||||
if serena_data_folder is not None:
|
||||
@@ -46,6 +54,7 @@ class MemoriesManager:
|
||||
self._project_memory_dir.mkdir(parents=True, exist_ok=True)
|
||||
self._encoding = SERENA_FILE_ENCODING
|
||||
self._read_only_memory_patterns = [re.compile(pattern) for pattern in set(read_only_memory_patterns)]
|
||||
self._ignored_memory_patterns = [re.compile(pattern) for pattern in set(ignored_memory_patterns)]
|
||||
|
||||
def _is_read_only_memory(self, name: str) -> bool:
|
||||
for pattern in self._read_only_memory_patterns:
|
||||
@@ -53,6 +62,19 @@ class MemoriesManager:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_ignored_memory(self, name: str) -> bool:
|
||||
for pattern in self._ignored_memory_patterns:
|
||||
if pattern.fullmatch(name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _check_not_ignored(self, name: str) -> None:
|
||||
if self._is_ignored_memory(name):
|
||||
raise ValueError(
|
||||
f"Memory '{name}' matches an ignored_memory_patterns pattern and cannot be accessed. "
|
||||
f"Use the read_file tool on the raw file path instead."
|
||||
)
|
||||
|
||||
def _is_global(self, name: str) -> bool:
|
||||
return name == self.GLOBAL_TOPIC or name.startswith(self.GLOBAL_TOPIC + "/")
|
||||
|
||||
@@ -95,6 +117,7 @@ class MemoriesManager:
|
||||
raise PermissionError(f"Attempted to write to read_only memory: '{name}')")
|
||||
|
||||
def load_memory(self, name: str) -> str:
|
||||
self._check_not_ignored(name)
|
||||
memory_file_path = self.get_memory_file_path(name)
|
||||
if not memory_file_path.exists():
|
||||
return f"Memory file {name} not found, consider creating it with the `write_memory` tool if you need it."
|
||||
@@ -102,6 +125,7 @@ class MemoriesManager:
|
||||
return f.read()
|
||||
|
||||
def save_memory(self, name: str, content: str, is_tool_context: bool) -> str:
|
||||
self._check_not_ignored(name)
|
||||
self._check_write_access(name, is_tool_context)
|
||||
memory_file_path = self.get_memory_file_path(name)
|
||||
with open(memory_file_path, "w", encoding=self._encoding) as f:
|
||||
@@ -144,6 +168,8 @@ class MemoriesManager:
|
||||
for md_file in search_dir.rglob("*.md"):
|
||||
rel = str(md_file.relative_to(base_dir).with_suffix("")).replace(os.sep, "/")
|
||||
memory_name = prefix + rel
|
||||
if self._is_ignored_memory(memory_name):
|
||||
continue
|
||||
result.add(memory_name, is_read_only=self._is_read_only_memory(memory_name))
|
||||
return result
|
||||
|
||||
@@ -181,6 +207,7 @@ class MemoriesManager:
|
||||
return memories
|
||||
|
||||
def delete_memory(self, name: str, is_tool_context: bool) -> str:
|
||||
self._check_not_ignored(name)
|
||||
self._check_write_access(name, is_tool_context)
|
||||
memory_file_path = self.get_memory_file_path(name)
|
||||
if not memory_file_path.exists():
|
||||
@@ -193,6 +220,8 @@ class MemoriesManager:
|
||||
Rename or move a memory file.
|
||||
Moving between global and project scope (e.g. "global/foo" -> "bar") is supported.
|
||||
"""
|
||||
self._check_not_ignored(old_name)
|
||||
self._check_not_ignored(new_name)
|
||||
self._check_write_access(new_name, is_tool_context)
|
||||
|
||||
old_path = self.get_memory_file_path(old_name)
|
||||
@@ -220,6 +249,7 @@ class MemoriesManager:
|
||||
:param mode: "literal" or "regex"
|
||||
:param allow_multiple_occurrences:
|
||||
"""
|
||||
self._check_not_ignored(name)
|
||||
self._check_write_access(name, is_tool_context)
|
||||
memory_file_path = self.get_memory_file_path(name)
|
||||
if not memory_file_path.exists():
|
||||
@@ -250,7 +280,12 @@ class Project(ToStringMixin):
|
||||
log.info("Serena project data folder: %s", self._serena_data_folder)
|
||||
|
||||
read_only_memory_patterns = serena_config.read_only_memory_patterns + project_config.read_only_memory_patterns
|
||||
self.memories_manager = MemoriesManager(self._serena_data_folder, read_only_memory_patterns=read_only_memory_patterns)
|
||||
ignored_memory_patterns = serena_config.ignored_memory_patterns + project_config.ignored_memory_patterns
|
||||
self.memories_manager = MemoriesManager(
|
||||
self._serena_data_folder,
|
||||
read_only_memory_patterns=read_only_memory_patterns,
|
||||
ignored_memory_patterns=ignored_memory_patterns,
|
||||
)
|
||||
|
||||
# resolve line ending (project -> global)
|
||||
self.line_ending = project_config.line_ending or serena_config.line_ending
|
||||
|
||||
@@ -141,3 +141,11 @@ symbol_info_budget:
|
||||
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
||||
# Extends the list from the global configuration, merging the two lists.
|
||||
read_only_memory_patterns: []
|
||||
|
||||
# list of regex patterns for memories to completely ignore.
|
||||
# Matching memories will not appear in list_memories or activate_project output
|
||||
# and cannot be accessed via read_memory or write_memory.
|
||||
# To access ignored memory files, use the read_file tool on the raw file path.
|
||||
# Extends the list from the global configuration, merging the two lists.
|
||||
# Example: ["_archive/.*", "_episodes/.*"]
|
||||
ignored_memory_patterns: []
|
||||
@@ -79,6 +79,15 @@ ignored_paths: []
|
||||
# You can extend the list on a per-project basis in the project.yml configuration file.
|
||||
read_only_memory_patterns: []
|
||||
|
||||
# list of regex patterns for memories to completely ignore.
|
||||
# Matching memories will not appear in list_memories or activate_project output
|
||||
# and cannot be accessed via read_memory or write_memory.
|
||||
# To access ignored memory files, use the read_file tool on the raw file path.
|
||||
# This is useful for projects with large numbers of archived memory files.
|
||||
# You can extend the list on a per-project basis in the project.yml configuration file.
|
||||
# Example: ["_archive/.*", "_episodes/.*"]
|
||||
ignored_memory_patterns: []
|
||||
|
||||
# timeout, in seconds, after which tool executions are terminated
|
||||
tool_timeout: 240
|
||||
|
||||
|
||||
Reference in new issue
Block a user