feat: Refactor AgentLoader into base class and add InMemory impl alongside existing filesystem impl

PiperOrigin-RevId: 786008518
This commit is contained in:
Alejandro Cruzado-Ruiz
2025-07-22 14:57:56 -07:00
committed by Copybara-Service
parent 4ae4c69c32
commit bda3df2480
5 changed files with 58 additions and 24 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ import uuid
from typing_extensions import deprecated
from ..agents import Agent
from ..agents.llm_agent import Agent
from ..artifacts.base_artifact_service import BaseArtifactService
from ..evaluation.base_eval_service import BaseEvalService
from ..evaluation.base_eval_service import EvaluateConfig
+1 -22
View File
@@ -27,7 +27,6 @@ import typing
from typing import Any
from typing import List
from typing import Literal
from typing import Mapping
from typing import Optional
import click
@@ -407,20 +406,7 @@ def get_fast_api_app(
@app.get("/list-apps")
def list_apps() -> list[str]:
base_path = Path.cwd() / agents_dir
if not base_path.exists():
raise HTTPException(status_code=404, detail="Path not found")
if not base_path.is_dir():
raise HTTPException(status_code=400, detail="Not a directory")
agent_names = [
x
for x in os.listdir(base_path)
if os.path.isdir(os.path.join(base_path, x))
and not x.startswith(".")
and x != "__pycache__"
]
agent_names.sort()
return agent_names
return agent_loader.list_agents()
@app.get("/debug/trace/{event_id}")
def get_trace_dict(event_id: str) -> Any:
@@ -525,13 +511,6 @@ def get_fast_api_app(
return session
def _get_eval_set_file_path(app_name, agents_dir, eval_set_id) -> str:
return os.path.join(
agents_dir,
app_name,
eval_set_id + _EVAL_SET_FILE_EXTENSION,
)
@app.post(
"/apps/{app_name}/eval_sets/{eval_set_id}",
response_model_exclude_none=True,
+19 -1
View File
@@ -17,20 +17,23 @@ from __future__ import annotations
import importlib
import logging
import os
from pathlib import Path
import sys
from typing import Optional
from pydantic import ValidationError
from typing_extensions import override
from . import envs
from ...agents import config_agent_utils
from ...agents.base_agent import BaseAgent
from ...utils.feature_decorator import working_in_progress
from .base_agent_loader import BaseAgentLoader
logger = logging.getLogger("google_adk." + __name__)
class AgentLoader:
class AgentLoader(BaseAgentLoader):
"""Centralized agent loading with proper isolation, caching, and .env loading.
Support loading agents from below folder/file structures:
a) {agent_name}.agent as a module name:
@@ -188,6 +191,7 @@ class AgentLoader:
" exposed."
)
@override
def load_agent(self, agent_name: str) -> BaseAgent:
"""Load an agent module (with caching & .env) and return its root_agent."""
if agent_name in self._agent_cache:
@@ -199,6 +203,20 @@ class AgentLoader:
self._agent_cache[agent_name] = agent
return agent
@override
def list_agents(self) -> list[str]:
"""Lists all agents available in the agent loader (sorted alphabetically)."""
base_path = Path.cwd() / self.agents_dir
agent_names = [
x
for x in os.listdir(base_path)
if os.path.isdir(os.path.join(base_path, x))
and not x.startswith(".")
and x != "__pycache__"
]
agent_names.sort()
return agent_names
def remove_agent_from_cache(self, agent_name: str):
# Clear module cache for the agent and its submodules
keys_to_delete = [
@@ -0,0 +1,34 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Base class for agent loaders."""
from __future__ import annotations
from abc import ABC
from abc import abstractmethod
from ...agents.base_agent import BaseAgent
class BaseAgentLoader(ABC):
"""Abstract base class for agent loaders."""
@abstractmethod
def load_agent(self, agent_name: str) -> BaseAgent:
"""Loads an instance of an agent with the given name."""
@abstractmethod
def list_agents(self) -> list[str]:
"""Lists all agents available in the agent loader in alphabetical order."""
+3
View File
@@ -189,6 +189,9 @@ def mock_agent_loader():
def load_agent(self, app_name):
return root_agent
def list_agents(self):
return ["test_app"]
return MockAgentLoader(".")