mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 02:14:49 +00:00
Merge pull request #523 from oraios/feature/make_max_tokens_configurable
Config enhancement: allow configuring default_max_tool_answer_chars
This commit is contained in:
8 files changed
+45
-31
No files matched your search
@@ -350,6 +350,11 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
|
||||
on the first run, which can take some time and require internet access. Others, like the Anthropic ones, may require an API key
|
||||
and rate limits may apply.
|
||||
"""
|
||||
default_max_tool_answer_chars: int = 150_000
|
||||
"""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
|
||||
through the global configuration.
|
||||
"""
|
||||
|
||||
CONFIG_FILE = "serena_config.yml"
|
||||
CONFIG_FILE_DOCKER = "serena_config.docker.yml" # Docker-specific config file; auto-generated if missing, mounted via docker-compose for user customization
|
||||
@@ -453,6 +458,7 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin):
|
||||
instance.token_count_estimator = loaded_commented_yaml.get(
|
||||
"token_count_estimator", RegisteredTokenCountEstimator.TIKTOKEN_GPT4O.name
|
||||
)
|
||||
instance.default_max_tool_answer_chars = loaded_commented_yaml.get("default_max_tool_answer_chars", 150_000)
|
||||
|
||||
# re-save the configuration file if any migrations were performed
|
||||
if num_project_migrations > 0:
|
||||
|
||||
@@ -48,6 +48,11 @@ jetbrains: False
|
||||
# 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
|
||||
# through the global configuration.
|
||||
|
||||
record_tool_usage_stats: False
|
||||
# whether to record tool usage statistics, they will be shown in the web dashboard if recording is active.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Tools supporting the execution of (external) commands
|
||||
"""
|
||||
|
||||
from serena.tools import TOOL_DEFAULT_MAX_ANSWER_LENGTH, Tool, ToolMarkerCanEdit
|
||||
from serena.tools import Tool, ToolMarkerCanEdit
|
||||
from serena.util.shell import execute_shell_command
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ExecuteShellCommandTool(Tool, ToolMarkerCanEdit):
|
||||
command: str,
|
||||
cwd: str | None = None,
|
||||
capture_stderr: bool = True,
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Execute a shell command and return its output. If there is a memory about suggested commands, read that first.
|
||||
@@ -26,7 +26,7 @@ class ExecuteShellCommandTool(Tool, ToolMarkerCanEdit):
|
||||
:param cwd: the working directory to execute the command in. If None, the project root will be used.
|
||||
:param capture_stderr: whether to capture and return stderr output
|
||||
:param max_answer_chars: if the output is longer than this number of characters,
|
||||
no content will be returned. Don't adjust unless there is really no other way to get the content
|
||||
no content will be returned. -1 means using the default value, don't adjust unless there is no other way to get the content
|
||||
required for the task.
|
||||
:return: a JSON object containing the command's stdout and optionally stderr output
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,7 @@ from fnmatch import fnmatch
|
||||
from pathlib import Path
|
||||
|
||||
from serena.text_utils import search_files
|
||||
from serena.tools import SUCCESS_RESULT, TOOL_DEFAULT_MAX_ANSWER_LENGTH, EditedFileContext, Tool, ToolMarkerCanEdit, ToolMarkerOptional
|
||||
from serena.tools import SUCCESS_RESULT, EditedFileContext, Tool, ToolMarkerCanEdit, ToolMarkerOptional
|
||||
from serena.util.file_system import scan_directory
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ class ReadFileTool(Tool):
|
||||
Reads a file within the project directory.
|
||||
"""
|
||||
|
||||
def apply(
|
||||
self, relative_path: str, start_line: int = 0, end_line: int | None = None, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH
|
||||
) -> str:
|
||||
def apply(self, relative_path: str, start_line: int = 0, end_line: int | None = None, max_answer_chars: int = -1) -> str:
|
||||
"""
|
||||
Reads the given file or a chunk of it. Generally, symbolic operations
|
||||
like find_symbol or find_referencing_symbols should be preferred if you know which symbols you are looking for.
|
||||
@@ -89,15 +87,15 @@ class ListDirTool(Tool):
|
||||
Lists files and directories in the given directory (optionally with recursion).
|
||||
"""
|
||||
|
||||
def apply(self, relative_path: str, recursive: bool, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH) -> str:
|
||||
def apply(self, relative_path: str, recursive: bool, max_answer_chars: int = -1) -> str:
|
||||
"""
|
||||
Lists all non-gitignored files and directories in the given directory (optionally with recursion).
|
||||
|
||||
:param relative_path: the relative path to the directory to list; pass "." to scan the project root
|
||||
:param recursive: whether to scan subdirectories recursively
|
||||
:param max_answer_chars: if the output is longer than this number of characters,
|
||||
no content will be returned. Don't adjust unless there is really no other way to get the content
|
||||
required for the task.
|
||||
no content will be returned. -1 means the default value from the config will be used.
|
||||
Don't adjust unless there is really no other way to get the content required for the task.
|
||||
:return: a JSON object with the names of directories and files within the given directory
|
||||
"""
|
||||
# Check if the directory exists before validation
|
||||
@@ -309,7 +307,7 @@ class SearchForPatternTool(Tool):
|
||||
paths_exclude_glob: str = "",
|
||||
relative_path: str = "",
|
||||
restrict_search_to_code_files: bool = False,
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Offers a flexible search for arbitrary patterns in the codebase, including the
|
||||
@@ -350,7 +348,9 @@ class SearchForPatternTool(Tool):
|
||||
:param relative_path: only subpaths of this path (relative to the repo root) will be analyzed. If a path to a single
|
||||
file is passed, only that will be searched. The path must exist, otherwise a `FileNotFoundError` is raised.
|
||||
:param max_answer_chars: if the output is longer than this number of characters,
|
||||
no content will be returned. Don't adjust unless there is really no other way to get the content
|
||||
no content will be returned.
|
||||
-1 means the default value from the config will be used.
|
||||
Don't adjust unless there is really no other way to get the content
|
||||
required for the task. Instead, if the output is too long, you should
|
||||
make a stricter query.
|
||||
:param restrict_search_to_code_files: whether to restrict the search to only those files where
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json
|
||||
|
||||
from serena.tools import TOOL_DEFAULT_MAX_ANSWER_LENGTH, Tool, ToolMarkerOptional, ToolMarkerSymbolicRead
|
||||
from serena.tools import Tool, ToolMarkerOptional, ToolMarkerSymbolicRead
|
||||
from serena.tools.jetbrains_plugin_client import JetBrainsPluginClient
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class JetBrainsFindSymbolTool(Tool, ToolMarkerSymbolicRead, ToolMarkerOptional):
|
||||
depth: int = 0,
|
||||
relative_path: str | None = None,
|
||||
include_body: bool = False,
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Retrieves information on all symbols/code entities (classes, methods, etc.) based on the given `name_path`,
|
||||
@@ -57,6 +57,7 @@ class JetBrainsFindSymbolTool(Tool, ToolMarkerSymbolicRead, ToolMarkerOptional):
|
||||
speed up the search as well as reduce the number of results.
|
||||
:param include_body: If True, include the symbol's source code. Use judiciously.
|
||||
:param max_answer_chars: max characters for the JSON result. If exceeded, no content is returned.
|
||||
-1 means the default value from the config will be used.
|
||||
:return: JSON string: a list of symbols (with locations) matching the name.
|
||||
"""
|
||||
with JetBrainsPluginClient.from_project(self.project) as client:
|
||||
@@ -79,7 +80,7 @@ class JetBrainsFindReferencingSymbolsTool(Tool, ToolMarkerSymbolicRead, ToolMark
|
||||
self,
|
||||
name_path: str,
|
||||
relative_path: str,
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Finds symbols that reference the symbol at the given `name_path`.
|
||||
@@ -88,7 +89,8 @@ class JetBrainsFindReferencingSymbolsTool(Tool, ToolMarkerSymbolicRead, ToolMark
|
||||
:param name_path: name path of the symbol for which to find references; matching logic as described in find symbol tool.
|
||||
:param relative_path: the relative path to the file containing the symbol for which to find references.
|
||||
Note that here you can't pass a directory but must pass a file.
|
||||
:param max_answer_chars: max characters for the JSON result. If exceeded, no content is returned.
|
||||
:param max_answer_chars: max characters for the JSON result. If exceeded, no content is returned. -1 means the
|
||||
default value from the config will be used.
|
||||
:return: a list of JSON objects with the symbols referencing the requested symbol
|
||||
"""
|
||||
with JetBrainsPluginClient.from_project(self.project) as client:
|
||||
@@ -108,7 +110,7 @@ class JetBrainsGetSymbolsOverviewTool(Tool, ToolMarkerSymbolicRead, ToolMarkerOp
|
||||
def apply(
|
||||
self,
|
||||
relative_path: str,
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Gets an overview of the top-level symbols in the given file.
|
||||
@@ -119,6 +121,7 @@ class JetBrainsGetSymbolsOverviewTool(Tool, ToolMarkerSymbolicRead, ToolMarkerOp
|
||||
|
||||
:param relative_path: the relative path to the file to get the overview of
|
||||
:param max_answer_chars: max characters for the JSON result. If exceeded, no content is returned.
|
||||
-1 means the default value from the config will be used.
|
||||
:return: a JSON object containing the symbols
|
||||
"""
|
||||
with JetBrainsPluginClient.from_project(self.project) as client:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json
|
||||
|
||||
from serena.tools import TOOL_DEFAULT_MAX_ANSWER_LENGTH, Tool
|
||||
from serena.tools import Tool
|
||||
|
||||
|
||||
class WriteMemoryTool(Tool):
|
||||
@@ -8,7 +8,7 @@ class WriteMemoryTool(Tool):
|
||||
Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
"""
|
||||
|
||||
def apply(self, memory_name: str, content: str, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH) -> str:
|
||||
def apply(self, memory_name: str, content: str, max_answer_chars: int = -1) -> str:
|
||||
"""
|
||||
Write some information about this project that can be useful for future tasks to a memory in md format.
|
||||
The memory name should be meaningful.
|
||||
@@ -26,7 +26,7 @@ class ReadMemoryTool(Tool):
|
||||
Reads the memory with the given name from Serena's project-specific memory store.
|
||||
"""
|
||||
|
||||
def apply(self, memory_file_name: str, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH) -> str:
|
||||
def apply(self, memory_file_name: str, max_answer_chars: int = -1) -> str:
|
||||
"""
|
||||
Read the content of a memory file. This tool should only be used if the information
|
||||
is relevant to the current task. You can infer whether the information
|
||||
|
||||
@@ -11,7 +11,6 @@ from typing import Any
|
||||
|
||||
from serena.tools import (
|
||||
SUCCESS_RESULT,
|
||||
TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
Tool,
|
||||
ToolMarkerSymbolicEdit,
|
||||
ToolMarkerSymbolicRead,
|
||||
@@ -52,7 +51,7 @@ class GetSymbolsOverviewTool(Tool, ToolMarkerSymbolicRead):
|
||||
Gets an overview of the top-level symbols defined in a given file.
|
||||
"""
|
||||
|
||||
def apply(self, relative_path: str, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH) -> str:
|
||||
def apply(self, relative_path: str, max_answer_chars: int = -1) -> str:
|
||||
"""
|
||||
Use this tool to get a high-level understanding of the code symbols in a file.
|
||||
This should be the first tool to call when you want to understand a new file, unless you already know
|
||||
@@ -60,8 +59,8 @@ class GetSymbolsOverviewTool(Tool, ToolMarkerSymbolicRead):
|
||||
|
||||
:param relative_path: the relative path to the file to get the overview of
|
||||
:param max_answer_chars: if the overview is longer than this number of characters,
|
||||
no content will be returned. Don't adjust unless there is really no other way to get the content
|
||||
required for the task.
|
||||
no content will be returned. -1 means the default value from the config will be used.
|
||||
Don't adjust unless there is really no other way to get the content required for the task.
|
||||
:return: a JSON object containing info about top-level symbols in the file
|
||||
"""
|
||||
symbol_retriever = self.create_language_server_symbol_retriever()
|
||||
@@ -92,7 +91,7 @@ class FindSymbolTool(Tool, ToolMarkerSymbolicRead):
|
||||
include_kinds: list[int] = [], # noqa: B006
|
||||
exclude_kinds: list[int] = [], # noqa: B006
|
||||
substring_matching: bool = False,
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Retrieves information on all symbols/code entities (classes, methods, etc.) based on the given `name_path`,
|
||||
@@ -142,6 +141,7 @@ class FindSymbolTool(Tool, ToolMarkerSymbolicRead):
|
||||
If not provided, no kinds are excluded.
|
||||
:param substring_matching: If True, use substring matching for the last segment of `name`.
|
||||
:param max_answer_chars: Max characters for the JSON result. If exceeded, no content is returned.
|
||||
-1 means the default value from the config will be used.
|
||||
:return: a list of symbols (with locations) matching the name.
|
||||
"""
|
||||
parsed_include_kinds: Sequence[SymbolKind] | None = [SymbolKind(k) for k in include_kinds] if include_kinds else None
|
||||
@@ -171,7 +171,7 @@ class FindReferencingSymbolsTool(Tool, ToolMarkerSymbolicRead):
|
||||
relative_path: str,
|
||||
include_kinds: list[int] = [], # noqa: B006
|
||||
exclude_kinds: list[int] = [], # noqa: B006
|
||||
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
|
||||
max_answer_chars: int = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Finds references to the symbol at the given `name_path`. The result will contain metadata about the referencing symbols
|
||||
|
||||
@@ -70,9 +70,6 @@ class Component(ABC):
|
||||
return self.agent.lines_read
|
||||
|
||||
|
||||
TOOL_DEFAULT_MAX_ANSWER_LENGTH = int(15e4)
|
||||
|
||||
|
||||
class ToolMarker:
|
||||
"""
|
||||
Base class for tool markers.
|
||||
@@ -214,8 +211,11 @@ class Tool(Component):
|
||||
params[param] = value
|
||||
log.info(f"{self.get_name_from_cls()}: {dict_string(params)}")
|
||||
|
||||
@staticmethod
|
||||
def _limit_length(result: str, max_answer_chars: int) -> str:
|
||||
def _limit_length(self, result: str, max_answer_chars: int) -> str:
|
||||
if max_answer_chars == -1:
|
||||
max_answer_chars = self.agent.serena_config.default_max_tool_answer_chars
|
||||
if max_answer_chars <= 0:
|
||||
raise ValueError(f"Must be positive or the default (-1), got: {max_answer_chars=}")
|
||||
if (n_chars := len(result)) > max_answer_chars:
|
||||
result = (
|
||||
f"The answer is too long ({n_chars} characters). "
|
||||
|
||||
Reference in new issue
Block a user