diff --git a/prompts/mcp_prompts.yml b/prompts/mcp_prompts.yml index 450aba90..3d5533f7 100644 --- a/prompts/mcp_prompts.yml +++ b/prompts/mcp_prompts.yml @@ -68,7 +68,7 @@ prompts: system_prompt: | You are a professional coding agent who is concerned with one particular codebase. You have tools to explore the codebase - iteratively, to edit files and to execute arbitrary commands. You heavily rely on these tools to solve the tasks give to you, + iteratively, to edit files and to execute arbitrary commands. You heavily rely on these tools to solve the tasks given to you, but you also operate in a frugal and intelligent manner, always keeping in mind to not load content that is not needed for the task at hand. If you cannot find everything you need in the codebase and generally when in doubt, you ask the user clarifying questions. diff --git a/scripts/agno_agent.py b/scripts/agno_agent.py index 32c0ccd5..98271347 100644 --- a/scripts/agno_agent.py +++ b/scripts/agno_agent.py @@ -2,6 +2,7 @@ import os from logging import Logger from agno.agent import Agent +from agno.memory import AgentMemory from agno.models.anthropic import Claude from agno.models.google import Gemini from agno.playground import Playground, serve_playground_app @@ -32,9 +33,12 @@ agno_agent = Agent( tools=[SerenaAgnoToolkit(serena_agent)], # type: ignore show_tool_calls=False, markdown=True, - system_message="", # Todo + system_message=serena_agent.prompt_factory.create_system_prompt(), read_tool_call_history=False, telemetry=False, + memory=AgentMemory(), + add_history_to_messages=True, + num_history_responses=100, # you might want to adjust this (expense vs. history awareness) ) # The app object must be in the module scope so that the server can access it for hot reloading diff --git a/src/serena/agno.py b/src/serena/agno.py index 3a9991ad..982cc29d 100644 --- a/src/serena/agno.py +++ b/src/serena/agno.py @@ -8,6 +8,64 @@ from serena.agent import SerenaAgent, Tool log = logging.getLogger(__name__) +def _patch_gemini_schema_conversion() -> None: + """ + This fixes errors in Agno's Gemini schema conversion, which does not correctly handle + optional types (union with None) + """ + from agno.models.google import gemini + from google.genai.types import Schema + + def _convert_schema(schema_dict: dict) -> Schema | None: + schema_type = schema_dict.get("type", "") + if isinstance(schema_type, list): + schema_type = schema_type[0] + schema_type = schema_type.upper() + description = schema_dict.get("description", "") + + if schema_type == "OBJECT" and "properties" in schema_dict: + properties = {key: _convert_schema(prop_def) for key, prop_def in schema_dict["properties"].items()} + required = schema_dict.get("required", []) + + if properties: + return Schema( + type=schema_type, + properties=properties, + required=required, + description=description, + ) + else: + return None + + elif schema_type == "ARRAY" and "items" in schema_dict: + items = _convert_schema(schema_dict["items"]) + return Schema(type=schema_type, description=description, items=items) + + elif schema_type == "": + if "anyOf" in schema_dict: + relevant_sub_schemas = [] + is_optional = False + for sub_schema in schema_dict["anyOf"]: + if sub_schema.get("type") == "null": + is_optional = True # noqa: F841 + continue + relevant_sub_schemas.append(sub_schema) + # TODO handle is_optional (requires handling at the outer level) + if len(relevant_sub_schemas) == 1: + return _convert_schema(relevant_sub_schemas[0]) + else: + return Schema(any_of=[_convert_schema(item) for item in schema_dict["anyOf"]], description=description) + else: + raise ValueError(f"Unhandled schema: {schema_dict}") + else: + return Schema(type=schema_type, description=description) + + gemini._convert_schema = _convert_schema + + +_patch_gemini_schema_conversion() + + class SerenaAgnoToolkit(Toolkit): def __init__(self, serena_agent: SerenaAgent): super().__init__("Serena") @@ -28,5 +86,5 @@ class SerenaAgnoToolkit(Toolkit): function = Function.from_callable(tool.get_apply_fn()) function.name = tool.get_name() function.entrypoint = entrypoint - function.show_result = True + function.skip_entrypoint_processing = True return function