Skip to content

Commit 11a2ffe

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: allow setting agent/application name for BigQuery tools
This will allow tracking of tool usage per agent/application. PiperOrigin-RevId: 800607186
1 parent f4a8df0 commit 11a2ffe

File tree

9 files changed

+259
-33
lines changed

9 files changed

+259
-33
lines changed

contributing/samples/bigquery/agent.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@
2525
# Define an appropriate credential type
2626
CREDENTIALS_TYPE = AuthCredentialTypes.OAUTH2
2727

28+
# Define an appropriate application name
29+
BIGQUERY_AGENT_NAME = "adk_sample_bigquery_agent"
30+
2831

2932
# Define BigQuery tool config with write mode set to allowed. Note that this is
3033
# only to demonstrate the full capability of the BigQuery tools. In production
3134
# you may want to change to BLOCKED (default write mode, effectively makes the
3235
# tool read-only) or PROTECTED (only allows writes in the anonymous dataset of a
3336
# BigQuery session) write mode.
34-
tool_config = BigQueryToolConfig(write_mode=WriteMode.ALLOWED)
37+
tool_config = BigQueryToolConfig(
38+
write_mode=WriteMode.ALLOWED, application_name=BIGQUERY_AGENT_NAME
39+
)
3540

3641
if CREDENTIALS_TYPE == AuthCredentialTypes.OAUTH2:
3742
# Initiaze the tools to do interactive OAuth
@@ -64,7 +69,7 @@
6469
# debug CLI
6570
root_agent = LlmAgent(
6671
model="gemini-2.0-flash",
67-
name="bigquery_agent",
72+
name=BIGQUERY_AGENT_NAME,
6873
description=(
6974
"Agent to answer questions about BigQuery data and models and execute"
7075
" SQL queries."

src/google/adk/tools/bigquery/client.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@
2626

2727

2828
def get_bigquery_client(
29-
*, project: Optional[str], credentials: Credentials
29+
*,
30+
project: Optional[str],
31+
credentials: Credentials,
32+
user_agent: Optional[str] = None,
3033
) -> bigquery.Client:
3134
"""Get a BigQuery client."""
3235

33-
client_info = google.api_core.client_info.ClientInfo(user_agent=USER_AGENT)
36+
user_agent = f"{USER_AGENT} {user_agent}" if user_agent else USER_AGENT
37+
38+
client_info = google.api_core.client_info.ClientInfo(user_agent=user_agent)
3439

3540
bigquery_client = bigquery.Client(
3641
project=project, credentials=credentials, client_info=client_info

src/google/adk/tools/bigquery/config.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
from __future__ import annotations
1616

1717
from enum import Enum
18+
from typing import Optional
1819

1920
from pydantic import BaseModel
21+
from pydantic import field_validator
2022

2123
from ...utils.feature_decorator import experimental
2224

@@ -58,4 +60,21 @@ class BigQueryToolConfig(BaseModel):
5860
max_query_result_rows: int = 50
5961
"""Maximum number of rows to return from a query.
6062
61-
By default, the query result will be limited to 50 rows."""
63+
By default, the query result will be limited to 50 rows.
64+
"""
65+
66+
application_name: Optional[str] = None
67+
"""Name of the application using the BigQuery tools.
68+
69+
By default, no particular application name will be set in the BigQuery
70+
interaction. But if the the tool user (agent builder) wants to differentiate
71+
their application/agent for tracking or support purpose, they can set this field.
72+
"""
73+
74+
@field_validator('application_name')
75+
@classmethod
76+
def validate_application_name(cls, v):
77+
"""Validate the application name."""
78+
if v and ' ' in v:
79+
raise ValueError('Application name should not contain spaces.')
80+
return v

src/google/adk/tools/bigquery/metadata_tool.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
from google.cloud import bigquery
1919

2020
from . import client
21+
from .config import BigQueryToolConfig
2122

2223

23-
def list_dataset_ids(project_id: str, credentials: Credentials) -> list[str]:
24+
def list_dataset_ids(
25+
project_id: str, credentials: Credentials, settings: BigQueryToolConfig
26+
) -> list[str]:
2427
"""List BigQuery dataset ids in a Google Cloud project.
2528
2629
Args:
@@ -45,7 +48,9 @@ def list_dataset_ids(project_id: str, credentials: Credentials) -> list[str]:
4548
"""
4649
try:
4750
bq_client = client.get_bigquery_client(
48-
project=project_id, credentials=credentials
51+
project=project_id,
52+
credentials=credentials,
53+
user_agent=settings.application_name,
4954
)
5055

5156
datasets = []
@@ -60,7 +65,10 @@ def list_dataset_ids(project_id: str, credentials: Credentials) -> list[str]:
6065

6166

6267
def get_dataset_info(
63-
project_id: str, dataset_id: str, credentials: Credentials
68+
project_id: str,
69+
dataset_id: str,
70+
credentials: Credentials,
71+
settings: BigQueryToolConfig,
6472
) -> dict:
6573
"""Get metadata information about a BigQuery dataset.
6674
@@ -111,7 +119,9 @@ def get_dataset_info(
111119
"""
112120
try:
113121
bq_client = client.get_bigquery_client(
114-
project=project_id, credentials=credentials
122+
project=project_id,
123+
credentials=credentials,
124+
user_agent=settings.application_name,
115125
)
116126
dataset = bq_client.get_dataset(
117127
bigquery.DatasetReference(project_id, dataset_id)
@@ -125,7 +135,10 @@ def get_dataset_info(
125135

126136

127137
def list_table_ids(
128-
project_id: str, dataset_id: str, credentials: Credentials
138+
project_id: str,
139+
dataset_id: str,
140+
credentials: Credentials,
141+
settings: BigQueryToolConfig,
129142
) -> list[str]:
130143
"""List table ids in a BigQuery dataset.
131144
@@ -144,7 +157,9 @@ def list_table_ids(
144157
"""
145158
try:
146159
bq_client = client.get_bigquery_client(
147-
project=project_id, credentials=credentials
160+
project=project_id,
161+
credentials=credentials,
162+
user_agent=settings.application_name,
148163
)
149164

150165
tables = []
@@ -161,7 +176,11 @@ def list_table_ids(
161176

162177

163178
def get_table_info(
164-
project_id: str, dataset_id: str, table_id: str, credentials: Credentials
179+
project_id: str,
180+
dataset_id: str,
181+
table_id: str,
182+
credentials: Credentials,
183+
settings: BigQueryToolConfig,
165184
) -> dict:
166185
"""Get metadata information about a BigQuery table.
167186
@@ -260,7 +279,9 @@ def get_table_info(
260279
"""
261280
try:
262281
bq_client = client.get_bigquery_client(
263-
project=project_id, credentials=credentials
282+
project=project_id,
283+
credentials=credentials,
284+
user_agent=settings.application_name,
264285
)
265286
return bq_client.get_table(
266287
bigquery.TableReference(

src/google/adk/tools/bigquery/query_tool.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ def execute_sql(
8080
try:
8181
# Get BigQuery client
8282
bq_client = client.get_bigquery_client(
83-
project=project_id, credentials=credentials
83+
project=project_id,
84+
credentials=credentials,
85+
user_agent=settings.application_name,
8486
)
8587

8688
# BigQuery connection properties where applicable

tests/unittests/tools/bigquery/test_bigquery_client.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
from __future__ import annotations
1616

1717
import os
18-
import re
1918
from unittest import mock
2019

20+
import google.adk
2121
from google.adk.tools.bigquery.client import get_bigquery_client
2222
from google.auth.exceptions import DefaultCredentialsError
2323
from google.oauth2.credentials import Credentials
@@ -109,8 +109,8 @@ def test_bigquery_client_project_set_with_env():
109109
assert client.project == "test-gcp-project"
110110

111111

112-
def test_bigquery_client_user_agent():
113-
"""Test BigQuery client user agent."""
112+
def test_bigquery_client_user_agent_default():
113+
"""Test BigQuery client default user agent."""
114114
with mock.patch(
115115
"google.cloud.bigquery.client.Connection", autospec=True
116116
) as mock_connection:
@@ -123,7 +123,33 @@ def test_bigquery_client_user_agent():
123123
# Verify that the tracking user agent was set
124124
client_info_arg = mock_connection.call_args[1].get("client_info")
125125
assert client_info_arg is not None
126-
assert re.search(
127-
r"adk-bigquery-tool google-adk/([0-9A-Za-z._\-+/]+)",
128-
client_info_arg.user_agent,
126+
expected_user_agents = {
127+
"adk-bigquery-tool",
128+
f"google-adk/{google.adk.__version__}",
129+
}
130+
actual_user_agents = set(client_info_arg.user_agent.split())
131+
assert expected_user_agents.issubset(actual_user_agents)
132+
133+
134+
def test_bigquery_client_user_agent_custom():
135+
"""Test BigQuery client custom user agent."""
136+
with mock.patch(
137+
"google.cloud.bigquery.client.Connection", autospec=True
138+
) as mock_connection:
139+
# Trigger the BigQuery client creation
140+
get_bigquery_client(
141+
project="test-gcp-project",
142+
credentials=mock.create_autospec(Credentials, instance=True),
143+
user_agent="custom_user_agent",
129144
)
145+
146+
# Verify that the tracking user agent was set
147+
client_info_arg = mock_connection.call_args[1].get("client_info")
148+
assert client_info_arg is not None
149+
expected_user_agents = {
150+
"adk-bigquery-tool",
151+
f"google-adk/{google.adk.__version__}",
152+
"custom_user_agent",
153+
}
154+
actual_user_agents = set(client_info_arg.user_agent.split())
155+
assert expected_user_agents.issubset(actual_user_agents)

0 commit comments

Comments
 (0)