Buncha small fixes

This commit is contained in:
Michael Panchenko
2025-05-23 18:22:28 +02:00
parent f85aabb40e
commit 87326d9dcd
6 changed files with 60 additions and 37 deletions
+1 -1
View File
@@ -12,4 +12,4 @@ prompt: |
excluded_tools:
- get_current_config
- activate_project
- activate_modes
- switch_modes
+32 -15
View File
@@ -321,8 +321,8 @@ class SerenaAgent:
context = SerenaAgentContext.load_default()
if modes is None:
modes = SerenaAgentMode.load_default_modes()
self.context = context
self.modes = modes
self._context = context
self._modes = modes
self._update_active_tools()
log.info(f"Loaded tools ({len(self._all_tools)}): {', '.join([tool.get_name() for tool in self._all_tools.values()])}")
@@ -368,15 +368,21 @@ class SerenaAgent:
:param modes: List of mode names or paths to use
"""
self.current_modes = modes
self._modes = modes
self._update_active_tools()
log.info(f"Set modes to {[mode.name for mode in modes]}")
def get_active_modes(self) -> list[SerenaAgentMode]:
"""
:return: the list of active modes
"""
return list(self._modes)
def create_system_prompt(self) -> str:
return self.prompt_factory.create_system_prompt(
context_system_prompt=self.context.prompt,
mode_system_prompts=[mode.prompt for mode in self.current_modes],
context_system_prompt=self._context.prompt,
mode_system_prompts=[mode.prompt for mode in self._modes],
)
def _update_active_tools(self) -> None:
@@ -390,9 +396,9 @@ class SerenaAgent:
"""
# Collect all excluded tools with the desired priority mode < context < project
excluded_tool_classes: set[type[Tool]] = set()
for mode in self.current_modes:
for mode in self._modes:
excluded_tool_classes.update(mode.get_excluded_tool_classes())
excluded_tool_classes.update(self.context.get_excluded_tool_classes())
excluded_tool_classes.update(self._context.get_excluded_tool_classes())
if self.project_config is not None:
excluded_tool_classes.update(self.project_config.get_excluded_tool_classes())
@@ -440,6 +446,24 @@ class SerenaAgent:
"""
return sorted([tool.get_name() for tool in self._active_tools.values()])
def get_current_config_overview(self) -> str:
"""
:return: a string overview of the current configuration, including the active project, context, modes, and tools
"""
result_str = "Current configuration:\n"
if self.project_config is not None:
result_str += f"Active project: {self.project_config.project_name}\n"
result_str += f"Active context: {self._context.name}\n"
result_str += "Active modes: {}\n".format(", ".join([mode.name for mode in self.get_active_modes()]))
result_str += "Active tools (after all exclusions from the project, context, and modes):\n"
active_tool_names = self.get_active_tool_names()
# print the tool names in chunks
chunk_size = 4
for i in range(0, len(active_tool_names), chunk_size):
chunk = active_tool_names[i : i + chunk_size]
result_str += " " + ", ".join(chunk) + "\n"
return result_str
def is_language_server_running(self) -> bool:
return self.language_server is not None and self.language_server.is_running()
@@ -1528,14 +1552,7 @@ class GetCurrentConfigTool(Tool):
"""
Print the current configuration of the agent, including the active modes, tools, and context.
"""
result_str = "Current configuration:\n"
if self.agent.project_config is not None:
result_str += f"Active project: {self.agent.project_config.project_name}\n"
result_str += f"Active context: {self.agent.context.name}\n"
result_str += "Active modes: {}\n".format(", ".join([mode.name for mode in self.agent.current_modes]))
result_str += "Active tools (exclusions from the project, context, and modes):\n"
result_str += "\n".join(self.agent.get_active_tool_names())
return result_str
return self.agent.get_current_config_overview()
class InitialInstructionsTool(Tool):
+5 -5
View File
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Self
import yaml
from sensai.util import logging
from serena.constants import CONTEXT_YAMLS_DIR, MODE_YAMLS_DIR
from serena.constants import CONTEXT_YAMLS_DIR, DEFAULT_CONTEXT, DEFAULT_MODES, MODE_YAMLS_DIR
if TYPE_CHECKING:
from serena.agent import Tool
@@ -69,7 +69,7 @@ class SerenaAgentMode:
@classmethod
def load_default_modes(cls) -> list[Self]:
"""Load the default modes (interactive and editing)."""
return [cls.from_name("interactive"), cls.from_name("editing")]
return [cls.from_name(mode) for mode in DEFAULT_MODES]
@classmethod
def load(cls, name_or_path: str | Path) -> Self:
@@ -111,8 +111,8 @@ class SerenaAgentContext:
yaml_path = os.path.join(CONTEXT_YAMLS_DIR, f"{name}.yml")
if not os.path.exists(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. "
f"Available contexts: {cls.list_registered_context_names()}"
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"Available contexts:\n{cls.list_registered_context_names()}"
)
return cls.from_yaml(yaml_path)
@@ -131,7 +131,7 @@ class SerenaAgentContext:
@classmethod
def load_default(cls) -> Self:
"""Load the default context."""
return cls.from_name("default")
return cls.from_name(DEFAULT_CONTEXT)
def print_overview(self) -> None:
"""Print an overview of the mode."""
+3
View File
@@ -6,3 +6,6 @@ REPO_ROOT = str(_repo_root_path)
PROMPT_TEMPLATES_DIR = str(_repo_root_path / "config" / "prompt_templates")
CONTEXT_YAMLS_DIR = str(_repo_root_path / "config" / "contexts")
MODE_YAMLS_DIR = str(_repo_root_path / "config" / "modes")
DEFAULT_CONTEXT = "desktop-app"
DEFAULT_MODES = ("interactive", "editing")
+19 -16
View File
@@ -20,6 +20,7 @@ from sensai.util.helper import mark_used
from serena.agent import SerenaAgent, Tool, show_fatal_exception_safe
from serena.config import SerenaAgentContext, SerenaAgentMode
from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES
log = logging.getLogger(__name__)
LOG_FORMAT = "%(levelname)-5s %(asctime)-15s %(name)s:%(funcName)s:%(lineno)d - %(message)s"
@@ -94,9 +95,13 @@ def make_tool(
)
def create_mcp_server(
project_file_path: str | None, host: str = "0.0.0.0", port: int = 8000, context: str = "default", modes: Sequence[str] = ("default",)
) -> FastMCP:
def create_mcp_server_and_agent(
project_file_path: str | None,
host: str = "0.0.0.0",
port: int = 8000,
context: str = DEFAULT_CONTEXT,
modes: Sequence[str] = DEFAULT_MODES,
) -> tuple[FastMCP, SerenaAgent]:
"""
Create an MCP server.
@@ -147,7 +152,7 @@ def create_mcp_server(
update_tools()
return mcp
return mcp, agent
@click.command()
@@ -169,7 +174,8 @@ def create_mcp_server(
@click.option(
"--context",
type=str,
default="desktop-app",
show_default=True,
default=DEFAULT_CONTEXT,
help="Context to use. This can be a name of a built-in context ('desktop-app', 'agent', 'ide-assistant') "
"or a path to a custom context YAML file.",
)
@@ -178,7 +184,8 @@ def create_mcp_server(
"modes",
type=str,
multiple=True,
default=["editing", "interactive"],
default=DEFAULT_MODES,
show_default=True,
help="Mode(s) to use. This can be names of built-in modes ('planning', 'editing', 'one-shot', 'interactive') "
"or paths to custom mode YAML files. Can be specified multiple times to combine modes.",
)
@@ -192,22 +199,22 @@ def create_mcp_server(
@click.option(
"--host",
type=str,
default="0.0.0.0",
show_default=True,
default="0.0.0.0",
help="Host to bind to (for SSE transport).",
)
@click.option(
"--port",
type=int,
default=8000,
show_default=True,
default=8000,
help="Port to bind to (for SSE transport).",
)
def start_mcp_server(
project_file_opt: str | None,
project_file_arg: str | None,
context: str,
modes: tuple[str, ...],
context: str = DEFAULT_CONTEXT,
modes: tuple[str, ...] = DEFAULT_MODES,
transport: Literal["stdio", "sse"] = "stdio",
host: str = "0.0.0.0",
port: int = 8000,
@@ -222,7 +229,7 @@ def start_mcp_server(
# This is for backward compatibility with the old CLI, should be removed in the future!
project_file = project_file_arg if project_file_arg is not None else project_file_opt
mcp_server = create_mcp_server(project_file_path=project_file, host=host, port=port, context=context, modes=modes)
mcp_server, agent = create_mcp_server_and_agent(project_file_path=project_file, host=host, port=port, context=context, modes=modes)
# log after server creation such that the log appears in the GUI
if project_file_arg is not None:
@@ -232,10 +239,6 @@ def start_mcp_server(
f"Used path: {project_file}"
)
# Log selected context and modes
if context:
log.info(f"Using context: {context}")
if modes:
log.info(f"Using modes: {', '.join(modes)}")
log.info(f"Starting serena agent in MCP server with config:\n{agent.get_current_config_overview()}")
mcp_server.run(transport=transport)
View File