feat: Propagate application_name set for the BigQuery Tools as BigQuery job labels

This change will help the tools user identify per agent job usage in BQ console and INFORMATION_SCHEMA views. This change fulfills the feature request #3582. Here is a demo after change: screen/C6YB4ge2FM2ZREi.

PiperOrigin-RevId: 834480140
This commit is contained in:
Google Team Member
2025-11-19 16:02:06 -08:00
committed by Copybara-Service
parent 131d39c3db
commit f13a11e1dc
3 changed files with 88 additions and 6 deletions
+3 -1
View File
@@ -80,7 +80,9 @@ class BigQueryToolConfig(BaseModel):
By default, no particular application name will be set in the BigQuery
interaction. But if the tool user (agent builder) wants to differentiate
their application/agent for tracking or support purpose, they can set this field.
their application/agent for tracking or support purpose, they can set this
field. If set, this value will be added to the user_agent in BigQuery API calls, and also to the BigQuery job labels with the key
"adk-bigquery-application-name".
"""
compute_project_id: Optional[str] = None
@@ -71,6 +71,8 @@ def _execute_sql(
bq_job_labels = {}
if caller_id:
bq_job_labels["adk-bigquery-tool"] = caller_id
if settings and settings.application_name:
bq_job_labels["adk-bigquery-application-name"] = settings.application_name
if not settings or settings.write_mode == WriteMode.BLOCKED:
dry_run_query_job = bq_client.query(
@@ -1673,7 +1673,9 @@ def test_execute_sql_job_labels(
query = "SELECT 123 AS num"
statement_type = "SELECT"
credentials = mock.create_autospec(Credentials, instance=True)
tool_settings = BigQueryToolConfig(write_mode=write_mode)
tool_settings = BigQueryToolConfig(
write_mode=write_mode, application_name="test-app"
)
tool_context = mock.create_autospec(ToolContext, instance=True)
tool_context.state.get.return_value = None
@@ -1702,12 +1704,13 @@ def test_execute_sql_job_labels(
for call_args in call_args_list:
_, mock_kwargs = call_args
assert mock_kwargs["job_config"].labels == {
"adk-bigquery-tool": "execute_sql"
"adk-bigquery-tool": "execute_sql",
"adk-bigquery-application-name": "test-app",
}
@pytest.mark.parametrize(
("tool_call", "expected_label"),
("tool_call", "expected_tool_label"),
[
pytest.param(
lambda tool_context: query_tool.forecast(
@@ -1751,7 +1754,7 @@ def test_execute_sql_job_labels(
),
],
)
def test_ml_tool_job_labels(tool_call, expected_label):
def test_ml_tool_job_labels(tool_call, expected_tool_label):
"""Test ML tools for job label."""
with mock.patch.object(bigquery, "Client", autospec=True) as Client:
@@ -1768,10 +1771,85 @@ def test_ml_tool_job_labels(tool_call, expected_label):
for call_args in call_args_list:
_, mock_kwargs = call_args
assert mock_kwargs["job_config"].labels == {
"adk-bigquery-tool": expected_label
"adk-bigquery-tool": expected_tool_label
}
@pytest.mark.parametrize(
("tool_call", "expected_tool_label"),
[
pytest.param(
lambda tool_context: query_tool.forecast(
project_id="test-project",
history_data="SELECT * FROM `test-dataset.test-table`",
timestamp_col="ts_col",
data_col="data_col",
credentials=mock.create_autospec(Credentials, instance=True),
settings=BigQueryToolConfig(
write_mode=WriteMode.ALLOWED, application_name="test-app"
),
tool_context=tool_context,
),
"forecast",
id="forecast-app-name",
),
pytest.param(
lambda tool_context: query_tool.analyze_contribution(
project_id="test-project",
input_data="test-dataset.test-table",
dimension_id_cols=["dim1", "dim2"],
contribution_metric="SUM(metric)",
is_test_col="is_test",
credentials=mock.create_autospec(Credentials, instance=True),
settings=BigQueryToolConfig(
write_mode=WriteMode.ALLOWED, application_name="test-app"
),
tool_context=tool_context,
),
"analyze_contribution",
id="analyze-contribution-app-name",
),
pytest.param(
lambda tool_context: query_tool.detect_anomalies(
project_id="test-project",
history_data="SELECT * FROM `test-dataset.test-table`",
times_series_timestamp_col="ts_timestamp",
times_series_data_col="ts_data",
credentials=mock.create_autospec(Credentials, instance=True),
settings=BigQueryToolConfig(
write_mode=WriteMode.ALLOWED, application_name="test-app"
),
tool_context=tool_context,
),
"detect_anomalies",
id="detect-anomalies-app-name",
),
],
)
def test_ml_tool_job_labels_w_application_name(tool_call, expected_tool_label):
"""Test ML tools for job label with application name."""
with mock.patch.object(bigquery, "Client", autospec=True) as Client:
bq_client = Client.return_value
tool_context = mock.create_autospec(ToolContext, instance=True)
tool_context.state.get.return_value = None
tool_call(tool_context)
expected_labels = {
"adk-bigquery-tool": expected_tool_label,
"adk-bigquery-application-name": "test-app",
}
for call_args_list in [
bq_client.query.call_args_list,
bq_client.query_and_wait.call_args_list,
]:
for call_args in call_args_list:
_, mock_kwargs = call_args
assert mock_kwargs["job_config"].labels == expected_labels
def test_execute_sql_max_rows_config():
"""Test execute_sql tool respects max_query_result_rows from config."""
project = "my_project"