diff --git a/pyproject.toml b/pyproject.toml index 7eccb2bb1..e360ebdb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,6 +105,7 @@ test = [ "pytest-mock>=3.14.0", "pytest-xdist>=3.6.1", "pytest>=8.3.4", + "python-multipart>=0.0.9", # go/keep-sorted end ] diff --git a/src/google/adk/cli/fast_api.py b/src/google/adk/cli/fast_api.py index 2e37f3bb1..face2495e 100644 --- a/src/google/adk/cli/fast_api.py +++ b/src/google/adk/cli/fast_api.py @@ -20,6 +20,7 @@ import logging import os from pathlib import Path +import shutil import time import traceback import typing @@ -32,6 +33,7 @@ from fastapi import FastAPI from fastapi import HTTPException from fastapi import Query +from fastapi import UploadFile from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse from fastapi.responses import StreamingResponse @@ -51,7 +53,6 @@ from typing_extensions import override from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer -import yaml from ..agents import RunConfig from ..agents.live_request_queue import LiveRequest @@ -212,14 +213,6 @@ class GetEventGraphResult(common.BaseModel): dot_src: str -class AgentBuildRequest(common.BaseModel): - agent_name: str - agent_type: str - model: str - description: str - instruction: str - - def get_fast_api_app( *, agents_dir: str, @@ -820,26 +813,30 @@ async def delete_artifact( @working_in_progress("builder_save is not ready for use.") @app.post("/builder/save", response_model_exclude_none=True) - async def builder_build(req: AgentBuildRequest): + async def builder_build(files: list[UploadFile]) -> bool: base_path = Path.cwd() / agents_dir - agent = { - "agent_class": req.agent_type, - "name": req.agent_name, - "model": req.model, - "description": req.description, - "instruction": f"""{req.instruction}""", - } - try: - agent_dir = os.path.join(base_path, req.agent_name) - os.makedirs(agent_dir, exist_ok=True) - file_path = os.path.join(agent_dir, "root_agent.yaml") - with open(file_path, "w") as file: - yaml.dump(agent, file, default_flow_style=False) - agent_loader.load_agent(agent_name=req.agent_name) - return True - except Exception as e: - logger.exception("Error in builder_build: %s", e) - return False + + for file in files: + try: + # File name format: {app_name}/{agent_name}.yaml + if not file.filename: + logger.exception("Agent name is missing in the input files") + return False + + agent_name, filename = file.filename.split("/") + + agent_dir = os.path.join(base_path, agent_name) + os.makedirs(agent_dir, exist_ok=True) + file_path = os.path.join(agent_dir, filename) + + with open(file_path, "w") as buffer: + shutil.copyfileobj(file.file, buffer) + + except Exception as e: + logger.exception("Error in builder_build: %s", e) + return False + + return True @app.post("/run", response_model_exclude_none=True) async def agent_run(req: AgentRunRequest) -> list[Event]: