Compare commits

...

42 Commits

Author SHA1 Message Date
Harsh Jha
12c554df1f chore: fix auth 2025-09-09 19:03:07 +05:30
Harsh Jha
9a4409b5ff chore: added pkg of wget 2025-09-09 19:03:07 +05:30
Harsh Jha
d4c78ade87 chore: fix wget cmd 2025-09-09 19:03:07 +05:30
Harsh Jha
33c9009863 chore: fix of cloudsql 2025-09-09 19:03:07 +05:30
Harsh Jha
865040166a chore: fix toolbox instance 2025-09-09 19:03:07 +05:30
Harsh Jha
c1a9eda0b3 chore: fix toolbox url 2025-09-09 19:03:07 +05:30
Harsh Jha
cf24f5cca6 chore: fix variable name of version of toolbox 2025-09-09 19:03:07 +05:30
Harsh Jha
08c9c9e811 removed log of secret value 2025-09-09 19:03:07 +05:30
Harsh Jha
6ad513a256 chore: removed cloudsql steps 2025-09-09 19:03:07 +05:30
Harsh Jha
c3db65c134 fix: removed extra steps 2025-09-09 19:03:07 +05:30
Harsh Jha
5cc43b3e8a chore: added license for quickstart_py.yaml 2025-09-09 19:03:07 +05:30
Harsh Jha
1a730705b1 updated 2025-09-09 19:03:07 +05:30
Harsh Jha
bba285a5b3 debug for pg password 2025-09-09 19:03:07 +05:30
Harsh Jha
170984706e updated 2025-09-09 19:03:06 +05:30
Harsh Jha
2db6aa3218 updated 2025-09-09 19:03:06 +05:30
Harsh Jha
6d9a22aabd updated 2025-09-09 19:03:06 +05:30
Harsh Jha
6d829ef065 updated 2025-09-09 19:03:06 +05:30
Harsh Jha
6813e6211d update 2025-09-09 19:03:06 +05:30
Harsh Jha
f7f8870a85 fixed: quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
7cc6d4e08d added psql cmd 2025-09-09 19:03:06 +05:30
Harsh Jha
2ea5671fa7 updated 2025-09-09 19:03:06 +05:30
Harsh Jha
19d7a9419a updated quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
ad9137a452 updated quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
106183fae8 updated quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
c32cc78012 chore: updated ci files 2025-09-09 19:03:06 +05:30
Harsh Jha
af2afd47f1 updated quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
84bb5b8078 chore: updated quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
30e2888370 chore(ci): update quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
dba96fa4f8 chore(ci): update _GCP_PROJECT_NUMBER value in quickstart_py.yaml 2025-09-09 19:03:06 +05:30
Harsh Jha
7ff06e1345 feat(ci): add ci files for py sample workflow 2025-09-09 19:03:05 +05:30
Harsh Jha
3e213c110c chore(python/quickstart): address multiple review comments in sample code 2025-09-09 19:01:50 +05:30
Harsh Jha
72d1bf72b9 test: Remove redundant script output check from tests 2025-09-09 18:59:47 +05:30
Harsh Jha
704413f475 fix(python/quickstart): pin exact versions in requirements.txt 2025-09-09 18:59:46 +05:30
Harsh Jha
ef3aa80fac fix(python/quickstart): revert code sample in all python quickstart examples 2025-09-09 18:59:46 +05:30
Harsh Jha
7d16633dc1 feat: Implement language-agnostic snippet shortcode 2025-09-09 18:59:46 +05:30
Harsh Jha
1d9b72a926 refactor(docs): Fix Table of Contents and optimize region include shortcode 2025-09-09 18:59:46 +05:30
Harsh Jha
751142a652 refactor(docs): Replace duplicated content with a shared shortcode 2025-09-09 18:59:46 +05:30
Harsh Jha
d4e6909a5b feat: Add shortcode for including file regions 2025-09-09 18:59:46 +05:30
Harsh Jha
b658710b55 chore(docs/python): remove compile-time test from quickstart samples 2025-09-09 18:59:46 +05:30
Harsh Jha
f1dc359046 refactor(test): update quickstart tests to only check for error-free execution 2025-09-09 18:59:46 +05:30
Harsh Jha
f1c8b2ac87 feat: add pytest-based test infrastructure for Python quickstart frameworks 2025-09-09 18:59:46 +05:30
Harsh Jha
3ebc2879e9 feat(adk): add Python quickstart testing infrastructure with requirements.txt, golden.txt, and quickstart_test.py 2025-09-09 18:59:01 +05:30
13 changed files with 337 additions and 3 deletions

