Support use of available_tools and available_markers in context/mode prompts

This commit is contained in:
Dominik Jain committed 2025-08-02 20:50:30 +02:00
1 parent e9e05c2f3f
commit 1bff8fa774
3 files changed
+42 -17

No files matched your search

+29 -14
View File
@@ -18,6 +18,7 @@ from typing import TYPE_CHECKING, Any, Optional, TypeVar
from sensai.util import logging
from sensai.util.logging import LogTime
from interprompt.jinja_template import JinjaTemplate
from serena import serena_version
from serena.analytics import RegisteredTokenCountEstimator, ToolUsageStats
from serena.config.context_mode import RegisteredContext, SerenaAgentContext, SerenaAgentMode
@@ -92,6 +93,23 @@ class MemoriesManager:
return f"Memory {name} deleted."
class AvailableTools:
def __init__(self, tools: list[Tool]):
"""
:param tools: the list of available tools
"""
self.tools = tools
self.tool_names = [tool.get_name_from_cls() for tool in tools]
self.tool_marker_names = set()
for marker_class in iter_subclasses(ToolMarker):
for tool in tools:
if isinstance(tool, marker_class):
self.tool_marker_names.add(marker_class.__name__)
def __len__(self) -> int:
return len(self.tools)
class SerenaAgent:
def __init__(
self,
@@ -193,7 +211,7 @@ class SerenaAgent:
tool_inclusion_definitions.append(SerenaAgentMode.from_name_internal("jetbrains"))
self._base_tool_set = ToolSet.default().apply(*tool_inclusion_definitions)
self._exposed_tools = {tc: t for tc, t in self._all_tools.items() if self._base_tool_set.includes_name(t.get_name())}
self._exposed_tools = AvailableTools([t for t in self._all_tools.values() if self._base_tool_set.includes_name(t.get_name())])
log.info(f"Number of exposed tools: {len(self._exposed_tools)}")
# create executor for starting the language server and running tools in another thread
@@ -302,7 +320,7 @@ class SerenaAgent:
If a client should attempt to use a tool that is dynamically disabled
(e.g. because a project is activated that disables it), it will receive an error.
"""
return list(self._exposed_tools.values())
return list(self._exposed_tools.tools)
def get_active_project(self) -> Project | None:
"""
@@ -336,24 +354,21 @@ class SerenaAgent:
"""
return list(self._modes)
def _format_prompt(self, prompt_template: str) -> str:
template = JinjaTemplate(prompt_template)
return template.render(available_tools=self._exposed_tools.tool_names, available_markers=self._exposed_tools.tool_marker_names)
def create_system_prompt(self) -> str:
available_tools = [tool.get_name_from_cls() for tool in self.get_exposed_tool_instances()]
# determine available tool markers (i.e. tool categories)
available_markers = set()
for marker_class in iter_subclasses(ToolMarker):
for tool in self.get_exposed_tool_instances():
if isinstance(tool, marker_class):
available_markers.add(marker_class.__name__)
available_markers = self._exposed_tools.tool_marker_names
log.info("Generating system prompt with available_tools=(see exposed tools), available_markers=%s", available_markers)
system_prompt = self.prompt_factory.create_system_prompt(
context_system_prompt=self._context.prompt,
mode_system_prompts=[mode.prompt for mode in self._modes],
available_tools=available_tools,
context_system_prompt=self._format_prompt(self._context.prompt),
mode_system_prompts=[self._format_prompt(mode.prompt) for mode in self._modes],
available_tools=self._exposed_tools.tool_names,
available_markers=available_markers,
)
log.info("System prompt:\n%s", system_prompt)
return system_prompt
def _update_active_tools(self) -> None:
"""
+8
View File
@@ -39,6 +39,10 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
name: str
prompt: str
"""
a Jinja2 template for the generation of the system prompt.
It is formatted by the agent (see SerenaAgent._format_prompt()).
"""
description: str = ""
def _tostring_includes(self) -> list[str]:
@@ -133,6 +137,10 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
name: str
prompt: str
"""
a Jinja2 template for the generation of the system prompt.
It is formatted by the agent (see SerenaAgent._format_prompt()).
"""
description: str = ""
def _tostring_includes(self) -> list[str]:
@@ -14,7 +14,7 @@ prompt: |
use the regex-based approach that is described below.
Let us first discuss the symbol-based approach.
Symbols are identified by their name path and relative file path, see the description of the `find_symbols` tool for more details
Symbols are identified by their name path and relative file path, see the description of the `find_symbol` tool for more details
on how the `name_path` matches symbols.
You can get information about available symbols by using the `get_symbols_overview` tool for finding top-level symbols in a file
or directory, or by using `find_symbol` if you already know the symbol's name path. You generally try to read as little code as possible
@@ -34,7 +34,8 @@ prompt: |
You will generally be able to use the info from the snippets and the regex-based approach to adjust the references as well.
You can assume that all symbol editing tools are reliable, so you don't need to verify the results if the tool returns without error.
Now let us discuss the regex-based approach.
{% if 'replace_regex' in available_tools %}
Let us discuss the regex-based approach.
The regex-based approach is your primary tool for editing code whenever replacing or deleting a whole symbol would be a more expensive operation.
This is the case if you need to adjust just a few lines of code within a method, or a chunk that is much smaller than a whole symbol.
You use other tools to find the relevant content and
@@ -104,7 +105,8 @@ prompt: |
Generally, I remind you that you rely on the regex tool with providing you the correct feedback, no need for more verification!
IMPORTANT: REMEMBER TO USE WILDCARDS WHEN APPROPRIATE! I WILL BE VERY UNHAPPY IF YOU WRITE LONG REGEXES WITHOUT USING WILDCARDS INSTEAD!
excluded_tools:
{% endif %}
excluded_tools:
- replace_lines
- insert_at_line
- delete_lines