Check tool functionality availability independently of the agent interface

Availability check:
* SerenaAgent.is_tool_function_available(tool_class): whether a tool's functionality is available
  to the LLM (tool mode: tool active; REPL mode: an enabled facade method corresponds to the tool)
* replaces the name-based tool_is_active/tool_is_exposed (activation message, dashboard, onboarding)

Prompts:
* activation message names functions via PromptParams.get_function_name (interface-specific names)
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-09-15 12:50:44 +02:00
1 parent 0706e3c98e
commit e39291979e
5 files changed
+50 -16

No files matched your search

+30 -12
View File
@@ -1017,6 +1017,15 @@ class SerenaAgent:
when using the REPL interface).
"""
def get_function_name(self, tool_class: type[Tool]) -> str:
"""
:param tool_class: the tool for which to get the function name
:return: the function name to use for this tool in prompts, which may be different from the tool's standard name
(e.g. when using a different language backend or when using the REPL interface)
"""
tool_name = tool_class.get_name_from_cls()
return self.tool_names_mapping.get(tool_name, tool_name)
def _get_prompt_params(self) -> PromptParams:
"""
:return: parameters for prompt rendering depending on the current agent interface, language backend and active tools/methods
@@ -1203,6 +1212,8 @@ class SerenaAgent:
proj = self._active_project
assert proj is not None, "A project must be active before calling this."
prompt_params = self._get_prompt_params()
# Note: The activation message is always returned in full, even if it was already provided in the current session,
# because some clients (e.g. Claude Desktop) will use the same session across multiple chats.
# So while we don't want the activation message to be additionally included in the system prompt
@@ -1222,16 +1233,16 @@ class SerenaAgent:
msg += f"File encoding: {proj.project_config.encoding}.\n"
# add list of memories (if memories are enabled)
include_memories = self._active_tools.contains_tool_class(ReadMemoryTool)
include_memories = self.is_tool_function_available(ReadMemoryTool)
if include_memories:
project_memories = proj.memory_manager.list_project_memories()
if project_memories:
msg += (
f"{json.dumps(project_memories.to_dict())}\n"
+ f"Use the `{ReadMemoryTool.get_name_from_cls()}` tool to read these memories later if they are relevant to the task.\n"
+ f"Use `{prompt_params.get_function_name(ReadMemoryTool)}` to read these memories later if they are relevant to the task.\n"
)
elif self._active_tools.contains_tool_class(OnboardingTool):
msg += f"Onboarding has not been performed yet. Ask the user whether to perform onboarding via the `{OnboardingTool.get_name_from_cls()}` tool.\n"
elif self.is_tool_function_available(OnboardingTool):
msg += f"Onboarding has not been performed yet. Ask the user whether to perform onboarding and if so, call `{prompt_params.get_function_name(OnboardingTool)}`.\n"
# add prompts for modes that were dynamically activated by the project
modes_with_prompts = self._project_prompt_status.get_modes_with_prompts_to_be_provided_for_project_activation(session_id)
@@ -1546,19 +1557,26 @@ class SerenaAgent:
"""
return self._active_tools.tool_names
def tool_is_active(self, tool_name: str) -> bool:
def get_active_tools(self) -> AvailableTools:
"""
:param tool_class: the name of the tool to check
:return: True if the tool is active, False otherwise
:return: the set of active tools
"""
return self._active_tools.contains_tool_name(tool_name)
return self._active_tools
def tool_is_exposed(self, tool_name: str) -> bool:
def is_tool_function_available(self, tool_class: type[Tool]) -> bool:
"""
:param tool_name: the name of the tool to check
:return: True if the tool is in the exposed tool set, False otherwise
Checks whether the functionality offered by a tool is available - either through the tool
itself being enabled or through the corresponding function being exposed in the REPL.
:param tool_class: the tool class
:return: whether the function is available
"""
return self._exposed_tools.contains_tool_name(tool_name)
if self._agent_interface == AgentInterface.TOOLS:
return self._active_tools.contains_tool_class(tool_class)
elif self._agent_interface == AgentInterface.REPL:
return self.get_repl().entrypoint.is_tool_function_available(tool_class)
else:
raise NotImplementedError
def get_current_config_overview(self) -> str:
"""
+2 -1
View File
@@ -27,6 +27,7 @@ from serena.analytics import ToolUsageStats
from serena.config.serena_config import SerenaConfig, SerenaPaths
from serena.constants import SERENA_DASHBOARD_DIR, SerenaPorts
from serena.task_executor import TaskExecutor
from serena.tools import ReadMemoryTool
from serena.util.logging import MemoryLogHandler
from serena.util.pypi import PyPIPackageInfo
from serena.util.pywebview import WebViewWithTray
@@ -604,7 +605,7 @@ class SerenaDashboardAPI:
# Get available memories if ReadMemoryTool is active
available_memories = None
if self._agent.tool_is_active("read_memory") and project is not None:
if self._agent.is_tool_function_available(ReadMemoryTool) and project is not None:
available_memories = project.memory_manager.list_memories().get_full_list()
# Get list of languages for the active project
+16 -1
View File
@@ -8,13 +8,16 @@ import ast
import logging
import re
import traceback
from typing import Any
from typing import TYPE_CHECKING, Any
from ..session import SerenaSession
from .external_project import ExternalProjectContext
from .facade import ApiScope, Facade, FacadeMethod, ReferencedType
from .representable import Representable
if TYPE_CHECKING:
from ..tools.tools_base import Tool
log = logging.getLogger(__name__)
@@ -45,6 +48,18 @@ class SerenaReplEntrypoint:
"""
return [method for facade in self._facades.values() for method in facade.get_enabled_methods()]
def is_tool_function_available(self, tool_class: "type[Tool]") -> bool:
"""
Checks whether any of the enabled methods corresponds to the given tool class.
:param tool_class: the tool class to check for
:return: whether any enabled method corresponds to the given tool class
"""
for method in self.get_enabled_methods():
if method.info.corresponding_tool == tool_class:
return True
return False
def set_external_project_(self, external_project: "ExternalProjectContext | None") -> None:
"""
:param external_project: the context of the external project being queried by the currently executing code
+1 -1
View File
@@ -308,7 +308,7 @@ class Tool(Component):
)
def is_active(self) -> bool:
return self.agent.tool_is_active(self.get_name())
return self.agent.get_active_tools().contains_tool_name(self.get_name())
def is_readonly(self) -> bool:
return not self.can_edit()
+1 -1
View File
@@ -19,7 +19,7 @@ class OnboardingTool(Tool, MemoryApiMixin):
:return: instructions on how to create the onboarding information
"""
write_memory_tool_available = self.agent.tool_is_exposed(WriteMemoryTool.get_name_from_cls())
write_memory_tool_available = self.agent.is_tool_function_available(WriteMemoryTool)
if not write_memory_tool_available:
return "Memory writing tool not activated, skipping onboarding."
return self._api().onboarding()