107
.ci/quickstart_py.sh Normal file
View File

@@ -0,0 +1,107 @@
#!/bin/bash
set -e
set -u
TABLE_NAME="hotels"
QUICKSTART_PYTHON_DIR="docs/en/getting-started/quickstart/python"
TOOLBOX_SETUP_DIR="/workspace/toolbox_setup"
apt-get update && apt-get install -y postgresql-client python3-venv curl wget
if [ ! -d "$QUICKSTART_PYTHON_DIR" ]; then
exit 1
fi
wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64 -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" &
PROXY_PID=$!
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
mkdir -p "${TOOLBOX_SETUP_DIR}"
echo "${TOOLS_YAML_CONTENT}" > "${TOOLBOX_SETUP_DIR}/tools.yaml"
if [ ! -f "${TOOLBOX_SETUP_DIR}/tools.yaml" ]; then echo "Failed to create tools.yaml"; exit 1; fi
curl -L "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -o "${TOOLBOX_SETUP_DIR}/toolbox"
chmod +x "${TOOLBOX_SETUP_DIR}/toolbox"
if [ ! -f "${TOOLBOX_SETUP_DIR}/toolbox" ]; then echo "Failed to download toolbox"; exit 1; fi
echo "--- Starting Toolbox Server ---"
cd "${TOOLBOX_SETUP_DIR}"
./toolbox --tools-file ./tools.yaml &
TOOLBOX_PID=$!
cd "/workspace"
sleep 5
cleanup_all() {
kill $TOOLBOX_PID || true
kill $PROXY_PID || true
}
trap cleanup_all EXIT
for ORCH_DIR in "$QUICKSTART_PYTHON_DIR"/*/; do
if [ ! -d "$ORCH_DIR" ]; then
continue
fi
(
set -e
ORCH_NAME=$(basename "$ORCH_DIR")
cleanup_orch() {
psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME" -c "DROP TABLE IF EXISTS $TABLE_NAME;"
if [ -d ".venv" ]; then
rm -rf ".venv"
fi
}
trap cleanup_orch EXIT
cd "$ORCH_DIR"
psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME" <<EOF
CREATE TABLE $TABLE_NAME (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
location VARCHAR NOT NULL,
price_tier VARCHAR NOT NULL,
checkin_date DATE NOT NULL,
checkout_date DATE NOT NULL,
booked BIT NOT NULL
);
INSERT INTO $TABLE_NAME (id, name, location, price_tier, checkin_date, checkout_date, booked)
VALUES
(1, 'Hilton Basel', 'Basel', 'Luxury', '2024-04-22', '2024-04-20', B'0'),
(2, 'Marriott Zurich', 'Zurich', 'Upscale', '2024-04-14', '2024-04-21', B'0'),
(3, 'Hyatt Regency Basel', 'Basel', 'Upper Upscale', '2024-04-02', '2024-04-20', B'0'),
(4, 'Radisson Blu Lucerne', 'Lucerne', 'Midscale', '2024-04-24', '2024-04-05', B'0'),
(5, 'Best Western Bern', 'Bern', 'Upper Midscale', '2024-04-23', '2024-04-01', B'0'),
(6, 'InterContinental Geneva', 'Geneva', 'Luxury', '2024-04-23', '2024-04-28', B'0'),
(7, 'Sheraton Zurich', 'Zurich', 'Upper Upscale', '2024-04-27', '2024-04-02', B'0'),
(8, 'Holiday Inn Basel', 'Basel', 'Upper Midscale', '2024-04-24', '2024-04-09', B'0'),
(9, 'Courtyard Zurich', 'Zurich', 'Upscale', '2024-04-03', '2024-04-13', B'0'),
(10, 'Comfort Inn Bern', 'Bern', 'Midscale', '2024-04-04', '2024-04-16', B'0');
EOF
VENV_DIR=".venv"
python3 -m venv "$VENV_DIR"
source "$VENV_DIR/bin/activate"
if [ -f "requirements.txt" ]; then
pip install -r requirements.txt
else
echo "Warning: requirements.txt not found. Skipping."
fi
echo "Running tests for $ORCH_NAME..."
pytest
)
done

57
.ci/quickstart_py.yaml Normal file
View File

@@ -0,0 +1,57 @@
# 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.
substitutions:
_GCP_PROJECT: "your-project-id"
_CLOUD_SQL_INSTANCE: "project-id:region:instance-name"
_DATABASE_NAME: "db-name"
_DB_USER: "db-user"
_TOOLS_YAML_SECRET: "tools yaml secret"
_API_KEY_SECRET: "api key secret"
_DB_PASS_SECRET: "db pass secret"
_GCP_PROJECT_NUMBER: "your-project-number"
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: 'run-psql-commands'
entrypoint: 'bash'
args:
- -c
- |
set -ex
export VERSION=$(cat ./cmd/version.txt)
chmod +x .ci/quickstart_py.sh
.ci/quickstart_py.sh
env:
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
- 'GCP_PROJECT=${_GCP_PROJECT}'
- 'DATABASE_NAME=${_DATABASE_NAME}'
- 'DB_USER=${_DB_USER}'
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
availableSecrets:
secretManager:
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/latest
env: 'TOOLS_YAML_CONTENT'
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
env: 'GOOGLE_API_KEY'
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
env: 'DB_PASSWORD'
timeout: 1800s
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -0,0 +1,3 @@
Hilton Basel
Hyatt Regency
book

View File

@@ -0,0 +1,38 @@
import os
import pytest
from pathlib import Path
import asyncio
from quickstart import main
@pytest.fixture(scope="module")
def golden_keywords():
"""Loads expected keywords from the golden.txt file."""
golden_file_path = Path("../../golden.txt")
if not golden_file_path.exists():
pytest.fail(f"Golden file not found: {golden_file_path}")
try:
with open(golden_file_path, 'r') as f:
return [line.strip() for line in f.readlines() if line.strip()]
except Exception as e:
pytest.fail(f"Could not read golden.txt: {e}")
# --- Execution Tests ---
class TestADKExecution:
"""Test ADK framework execution and output validation."""
@pytest.fixture(scope="function")
def script_output(self, capsys):
"""Run the quickstart function and return its output."""
asyncio.run(main())
return capsys.readouterr()
def test_script_runs_without_errors(self, script_output):
"""Test that the script runs and produces no stderr."""
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
def test_keywords_in_output(self, script_output, golden_keywords):
"""Test that expected keywords are present in the script's output."""
output = script_output.out
missing_keywords = [kw for kw in golden_keywords if kw not in output]
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"

View File

@@ -0,0 +1,3 @@
google-adk==0.1.0
toolbox-core==0.5.0
pytest==7.0.0

View File

@@ -29,7 +29,7 @@ queries = [
"My check in dates for my booking would be from April 10, 2024 to April 19, 2024.",
]
async def main():
async def run_application():
async with ToolboxClient("http://127.0.0.1:5000") as toolbox_client:
# The toolbox_tools list contains Python callables (functions/methods) designed for LLM tool-use
@@ -105,4 +105,4 @@ async def main():
history.append(final_model_response_content)
print(response2.text)
asyncio.run(main())
asyncio.run(run_application())

View File

@@ -0,0 +1,38 @@
import os
import pytest
from pathlib import Path
import asyncio
from quickstart import main
@pytest.fixture(scope="module")
def golden_keywords():
"""Loads expected keywords from the golden.txt file."""
golden_file_path = Path("../../golden.txt")
if not golden_file_path.exists():
pytest.fail(f"Golden file not found: {golden_file_path}")
try:
with open(golden_file_path, 'r') as f:
return [line.strip() for line in f.readlines() if line.strip()]
except Exception as e:
pytest.fail(f"Could not read golden.txt: {e}")
# --- Execution Tests ---
class TestADKExecution:
"""Test ADK framework execution and output validation."""
@pytest.fixture(scope="function")
def script_output(self, capsys):
"""Run the quickstart function and return its output."""
asyncio.run(main())
return capsys.readouterr()
def test_script_runs_without_errors(self, script_output):
"""Test that the script runs and produces no stderr."""
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
def test_keywords_in_output(self, script_output, golden_keywords):
"""Test that expected keywords are present in the script's output."""
output = script_output.out
missing_keywords = [kw for kw in golden_keywords if kw not in output]
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"

View File

@@ -0,0 +1,3 @@
google-genai==0.6.0
toolbox-core==0.5.0
pytest==7.0.0

View File

@@ -31,7 +31,7 @@ queries = [
"My check in dates would be from April 10, 2024 to April 19, 2024.",
]
async def main():
async def run_application():
# TODO(developer): replace this with another model if needed
model = ChatVertexAI(model_name="gemini-2.0-flash-001")
# model = ChatGoogleGenerativeAI(model="gemini-2.0-flash-001")

View File

@@ -0,0 +1,38 @@
import os
import pytest
from pathlib import Path
import asyncio
from quickstart import main
@pytest.fixture(scope="module")
def golden_keywords():
"""Loads expected keywords from the golden.txt file."""
golden_file_path = Path("../../golden.txt")
if not golden_file_path.exists():
pytest.fail(f"Golden file not found: {golden_file_path}")
try:
with open(golden_file_path, 'r') as f:
return [line.strip() for line in f.readlines() if line.strip()]
except Exception as e:
pytest.fail(f"Could not read golden.txt: {e}")
# --- Execution Tests ---
class TestADKExecution:
"""Test ADK framework execution and output validation."""
@pytest.fixture(scope="function")
def script_output(self, capsys):
"""Run the quickstart function and return its output."""
asyncio.run(main())
return capsys.readouterr()
def test_script_runs_without_errors(self, script_output):
"""Test that the script runs and produces no stderr."""
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
def test_keywords_in_output(self, script_output, golden_keywords):
"""Test that expected keywords are present in the script's output."""
output = script_output.out
missing_keywords = [kw for kw in golden_keywords if kw not in output]
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"

View File

@@ -0,0 +1,5 @@
langchain==0.2.0
langchain-google-vertexai==1.0.0
langgraph==0.2.0
toolbox-langchain==0.5.0
pytest==7.0.0

View File

@@ -0,0 +1,38 @@
import os
import pytest
from pathlib import Path
import asyncio
from quickstart import main
@pytest.fixture(scope="module")
def golden_keywords():
"""Loads expected keywords from the golden.txt file."""
golden_file_path = Path("../../golden.txt")
if not golden_file_path.exists():
pytest.fail(f"Golden file not found: {golden_file_path}")
try:
with open(golden_file_path, 'r') as f:
return [line.strip() for line in f.readlines() if line.strip()]
except Exception as e:
pytest.fail(f"Could not read golden.txt: {e}")
# --- Execution Tests ---
class TestADKExecution:
"""Test ADK framework execution and output validation."""
@pytest.fixture(scope="function")
def script_output(self, capsys):
"""Run the quickstart function and return its output."""
asyncio.run(main())
return capsys.readouterr()
def test_script_runs_without_errors(self, script_output):
"""Test that the script runs and produces no stderr."""
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
def test_keywords_in_output(self, script_output, golden_keywords):
"""Test that expected keywords are present in the script's output."""
output = script_output.out
missing_keywords = [kw for kw in golden_keywords if kw not in output]
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"

View File

@@ -0,0 +1,4 @@
llama-index==0.10.0
llama-index-llms-google-genai==0.1.0
toolbox-llamaindex==0.5.0
pytest==7.0.0