SerenaConfig: Use configuration setting 'language_backend' instead of 'jetbrains'

introducing enum LanguageBackend
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-12-17 12:54:33 +01:00
1 parent 946b0e7d51
commit d410aa9f90
5 files changed
+70 -35

No files matched your search

-1
View File
@@ -25,7 +25,6 @@ if __name__ == "__main__":
serena_config = SerenaConfig.from_config_file()
serena_config.log_level = logging.INFO
serena_config.jetbrains = False
serena_config.gui_log_window_enabled = False
serena_config.web_dashboard = False
+7 -4
View File
@@ -18,7 +18,7 @@ from interprompt.jinja_template import JinjaTemplate
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, ToolInclusionDefinition
from serena.config.serena_config import LanguageBackend, SerenaConfig, ToolInclusionDefinition
from serena.dashboard import SerenaDashboardAPI
from serena.ls_manager import LanguageServerManager
from serena.project import Project
@@ -226,7 +226,10 @@ class SerenaAgent:
self._tool_usage_stats = ToolUsageStats(token_count_estimator)
# log fundamental information
log.info(f"Starting Serena server (version={serena_version()}, process id={os.getpid()}, parent process id={os.getppid()})")
log.info(
f"Starting Serena server (version={serena_version()}, process id={os.getpid()}, parent process id={os.getppid()}; "
f"language backend={self.serena_config.language_backend.name})"
)
log.info("Configuration file: %s", self.serena_config.config_file_path)
log.info("Available projects: {}".format(", ".join(self.serena_config.project_names)))
log.info(f"Loaded tools ({len(self._all_tools)}): {', '.join([tool.get_name_from_cls() for tool in self._all_tools.values()])}")
@@ -238,7 +241,7 @@ class SerenaAgent:
tool_inclusion_definitions: list[ToolInclusionDefinition] = [self.serena_config, self._context]
if self._context.single_project:
tool_inclusion_definitions.extend(self._single_project_context_tool_inclusion_definitions(project))
if self.serena_config.jetbrains:
if self.serena_config.language_backend == LanguageBackend.JETBRAINS:
tool_inclusion_definitions.append(SerenaAgentMode.from_name_internal("jetbrains"))
self._base_tool_set = ToolSet.default().apply(*tool_inclusion_definitions)
@@ -514,7 +517,7 @@ class SerenaAgent:
"""
:return: whether this agent uses language server-based code analysis
"""
return not self.serena_config.jetbrains
return self.serena_config.language_backend == LanguageBackend.LSP
def _activate_project(self, project: Project) -> None:
log.info(f"Activating {project.project_name} at {project.project_root}")
+51 -12
View File
@@ -9,6 +9,7 @@ from collections.abc import Iterable
from copy import deepcopy
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from functools import cached_property
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional, Self, TypeVar
@@ -106,6 +107,26 @@ def get_serena_managed_in_project_dir(project_root: str | Path) -> str:
return os.path.join(project_root, SERENA_MANAGED_DIR_NAME)
class LanguageBackend(Enum):
LSP = "LSP"
"""
Use the language server protocol (LSP), spawning freely available language servers
via the SolidLSP library that is part of Serena
"""
JETBRAINS = "JetBrains"
"""
Use the Serena plugin in your JetBrains IDE.
(requires the plugin to be installed and the project being worked on to be open in your IDE)
"""
@staticmethod
def from_str(backend_str: str) -> "LanguageBackend":
for backend in LanguageBackend:
if backend.value.lower() == backend_str.lower():
return backend
raise ValueError(f"Unknown language backend '{backend_str}': valid values are {[b.value for b in LanguageBackend]}")
@dataclass(kw_only=True)
class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
project_name: str
@@ -362,9 +383,10 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
the path to the configuration file to which updates of the configuration shall be saved;
if None, the configuration is not saved to disk
"""
jetbrains: bool = False
language_backend: LanguageBackend = LanguageBackend.LSP
"""
whether to apply JetBrains mode
the language backend to use for code understanding features
"""
token_count_estimator: str = RegisteredTokenCountEstimator.CHAR_COUNT.name
@@ -446,7 +468,7 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
# load list of known projects
instance.projects = []
num_project_migrations = 0
num_migrations = 0
for path in loaded_commented_yaml["projects"]:
path = Path(path).resolve()
if not path.exists() or (path.is_dir() and not (path / ProjectConfig.rel_path_to_project_yml()).exists()):
@@ -456,7 +478,7 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
path = cls._migrate_out_of_project_config_file(path)
if path is None:
continue
num_project_migrations += 1
num_migrations += 1
project_config = ProjectConfig.load(path)
project = RegisteredProject(
project_root=str(path),
@@ -469,7 +491,21 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
field_name = key
return loaded_commented_yaml.get(key, get_dataclass_default(SerenaConfig, field_name))
# set other configuration parameters
# determine language backend
language_backend = get_dataclass_default(SerenaConfig, "language_backend")
if "language_backend" in loaded_commented_yaml:
backend_str = loaded_commented_yaml["language_backend"]
language_backend = LanguageBackend.from_str(backend_str)
else:
# backward compatibility (migrate Boolean field "jetbrains")
if "jetbrains" in loaded_commented_yaml:
num_migrations += 1
if loaded_commented_yaml["jetbrains"]:
language_backend = LanguageBackend.JETBRAINS
del loaded_commented_yaml["jetbrains"]
instance.language_backend = language_backend
# set other configuration parameters (primitive types)
instance.gui_log_window_enabled = get_value_or_default("gui_log_window", "gui_log_window_enabled")
instance.web_dashboard_listen_address = get_value_or_default("web_dashboard_listen_address")
instance.log_level = loaded_commented_yaml.get("log_level", loaded_commented_yaml.get("gui_log_level", logging.INFO))
@@ -479,16 +515,13 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
instance.trace_lsp_communication = get_value_or_default("trace_lsp_communication")
instance.excluded_tools = get_value_or_default("excluded_tools")
instance.included_optional_tools = get_value_or_default("included_optional_tools")
instance.jetbrains = get_value_or_default("jetbrains")
instance.token_count_estimator = get_value_or_default("token_count_estimator")
instance.default_max_tool_answer_chars = get_value_or_default("default_max_tool_answer_chars")
instance.ls_specific_settings = get_value_or_default("ls_specific_settings")
# re-save the configuration file if any migrations were performed
if num_project_migrations > 0:
log.info(
f"Migrated {num_project_migrations} project configurations from legacy format to in-project configuration; re-saving configuration"
)
if num_migrations > 0:
log.info("Legacy configuration was migrated; re-saving configuration file")
instance.save()
return instance
@@ -591,9 +624,15 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
"""
if self.config_file_path is None:
return
assert self.loaded_commented_yaml is not None, "Cannot save configuration without loaded YAML"
loaded_original_yaml = deepcopy(self.loaded_commented_yaml)
# projects are unique absolute paths
# we also canonicalize them before saving
# convert project objects into list of paths
loaded_original_yaml["projects"] = sorted({str(project.project_root) for project in self.projects})
# convert language backend to string
loaded_original_yaml["language_backend"] = self.language_backend.value
save_yaml(self.config_file_path, loaded_original_yaml, preserve_comments=True)
+2 -1
View File
@@ -9,6 +9,7 @@ from pydantic import BaseModel
from sensai.util import logging
from serena.analytics import ToolUsageStats
from serena.config.serena_config import LanguageBackend
from serena.constants import SERENA_DASHBOARD_DIR
from serena.task_executor import TaskExecutor
from serena.util.logging import MemoryLogHandler
@@ -443,7 +444,7 @@ class SerenaDashboardAPI:
available_modes=available_modes,
available_contexts=available_contexts,
available_memories=available_memories,
jetbrains_mode=self._agent.serena_config.jetbrains,
jetbrains_mode=self._agent.serena_config.language_backend == LanguageBackend.JETBRAINS,
languages=languages,
encoding=encoding,
)
+10 -17
View File
@@ -1,3 +1,12 @@
language_backend: LSP
# the language backend to use for code understanding and manipulation.
# Possible values are:
# * LSP: Use the language server protocol (LSP), spawning freely available language servers
# via the SolidLSP library that is part of Serena.
# * JetBrains: Use the Serena plugin in your JetBrains IDE.
# (requires the plugin to be installed and the project being worked on to be open
# in your IDE).
gui_log_window: False
# whether to open a graphical window with Serena's logs.
# This is mainly supported on Windows and (partly) on Linux; not available on macOS.
@@ -42,7 +51,6 @@ ls_specific_settings: {}
# (same entry as in project.yml) to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
#
tool_timeout: 240
# timeout, in seconds, after which tool executions are terminated
@@ -53,12 +61,6 @@ excluded_tools: []
included_optional_tools: []
# list of optional tools (which are disabled by default) to be included
jetbrains: False
# whether to enable JetBrains mode and use tools based on the Serena JetBrains IDE plugin
# instead of language server-based tools
# NOTE: The plugin is yet unreleased. This is for Serena developers only.
default_max_tool_answer_chars: 150000
# Used as default for tools where the apply method has a default maximal answer length.
# Even though the value of the max_answer_chars can be changed when calling the tool, it may make sense to adjust this default
@@ -73,14 +75,5 @@ token_count_estimator: CHAR_COUNT
# data files on first run), or ANTHROPIC_CLAUDE_SONNET_4 which will use the (free of cost) Anthropic API to
# estimate the token count using the Claude Sonnet 4 tokenizer.
# MANAGED BY SERENA, KEEP AT THE BOTTOM OF THE YAML AND DON'T EDIT WITHOUT NEED
# The list of registered projects.
# To add a project, within a chat, simply ask Serena to "activate the project /path/to/project" or,
# if the project was previously added, "activate the project <project name>".
# By default, the project's name will be the name of the directory containing the project, but you may change it
# by editing the (auto-generated) project configuration file `/path/project/project/.serena/project.yml` file.
# If you want to maintain full control of the project configuration, create the project.yml file manually and then
# instruct Serena to activate the project by its path for first-time activation.
# NOTE: Make sure there are no name collisions in the names of registered projects.
# The list of registered project paths (updated automatically).
projects: []