diff --git a/contributing/samples/gepa/experiment.py b/contributing/samples/gepa/experiment.py index f3751206a8..2710c3894c 100644 --- a/contributing/samples/gepa/experiment.py +++ b/contributing/samples/gepa/experiment.py @@ -43,7 +43,6 @@ from tau_bench.types import EnvRunResult from tau_bench.types import RunConfig import tau_bench_agent as tau_bench_agent_lib - import utils diff --git a/contributing/samples/gepa/run_experiment.py b/contributing/samples/gepa/run_experiment.py index d857da9635..e31db15788 100644 --- a/contributing/samples/gepa/run_experiment.py +++ b/contributing/samples/gepa/run_experiment.py @@ -25,7 +25,6 @@ from absl import flags import experiment from google.genai import types - import utils _OUTPUT_DIR = flags.DEFINE_string( diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000000..3f69952516 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,12 @@ +import nox + +@nox.session(python=["3.10", "3.11", "3.12", "3.13"]) +def lint(session): + session.install("-e", ".") + session.install("pylint") + session.run("pylint", "src/google", "--rcfile=pylintrc") + +@nox.session(python=["3.10", "3.11", "3.12", "3.13"]) +def unit(session): + session.install("-e", ".[test]") + session.run("pytest", "tests/unittests") diff --git a/repro_5428.py b/repro_5428.py new file mode 100644 index 0000000000..71064eaf75 --- /dev/null +++ b/repro_5428.py @@ -0,0 +1,20 @@ +import asyncio +from google.adk.tools.function_tool import FunctionTool + +async def generate_image( + prompt: str, + input_bytes: list[tuple[bytes, str]] | None = None, +) -> dict: + """Generate an image from a prompt.""" + return {"status": "success"} + +async def main(): + try: + generate_image_tool = FunctionTool(func=generate_image) + generate_image_tool._get_declaration() + print("SUCCESS! No validation error.") + except Exception as e: + print(f"FAILED! Error: {type(e).__name__}: {e}") + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/src/google/adk/cli/adk_web_server.py b/src/google/adk/cli/adk_web_server.py index ef83dcd45a..d543bb9593 100644 --- a/src/google/adk/cli/adk_web_server.py +++ b/src/google/adk/cli/adk_web_server.py @@ -656,6 +656,8 @@ def __init__( extra_plugins: Optional[list[str]] = None, logo_text: Optional[str] = None, logo_image_url: Optional[str] = None, + max_llm_calls: int = 500, + avatar_config: Optional[str] = None, url_prefix: Optional[str] = None, auto_create_session: bool = False, trigger_sources: Optional[list[str]] = None, @@ -675,10 +677,31 @@ def __init__( self.runners_to_clean: set[str] = set() self.current_app_name_ref: SharedValue[str] = SharedValue(value="") self.runner_dict = {} + self.max_llm_calls = max_llm_calls + self.avatar_config = avatar_config self.url_prefix = url_prefix self.auto_create_session = auto_create_session self.trigger_sources = trigger_sources + def _get_avatar_config(self) -> Optional[types.AvatarConfig]: + """Parses avatar_config string or file into AvatarConfig object.""" + if not self.avatar_config: + return None + + try: + # Check if it's a file path + if os.path.isfile(self.avatar_config): + with open(self.avatar_config, "r", encoding="utf-8") as f: + config_dict = json.load(f) + else: + # Assume it's a JSON string + config_dict = json.loads(self.avatar_config) + + return types.AvatarConfig.model_validate(config_dict) + except Exception as e: + logger.error("Failed to parse avatar_config: %s", e) + return None + async def get_runner_async(self, app_name: str) -> Runner: """Returns the cached runner for the given app.""" # Handle cleanup @@ -1898,6 +1921,10 @@ async def run_agent(req: RunAgentRequest) -> list[Event]: session_id=req.session_id, new_message=req.new_message, state_delta=req.state_delta, + run_config=RunConfig( + max_llm_calls=self.max_llm_calls, + avatar_config=self._get_avatar_config(), + ), invocation_id=req.invocation_id, ) ) as agen: @@ -1940,7 +1967,11 @@ async def event_generator(): session_id=req.session_id, new_message=req.new_message, state_delta=req.state_delta, - run_config=RunConfig(streaming_mode=stream_mode), + run_config=RunConfig( + streaming_mode=stream_mode, + max_llm_calls=self.max_llm_calls, + avatar_config=self._get_avatar_config(), + ), invocation_id=req.invocation_id, ) ) as agen: @@ -1982,6 +2013,26 @@ async def event_generator(): media_type="text/event-stream", ) + @app.get("/dev/build_graph_image/{app_name}", tags=[TAG_DEBUG]) + async def get_app_graph_image( + app_name: str, dark_mode: bool = False + ) -> Response: + """Returns the agent graph as an SVG image for the dev UI.""" + agent_or_app = self.agent_loader.load_agent(app_name) + root_agent = self._get_root_agent(agent_or_app) + + # Get graph with NO highlights (empty list) and specified theme + dot_graph = await agent_graph.get_agent_graph( + root_agent, [], dark_mode=dark_mode + ) + + if dot_graph and isinstance(dot_graph, graphviz.Digraph): + # Render the graph as SVG + svg_image = dot_graph.pipe(format="svg") + return Response(content=svg_image, media_type="image/svg+xml") + else: + raise HTTPException(status_code=404, detail="Graph not found") + @app.get( "/dev/{app_name}/graph", response_model_exclude_none=True, @@ -2119,6 +2170,8 @@ async def forward_events(): else None ), save_live_blob=save_live_blob, + max_llm_calls=self.max_llm_calls, + avatar_config=self._get_avatar_config(), ) async with Aclosing( runner.run_live( diff --git a/src/google/adk/cli/cli.py b/src/google/adk/cli/cli.py index 1d49f50d79..6953d541d1 100644 --- a/src/google/adk/cli/cli.py +++ b/src/google/adk/cli/cli.py @@ -24,6 +24,7 @@ from pydantic import BaseModel from ..agents.llm_agent import LlmAgent +from ..agents.run_config import RunConfig from ..apps.app import App from ..artifacts.base_artifact_service import BaseArtifactService from ..auth.credential_service.base_credential_service import BaseCredentialService @@ -56,6 +57,8 @@ async def run_input_file( credential_service: BaseCredentialService, input_path: str, memory_service: Optional[BaseMemoryService] = None, + max_llm_calls: int = 500, + avatar_config: Optional[types.AvatarConfig] = None, ) -> Session: app = ( agent_or_app @@ -81,7 +84,12 @@ async def run_input_file( content = types.Content(role='user', parts=[types.Part(text=query)]) async with Aclosing( runner.run_async( - user_id=session.user_id, session_id=session.id, new_message=content + user_id=session.user_id, + session_id=session.id, + new_message=content, + run_config=RunConfig( + max_llm_calls=max_llm_calls, avatar_config=avatar_config + ), ) ) as agen: async for event in agen: @@ -98,6 +106,8 @@ async def run_interactively( session_service: BaseSessionService, credential_service: BaseCredentialService, memory_service: Optional[BaseMemoryService] = None, + max_llm_calls: int = 500, + avatar_config: Optional[types.AvatarConfig] = None, ) -> None: app = ( root_agent_or_app @@ -124,6 +134,9 @@ async def run_interactively( new_message=types.Content( role='user', parts=[types.Part(text=query)] ), + run_config=RunConfig( + max_llm_calls=max_llm_calls, avatar_config=avatar_config + ), ) ) as agen: async for event in agen: @@ -145,6 +158,8 @@ async def run_cli( artifact_service_uri: Optional[str] = None, memory_service_uri: Optional[str] = None, use_local_storage: bool = True, + max_llm_calls: int = 500, + avatar_config: Optional[str] = None, ) -> None: """Runs an interactive CLI for a certain agent. @@ -170,6 +185,19 @@ async def run_cli( user_id = 'test_user' agents_dir = str(agent_parent_path) + + avatar_config_obj = None + if avatar_config: + try: + if Path(avatar_config).is_file(): + with open(avatar_config, 'r', encoding='utf-8') as f: + config_dict = json.load(f) + else: + config_dict = json.loads(avatar_config) + avatar_config_obj = types.AvatarConfig.model_validate(config_dict) + except Exception as e: + click.secho(f'Warning: Failed to parse avatar_config: {e}', fg='yellow') + agent_loader = AgentLoader(agents_dir=agents_dir) agent_or_app = agent_loader.load_agent(agent_folder_name) session_app_name = ( @@ -224,6 +252,8 @@ def _print_event(event) -> None: memory_service=memory_service, credential_service=credential_service, input_path=input_file, + max_llm_calls=max_llm_calls, + avatar_config=avatar_config_obj, ) elif saved_session_file: # Load the saved session from file @@ -250,6 +280,8 @@ def _print_event(event) -> None: session_service, credential_service, memory_service=memory_service, + max_llm_calls=max_llm_calls, + avatar_config=avatar_config_obj, ) else: session = await session_service.create_session( @@ -263,6 +295,8 @@ def _print_event(event) -> None: session_service, credential_service, memory_service=memory_service, + max_llm_calls=max_llm_calls, + avatar_config=avatar_config_obj, ) if save_session: diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index 07ccc15892..8588dc0e0c 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -51,6 +51,252 @@ ) +def _check_port_available(host: str, port: int) -> bool: + """Check if a port is available for binding on a host.""" + import socket + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + try: + s.bind((host, port)) + return True + except socket.error: + return False + + +def web_options(): + """Decorator to add web UI options to click commands.""" + + def decorator(func): + @click.option( + "--logo-text", + type=str, + help="Optional. The text to display in the logo of the web UI.", + default=None, + ) + @click.option( + "--logo-image-url", + type=str, + help=( + "Optional. The URL of the image to display in the logo of the" + " web UI." + ), + default=None, + ) + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return wrapper + + return decorator + + +def _deprecate_staging_bucket(ctx, param, value): + if value: + click.echo( + click.style( + f"WARNING: --{param} is deprecated and will be removed. Please" + " leave it unspecified.", + fg="yellow", + ), + err=True, + ) + return value + + +def deprecated_adk_services_options(): + """Deprecated ADK services options.""" + + def warn(alternative_param, ctx, param, value): + if value: + click.echo( + click.style( + f"WARNING: Deprecated option --{param.name} is used. Please use" + f" {alternative_param} instead.", + fg="yellow", + ), + err=True, + ) + return value + + def decorator(func): + @click.option( + "--session_db_url", + help="Deprecated. Use --session_service_uri instead.", + callback=functools.partial(warn, "--session_service_uri"), + ) + @click.option( + "--artifact_storage_uri", + type=str, + help="Deprecated. Use --artifact_service_uri instead.", + callback=functools.partial(warn, "--artifact_service_uri"), + default=None, + ) + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return wrapper + + return decorator + + +def fast_api_common_options(): + """Decorator to add common fast api options to click commands.""" + + def decorator(func): + + @click.option( + "--host", + type=str, + help="Optional. The binding host of the server", + default="127.0.0.1", + show_default=True, + ) + @click.option( + "--port", + type=int, + help="Optional. The port of the server", + default=8000, + ) + @click.option( + "--allow_origins", + help=( + "Optional. Origins to allow for CORS. Can be literal origins" + " (e.g., 'https://example.com') or regex patterns prefixed with" + " 'regex:' (e.g., 'regex:https://.*\\.example\\.com')." + ), + multiple=True, + ) + @click.option( + "-v", + "--verbose", + is_flag=True, + show_default=True, + default=False, + help="Enable verbose (DEBUG) logging. Shortcut for --log_level DEBUG.", + ) + @click.option( + "--log_level", + type=LOG_LEVELS, + default="INFO", + help="Optional. Set the logging level", + ) + @click.option( + "--trace_to_cloud", + is_flag=True, + show_default=True, + default=False, + help="Optional. Whether to enable cloud trace for telemetry.", + ) + @click.option( + "--otel_to_cloud", + is_flag=True, + show_default=True, + default=False, + help=("Optional. Whether to enable OpenTelemetry for Agent Engine."), + ) + @click.option( + "--reload/--no-reload", + default=True, + help=( + "Optional. Whether to enable auto reload for server. Not supported" + " for Cloud Run." + ), + ) + @click.option( + "--a2a", + is_flag=True, + show_default=True, + default=False, + help="Optional. Whether to enable A2A endpoint.", + ) + @click.option( + "--reload_agents", + is_flag=True, + default=False, + show_default=True, + help="Optional. Whether to enable live reload for agents changes.", + ) + @click.option( + "--eval_storage_uri", + type=str, + help=( + "Optional. The evals storage URI to store agent evals," + " supported URIs: gs://." + ), + default=None, + ) + @click.option( + "--max_llm_calls", + type=int, + default=500, + show_default=True, + help="Optional. Maximum number of LLM calls allowed for a given run.", + ) + @click.option( + "--avatar_config", + type=str, + help=( + "Optional. JSON string or path to JSON file containing" + " avatar configuration for live sessions (e.g.," + " '{\"avatarName\": \"avatar_id\"}')." + ), + default=None, + ) + @click.option( + "--extra_plugins", + help=( + "Optional. Comma-separated list of extra plugin classes or" + " instances to enable (e.g., my.module.MyPluginClass or" + " my.module.my_plugin_instance)." + ), + multiple=True, + ) + @click.option( + "--url_prefix", + type=str, + help=( + "Optional. URL path prefix when the application is mounted behind a" + " reverse proxy or API gateway (e.g., '/api/v1', '/adk'). This" + " ensures generated URLs and redirects work correctly when the app" + " is not served at the root path. Must start with '/' if provided." + ), + default=None, + ) + @click.option( + "--trigger_sources", + type=str, + help=( + "Optional. Comma-separated list of trigger sources to enable" + " (e.g., 'pubsub,eventarc'). Registers /apps/{app_name}/trigger/*" + " endpoints for batch and event-driven agent invocations." + ), + default=None, + ) + @functools.wraps(func) + @click.pass_context + def wrapper(ctx, *args, **kwargs): + log_level_source = ctx.get_parameter_source("log_level") + if ( + kwargs.pop("verbose", False) + and log_level_source == ParameterSource.DEFAULT + ): + kwargs["log_level"] = "DEBUG" + + trigger_sources = kwargs.get("trigger_sources") + if trigger_sources is not None: + kwargs["trigger_sources"] = [ + s.strip() for s in trigger_sources.split(",") if s.strip() + ] + + return func(*args, **kwargs) + + return wrapper + + return decorator + + def _apply_feature_overrides( *, enable_features: tuple[str, ...] = (), @@ -605,6 +851,7 @@ def wrapper(*args, **kwargs): @main.command("run", cls=HelpfulCommand) @feature_options() @adk_services_options(default_use_local_storage=True) +@fast_api_common_options() @click.option( "--save_session", type=bool, @@ -659,10 +906,25 @@ def cli_run( session_id: Optional[str], replay: Optional[str], resume: Optional[str], + host: str = "127.0.0.1", + port: int = 8000, + allow_origins: Optional[list[str]] = None, + log_level: str = "INFO", + trace_to_cloud: bool = False, + otel_to_cloud: bool = False, + reload: bool = True, + a2a: bool = False, + reload_agents: bool = False, + eval_storage_uri: Optional[str] = None, + max_llm_calls: int = 500, + avatar_config: Optional[str] = None, + extra_plugins: Optional[list[str]] = None, + url_prefix: Optional[str] = None, session_service_uri: Optional[str] = None, artifact_service_uri: Optional[str] = None, memory_service_uri: Optional[str] = None, use_local_storage: bool = True, + trigger_sources: Optional[list[str]] = None, ): """Runs an interactive CLI for a certain agent. @@ -689,6 +951,8 @@ def cli_run( artifact_service_uri=artifact_service_uri, memory_service_uri=memory_service_uri, use_local_storage=use_local_storage, + max_llm_calls=max_llm_calls, + avatar_config=avatar_config, ) ) @@ -1370,227 +1634,6 @@ def cli_generate_eval_cases( raise click.ClickException(f"Failed to generate eval case(s): {e}") from e -def web_options(): - """Decorator to add web UI options to click commands.""" - - def decorator(func): - @click.option( - "--logo-text", - type=str, - help="Optional. The text to display in the logo of the web UI.", - default=None, - ) - @click.option( - "--logo-image-url", - type=str, - help=( - "Optional. The URL of the image to display in the logo of the" - " web UI." - ), - default=None, - ) - @functools.wraps(func) - def wrapper(*args, **kwargs): - return func(*args, **kwargs) - - return wrapper - - return decorator - - -def _deprecate_staging_bucket(ctx, param, value): - if value: - click.echo( - click.style( - f"WARNING: --{param} is deprecated and will be removed. Please" - " leave it unspecified.", - fg="yellow", - ), - err=True, - ) - return value - - -def deprecated_adk_services_options(): - """Deprecated ADK services options.""" - - def warn(alternative_param, ctx, param, value): - if value: - click.echo( - click.style( - f"WARNING: Deprecated option --{param.name} is used. Please use" - f" {alternative_param} instead.", - fg="yellow", - ), - err=True, - ) - return value - - def decorator(func): - @click.option( - "--session_db_url", - help="Deprecated. Use --session_service_uri instead.", - callback=functools.partial(warn, "--session_service_uri"), - ) - @click.option( - "--artifact_storage_uri", - type=str, - help="Deprecated. Use --artifact_service_uri instead.", - callback=functools.partial(warn, "--artifact_service_uri"), - default=None, - ) - @functools.wraps(func) - def wrapper(*args, **kwargs): - return func(*args, **kwargs) - - return wrapper - - return decorator - - -def fast_api_common_options(): - """Decorator to add common fast api options to click commands.""" - - def decorator(func): - - @click.option( - "--host", - type=str, - help="Optional. The binding host of the server", - default="127.0.0.1", - show_default=True, - ) - @click.option( - "--port", - type=int, - help="Optional. The port of the server", - default=8000, - ) - @click.option( - "--allow_origins", - help=( - "Optional. Origins to allow for CORS. Can be literal origins" - " (e.g., 'https://example.com') or regex patterns prefixed with" - " 'regex:' (e.g., 'regex:https://.*\\.example\\.com')." - ), - multiple=True, - ) - @click.option( - "-v", - "--verbose", - is_flag=True, - show_default=True, - default=False, - help="Enable verbose (DEBUG) logging. Shortcut for --log_level DEBUG.", - ) - @click.option( - "--log_level", - type=LOG_LEVELS, - default="INFO", - help="Optional. Set the logging level", - ) - @click.option( - "--trace_to_cloud", - is_flag=True, - show_default=True, - default=False, - help="Optional. Whether to enable cloud trace for telemetry.", - ) - @click.option( - "--otel_to_cloud", - is_flag=True, - show_default=True, - default=False, - help=( - "Optional. Whether to write OTel data to Google Cloud" - " Observability services - Cloud Trace and Cloud Logging." - ), - ) - @click.option( - "--reload/--no-reload", - default=True, - help=( - "Optional. Whether to enable auto reload for server. Not supported" - " for Cloud Run." - ), - ) - @click.option( - "--a2a", - is_flag=True, - show_default=True, - default=False, - help="Optional. Whether to enable A2A endpoint.", - ) - @click.option( - "--reload_agents", - is_flag=True, - default=False, - show_default=True, - help="Optional. Whether to enable live reload for agents changes.", - ) - @click.option( - "--eval_storage_uri", - type=str, - help=( - "Optional. The evals storage URI to store agent evals," - " supported URIs: gs://." - ), - default=None, - ) - @click.option( - "--extra_plugins", - help=( - "Optional. Comma-separated list of extra plugin classes or" - " instances to enable (e.g., my.module.MyPluginClass or" - " my.module.my_plugin_instance)." - ), - multiple=True, - ) - @click.option( - "--url_prefix", - type=str, - help=( - "Optional. URL path prefix when the application is mounted behind a" - " reverse proxy or API gateway (e.g., '/api/v1', '/adk'). This" - " ensures generated URLs and redirects work correctly when the app" - " is not served at the root path. Must start with '/' if provided." - ), - default=None, - ) - # Parsed into list[str] by the wrapper below (server commands need a list). - @click.option( - "--trigger_sources", - type=str, - help=( - "Optional. Comma-separated list of trigger sources to enable" - " (e.g., 'pubsub,eventarc'). Registers /apps/{app_name}/trigger/*" - " endpoints for batch and event-driven agent invocations." - ), - default=None, - ) - @functools.wraps(func) - @click.pass_context - def wrapper(ctx, *args, **kwargs): - # If verbose flag is set and log level is not set, set log level to DEBUG. - log_level_source = ctx.get_parameter_source("log_level") - if ( - kwargs.pop("verbose", False) - and log_level_source == ParameterSource.DEFAULT - ): - kwargs["log_level"] = "DEBUG" - - # Parse comma-separated trigger_sources into a list. - trigger_sources = kwargs.get("trigger_sources") - if trigger_sources is not None: - kwargs["trigger_sources"] = [ - s.strip() for s in trigger_sources.split(",") if s.strip() - ] - - return func(*args, **kwargs) - - return wrapper - - return decorator @main.command("web") @@ -1609,6 +1652,8 @@ def wrapper(ctx, *args, **kwargs): def cli_web( agents_dir: str, eval_storage_uri: Optional[str] = None, + max_llm_calls: int = 500, + avatar_config: Optional[str] = None, log_level: str = "INFO", allow_origins: Optional[list[str]] = None, host: str = "127.0.0.1", @@ -1672,6 +1717,8 @@ async def _lifespan(app: FastAPI): memory_service_uri=memory_service_uri, use_local_storage=use_local_storage, eval_storage_uri=eval_storage_uri, + max_llm_calls=max_llm_calls, + avatar_config=avatar_config, allow_origins=allow_origins, web=True, trace_to_cloud=trace_to_cloud, @@ -1723,6 +1770,8 @@ async def _lifespan(app: FastAPI): def cli_api_server( agents_dir: str, eval_storage_uri: Optional[str] = None, + max_llm_calls: int = 500, + avatar_config: Optional[str] = None, log_level: str = "INFO", allow_origins: Optional[list[str]] = None, host: str = "127.0.0.1", @@ -1764,6 +1813,8 @@ def cli_api_server( memory_service_uri=memory_service_uri, use_local_storage=use_local_storage, eval_storage_uri=eval_storage_uri, + max_llm_calls=max_llm_calls, + avatar_config=avatar_config, allow_origins=allow_origins, web=False, trace_to_cloud=trace_to_cloud, diff --git a/src/google/adk/cli/fast_api.py b/src/google/adk/cli/fast_api.py index fa1948d4e2..cd8a696baf 100644 --- a/src/google/adk/cli/fast_api.py +++ b/src/google/adk/cli/fast_api.py @@ -82,6 +82,8 @@ def get_fast_api_app( memory_service_uri: Optional[str] = None, use_local_storage: bool = True, eval_storage_uri: Optional[str] = None, + max_llm_calls: int = 500, + avatar_config: Optional[str] = None, allow_origins: Optional[list[str]] = None, web: bool, a2a: bool = False, diff --git a/src/google/adk/tools/_function_parameter_parse_util.py b/src/google/adk/tools/_function_parameter_parse_util.py index a8e98980d5..62fd43635d 100644 --- a/src/google/adk/tools/_function_parameter_parse_util.py +++ b/src/google/adk/tools/_function_parameter_parse_util.py @@ -131,7 +131,19 @@ def _generate_json_schema_for_parameter( json_schema_dict = _add_unevaluated_items_to_fixed_len_tuple_schema( json_schema_dict ) - return json_schema_dict + + def _strip_unsupported_keys(d: Any) -> Any: + if isinstance(d, dict): + d.pop('prefixItems', None) + d.pop('unevaluatedItems', None) + for k, v in d.items(): + _strip_unsupported_keys(v) + elif isinstance(d, list): + for item in d: + _strip_unsupported_keys(item) + return d + + return _strip_unsupported_keys(json_schema_dict) def _is_builtin_primitive_or_compound( diff --git a/tests/unittests/cli/test_cli_tools_click_option_mismatch.py b/tests/unittests/cli/test_cli_tools_click_option_mismatch.py index 01026274df..9ffd62f088 100644 --- a/tests/unittests/cli/test_cli_tools_click_option_mismatch.py +++ b/tests/unittests/cli/test_cli_tools_click_option_mismatch.py @@ -98,7 +98,7 @@ def test_adk_run(): run_command, cli_run.callback, "run", - ignore_params={"enable_features", "disable_features"}, + ignore_params={"verbose", "enable_features", "disable_features"}, ) diff --git a/tests/unittests/cli/utils/test_cli.py b/tests/unittests/cli/utils/test_cli.py index f7df1bf17f..c7b1035b6a 100644 --- a/tests/unittests/cli/utils/test_cli.py +++ b/tests/unittests/cli/utils/test_cli.py @@ -423,6 +423,7 @@ async def _run_input_file( credential_service: InMemoryCredentialService, input_path: str, memory_service: Any = None, + **kwargs, ) -> object: del app_name, user_id, agent_or_app, artifact_service del session_service, credential_service, input_path