Skip to content

Commit 96779ac

Browse files
authored
feat: add --google-api-key option for AI Studio support (#577)
Add CLI option to use Google AI Studio API key instead of Vertex AI: - Generate .env file with GOOGLE_API_KEY when flag is passed - Skip GCP credential checks when using API key - Conditionally include/exclude Vertex AI initialization in agent templates
1 parent 32fe55c commit 96779ac

File tree

7 files changed

+98
-8
lines changed

7 files changed

+98
-8
lines changed

agent_starter_pack/agents/adk_a2a_base/app/agent.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# ruff: noqa
12
# Copyright 2025 Google LLC
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,17 +14,20 @@
1314
# limitations under the License.
1415

1516
import datetime
16-
import os
1717
from zoneinfo import ZoneInfo
1818

19-
import google.auth
2019
from google.adk.agents import Agent
2120
from google.adk.apps.app import App
21+
{%- if not cookiecutter.use_google_api_key %}
22+
23+
import os
24+
import google.auth
2225

2326
_, project_id = google.auth.default()
2427
os.environ["GOOGLE_CLOUD_PROJECT"] = project_id
2528
os.environ["GOOGLE_CLOUD_LOCATION"] = "global"
2629
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "True"
30+
{%- endif %}
2731

2832

2933
def get_weather(query: str) -> str:

agent_starter_pack/agents/adk_base/app/agent.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# ruff: noqa
12
# Copyright 2025 Google LLC
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,17 +14,20 @@
1314
# limitations under the License.
1415

1516
import datetime
16-
import os
1717
from zoneinfo import ZoneInfo
1818

19-
import google.auth
2019
from google.adk.agents import Agent
2120
from google.adk.apps.app import App
21+
{%- if not cookiecutter.use_google_api_key %}
22+
23+
import os
24+
import google.auth
2225

2326
_, project_id = google.auth.default()
2427
os.environ["GOOGLE_CLOUD_PROJECT"] = project_id
2528
os.environ["GOOGLE_CLOUD_LOCATION"] = "global"
2629
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "True"
30+
{%- endif %}
2731

2832

2933
def get_weather(query: str) -> str:

agent_starter_pack/agents/adk_live/app/agent.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# ruff: noqa
12
# Copyright 2025 Google LLC
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,19 +13,21 @@
1213
# See the License for the specific language governing permissions and
1314
# limitations under the License.
1415

15-
import os
16+
from google.adk.agents import Agent
17+
from google.adk.apps.app import App
18+
{%- if not cookiecutter.use_google_api_key %}
1619

20+
import os
1721
import google.auth
1822
import vertexai
19-
from google.adk.agents import Agent
20-
from google.adk.apps.app import App
2123

2224
_, project_id = google.auth.default()
2325
os.environ["GOOGLE_CLOUD_PROJECT"] = project_id
2426
os.environ["GOOGLE_CLOUD_LOCATION"] = "us-central1"
2527
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "True"
2628

2729
vertexai.init(project=project_id, location="us-central1")
30+
{%- endif %}
2831

2932

3033
def get_weather(query: str) -> str:

agent_starter_pack/cli/commands/create.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
def shared_template_options(f: Callable) -> Callable:
5959
"""Decorator to add shared options for template-based commands."""
6060
# Apply options in reverse order since decorators are applied bottom-up
61+
f = click.option(
62+
"--google-api-key",
63+
type=str,
64+
help="Use Google AI Studio API key instead of Vertex AI. Generates a .env file with the provided API key.",
65+
default=None,
66+
)(f)
6167
f = click.option(
6268
"-ag",
6369
"--agent-garden",
@@ -274,6 +280,7 @@ def create(
274280
skip_welcome: bool = False,
275281
locked: bool = False,
276282
cli_overrides: dict | None = None,
283+
google_api_key: str | None = None,
277284
) -> None:
278285
"""Create GCP-based AI agent projects from templates."""
279286
try:
@@ -715,11 +722,19 @@ def create(
715722
if debug:
716723
logging.debug(f"Selected region: {region}")
717724

725+
# Validate google_api_key is not used with langgraph agents
726+
if google_api_key and "langgraph" in final_agent.lower():
727+
raise click.ClickException(
728+
"--google-api-key is not supported for LangGraph agents. "
729+
"You can modify the generated template to use ChatGoogleGenerativeAI class instead. "
730+
"See https://docs.langchain.com/oss/python/integrations/chat/google_generative_ai"
731+
)
732+
718733
# GCP Setup
719734
logging.debug("Setting up GCP...")
720735

721736
creds_info = {}
722-
if not skip_checks:
737+
if not skip_checks and not google_api_key:
723738
# Set up GCP environment
724739
try:
725740
creds_info = setup_gcp_environment(
@@ -778,6 +793,7 @@ def create(
778793
cli_overrides=final_cli_overrides,
779794
agent_garden=agent_garden,
780795
remote_spec=remote_spec,
796+
google_api_key=google_api_key,
781797
)
782798

783799
# Replace region in all files if a different region was specified

agent_starter_pack/cli/commands/enhance.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ def enhance(
266266
base_template: str | None,
267267
adk: bool,
268268
agent_directory: str | None,
269+
google_api_key: str | None = None,
269270
) -> None:
270271
"""Enhance your existing project with AI agent capabilities.
271272
@@ -642,4 +643,5 @@ def enhance(
642643
base_template=base_template,
643644
skip_welcome=True, # Skip welcome message since enhance shows its own
644645
cli_overrides=final_cli_overrides if final_cli_overrides else None,
646+
google_api_key=google_api_key,
645647
)

agent_starter_pack/cli/utils/template.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,7 @@ def process_template(
750750
cli_overrides: dict[str, Any] | None = None,
751751
agent_garden: bool = False,
752752
remote_spec: Any | None = None,
753+
google_api_key: str | None = None,
753754
) -> None:
754755
"""Process the template directory and create a new project.
755756
@@ -768,6 +769,7 @@ def process_template(
768769
in_folder: Whether to template directly into the output directory instead of creating a subdirectory
769770
cli_overrides: Optional CLI override values that should take precedence over template config
770771
agent_garden: Whether this deployment is from Agent Garden
772+
google_api_key: Optional Google AI Studio API key to generate .env file
771773
"""
772774
logging.debug(f"Processing template from {template_dir}")
773775
logging.debug(f"Project name: {project_name}")
@@ -1040,6 +1042,7 @@ def get_agent_directory(
10401042
"agent_garden": agent_garden,
10411043
"agent_sample_id": agent_sample_id or "",
10421044
"agent_sample_publisher": agent_sample_publisher or "",
1045+
"use_google_api_key": bool(google_api_key),
10431046
"adk_cheatsheet": adk_cheatsheet_content,
10441047
"llm_txt": llm_txt_content,
10451048
"_copy_without_render": [
@@ -1422,6 +1425,19 @@ def get_agent_directory(
14221425
lock_file.truncate()
14231426
logging.debug(f"Updated project name in lock file at {lock_file_path}")
14241427

1428+
# Generate .env file for Google API Key if provided
1429+
if google_api_key:
1430+
env_file_path = final_destination / agent_directory / ".env"
1431+
env_content = f"""# AI Studio Configuration
1432+
GOOGLE_API_KEY={google_api_key}
1433+
"""
1434+
env_file_path.write_text(env_content)
1435+
logging.debug(f"Generated .env file at {env_file_path}")
1436+
console.print(
1437+
f"📝 Generated .env file at [cyan]{agent_directory}/.env[/cyan] "
1438+
"for Google AI Studio"
1439+
)
1440+
14251441
except Exception as e:
14261442
logging.error(f"Failed to process template: {e!s}")
14271443
raise

tests/cli/commands/test_create_local.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,51 @@ def test_readme_and_pyproject_conflict_handling_in_folder_mode(
278278
assert other_file.read_text() == "# Other file content"
279279

280280

281+
@patch("agent_starter_pack.cli.commands.create.setup_gcp_environment")
282+
@patch("agent_starter_pack.cli.commands.create.replace_region_in_files")
283+
def test_create_with_google_api_key(
284+
mock_replace_region: Mock,
285+
mock_setup_gcp: Mock,
286+
tmp_path: pathlib.Path,
287+
) -> None:
288+
"""Test the create command with --google-api-key generates .env and skips GCP init."""
289+
runner = CliRunner()
290+
291+
result = runner.invoke(
292+
create,
293+
[
294+
"my-test-project",
295+
"--google-api-key",
296+
"test-api-key-12345",
297+
"--auto-approve",
298+
"--deployment-target",
299+
"agent_engine",
300+
"--output-dir",
301+
str(tmp_path),
302+
],
303+
catch_exceptions=False,
304+
)
305+
306+
assert result.exit_code == 0, result.output
307+
308+
# GCP setup should NOT be called when using google-api-key
309+
mock_setup_gcp.assert_not_called()
310+
311+
# Verify .env file was created with the API key
312+
env_file = tmp_path / "my-test-project" / "app" / ".env"
313+
assert env_file.exists(), ".env file should be created"
314+
env_content = env_file.read_text()
315+
assert "GOOGLE_API_KEY=test-api-key-12345" in env_content
316+
317+
# Verify agent.py does NOT contain GCP initialization code
318+
agent_file = tmp_path / "my-test-project" / "app" / "agent.py"
319+
assert agent_file.exists(), "agent.py should exist"
320+
agent_content = agent_file.read_text()
321+
assert "google.auth.default" not in agent_content
322+
assert "GOOGLE_GENAI_USE_VERTEXAI" not in agent_content
323+
assert "GOOGLE_CLOUD_PROJECT" not in agent_content
324+
325+
281326
def test_readme_and_pyproject_conflict_handling_remote_template_mode(
282327
tmp_path: pathlib.Path,
283328
) -> None:

0 commit comments

Comments
 (0)