mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 06:15:43 +00:00
Allow overriding tool descriptions in the context, cli command for tool description
This commit is contained in:
1 parent
e41d09d937
commit
46967fa3cb
8 files changed
+65
-8
No files matched your search
@@ -227,6 +227,12 @@ class SerenaAgent:
|
||||
except Exception as e:
|
||||
log.error(f"Error activating project '{project}' at startup: {e}", exc_info=e)
|
||||
|
||||
def get_context(self) -> SerenaAgentContext:
|
||||
return self._context
|
||||
|
||||
def get_tool_description_override(self, tool_name: str) -> str | None:
|
||||
return self._context.tool_description_overrides.get(tool_name, None)
|
||||
|
||||
def _check_shell_settings(self) -> None:
|
||||
# On Windows, Claude Code sets COMSPEC to Git-Bash (often even with a path containing spaces),
|
||||
# which causes all sorts of trouble, preventing language servers from being launched correctly.
|
||||
@@ -583,3 +589,7 @@ class SerenaAgent:
|
||||
if self._gui_log_viewer:
|
||||
log.info("Stopping the GUI log window ...")
|
||||
self._gui_log_viewer.stop()
|
||||
|
||||
def get_tool_by_name(self, tool_name: str) -> Tool:
|
||||
tool_class = ToolRegistry().get_tool_class_by_name(tool_name)
|
||||
return self.get_tool(tool_class)
|
||||
+23
-1
@@ -26,7 +26,7 @@ from serena.constants import (
|
||||
USER_CONTEXT_YAMLS_DIR,
|
||||
USER_MODE_YAMLS_DIR,
|
||||
)
|
||||
from serena.mcp import SerenaMCPFactorySingleProcess
|
||||
from serena.mcp import SerenaMCPFactory, SerenaMCPFactorySingleProcess
|
||||
from serena.project import Project
|
||||
from serena.tools import ToolRegistry
|
||||
from serena.util.logging import MemoryLogHandler
|
||||
@@ -478,6 +478,28 @@ class ToolCommands(AutoRegisteringGroup):
|
||||
else:
|
||||
ToolRegistry().print_tool_overview()
|
||||
|
||||
@staticmethod
|
||||
@click.command(
|
||||
"description",
|
||||
help="Print the description of a tool, optionally with a specific context (the latter may modify the default description).",
|
||||
)
|
||||
@click.argument("tool_name", type=str)
|
||||
@click.option("--context", type=str, default=None, help="Context name or path to context file.")
|
||||
def description(tool_name: str, context: str | None = None) -> None:
|
||||
# Load the context
|
||||
serena_context = None
|
||||
if context:
|
||||
serena_context = SerenaAgentContext.load(context)
|
||||
|
||||
agent = SerenaAgent(
|
||||
project=None,
|
||||
serena_config=SerenaConfig(web_dashboard=False, log_level=logging.INFO),
|
||||
context=serena_context,
|
||||
)
|
||||
tool = agent.get_tool_by_name(tool_name)
|
||||
mcp_tool = SerenaMCPFactory.make_mcp_tool(tool)
|
||||
click.echo(mcp_tool.description)
|
||||
|
||||
|
||||
class PromptCommands(AutoRegisteringGroup):
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -4,7 +4,7 @@ Context and Mode configuration loader
|
||||
|
||||
import os
|
||||
from copy import copy
|
||||
from dataclasses import asdict, dataclass
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Self
|
||||
@@ -134,6 +134,8 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
name: str
|
||||
prompt: str
|
||||
description: str = ""
|
||||
tool_description_overrides: dict[str, str] = field(default_factory=dict)
|
||||
"""Maps tool names to custom descriptions, default descriptions are extracted from the tool docstrings."""
|
||||
|
||||
def _tostring_includes(self) -> list[str]:
|
||||
return ["name"]
|
||||
@@ -147,6 +149,9 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
def from_json_dict(cls, data: dict) -> Self:
|
||||
data = copy(data)
|
||||
data["excluded_tools"] = set(data["excluded_tools"])
|
||||
# Ensure backwards compatibility for tool_description_overrides
|
||||
if "tool_description_overrides" not in data:
|
||||
data["tool_description_overrides"] = {}
|
||||
return cls(**data)
|
||||
|
||||
@classmethod
|
||||
@@ -155,7 +160,10 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
|
||||
with open(yaml_path, encoding="utf-8") as f:
|
||||
data = yaml.safe_load(f)
|
||||
name = data.pop("name", Path(yaml_path).stem)
|
||||
return cls(name=name, **data)
|
||||
prompt = data["prompt"]
|
||||
description = data.get("description", "")
|
||||
tool_description_overrides = data.get("tool_description_overrides", {})
|
||||
return cls(name=name, prompt=prompt, description=description, tool_description_overrides=tool_description_overrides)
|
||||
|
||||
@classmethod
|
||||
def get_path(cls, name: str) -> str:
|
||||
|
||||
+9
-2
@@ -68,10 +68,16 @@ class SerenaMCPFactory:
|
||||
|
||||
# Mount the tool description as a combination of the docstring description and
|
||||
# the return value description, if it exists.
|
||||
if docstring.description:
|
||||
func_doc = f"{docstring.description.strip().strip('.')}."
|
||||
agent = cast(SerenaAgent, tool.agent)
|
||||
overridden_description = agent.get_context().tool_description_overrides.get(func_name, None)
|
||||
|
||||
if overridden_description is not None:
|
||||
func_doc = overridden_description
|
||||
elif docstring.description:
|
||||
func_doc = docstring.description
|
||||
else:
|
||||
func_doc = ""
|
||||
func_doc = func_doc.strip().strip(".")
|
||||
if docstring.returns and (docstring_returns_descr := docstring.returns.description):
|
||||
# Only add a space before "Returns" if func_doc is not empty
|
||||
prefix = " " if func_doc else ""
|
||||
@@ -113,6 +119,7 @@ class SerenaMCPFactory:
|
||||
for tool in self._iter_tools():
|
||||
mcp_tool = self.make_mcp_tool(tool)
|
||||
mcp._tool_manager._tools[tool.get_name()] = mcp_tool
|
||||
log.info(f"Starting MCP server with {len(mcp._tool_manager._tools)} tools: {list(mcp._tool_manager._tools.keys())}")
|
||||
|
||||
@abstractmethod
|
||||
def _instantiate_agent(self, serena_config: SerenaConfig, modes: list[SerenaAgentMode]) -> None:
|
||||
|
||||
@@ -3,4 +3,6 @@ prompt: |
|
||||
You are running in agent context where the system prompt is provided externally. You should use symbolic
|
||||
tools when possible for code understanding and modification.
|
||||
excluded_tools:
|
||||
- initial_instructions
|
||||
- initial_instructions
|
||||
|
||||
tool_description_overrides: {}
|
||||
@@ -1,4 +1,8 @@
|
||||
# See Serena's documentation for more details on concept of contexts.
|
||||
description: Description of the context, not used in the code.
|
||||
prompt: Prompt that will form part of the system prompt/initial instructions for agents started in this context.
|
||||
excluded_tools: []
|
||||
excluded_tools: []
|
||||
|
||||
# mapping of tool names to an override of their descriptions (the default description is the docstring of the Tool's apply method).
|
||||
# Sometimes, tool descriptions are too long (e.g., for ChatGPT), or users may want to override them for another reason.
|
||||
tool_description_overrides: {}
|
||||
@@ -10,4 +10,5 @@ prompt: |
|
||||
If complex relationships that the user asked for should be visualized or explained, consider creating
|
||||
a diagram in addition to your text-based communication. Note that in the chat interface you have various rendering
|
||||
options for text, html, and mermaid diagrams, as has been explained to you in your initial instructions.
|
||||
excluded_tools: []
|
||||
excluded_tools: []
|
||||
tool_description_overrides: {}
|
||||
@@ -26,3 +26,6 @@ excluded_tools:
|
||||
- prepare_for_new_conversation
|
||||
- summarize_changes
|
||||
- get_current_config
|
||||
|
||||
tool_description_overrides: {}
|
||||
|
||||
Reference in new issue
Block a user