From e30d99aea06aecdeaab9bbd1dee2fd884c42f8ac Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Tue, 1 Apr 2025 14:28:48 +0200 Subject: [PATCH 1/2] Fix typo --- prompts/mcp_prompts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From b1572a9717c5d0472736378e4b87b6962fbd5f23 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Tue, 1 Apr 2025 15:14:49 +0200 Subject: [PATCH 2/2] Make Gemini work, fixing issues with schema conversion --- scripts/agno_agent.py | 6 ++++- src/serena/agno.py | 60 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) 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