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