From f9d853a10eea6f1bad73b614e8f923ef6a6b4c66 Mon Sep 17 00:00:00 2001 From: Shahin Saadati Date: Wed, 19 Aug 2026 11:06:24 -0700 Subject: [PATCH 1/2] Add the Kotlin tab for the BigQuery agent analytics quickstart adk-kotlin 0.8.0 ships BigQueryAgentAnalyticsPlugin, so the quickstart's setup group can carry a Kotlin tab alongside Python and Java. Transcluded, so CI compiles and lints it. The tab says plainly what the Kotlin plugin does not do, because a bare third tab under this page's overview would promise far more than it delivers. It logs INVOCATION_STARTING and INVOCATION_COMPLETED only - not the LLM, tool, state or HITL events the page's table lists - fills the identity columns and content while leaving trace_id, latency_ms and attributes null, and writes rows one at a time through insertAll synchronously on the invocation path, not asynchronously through the Storage Write API the page describes. Grounded in BigQueryAgentAnalyticsPlugin.kt at the v0.8.0 tag, not the working tree. Only the setup group gets Kotlin. The page's six other groups cover event payloads and configuration surface the Kotlin plugin does not have. The plugin lives in the integrations module, so examples/kotlin needs that artifact to compile the snippet. One line is enough: unlike the a2a artifact, google-adk-kotlin-integrations publishes google-cloud-bigquery and google-auth on jvmApiElements, so the types its constructor defaults name are already on the compile classpath. Verified with the snippet ladder: L0 symbols, L1 compile, L2 ktlint, L3 transclusions, L5 registration and L6 badge all pass against the 0.8.0 pin. --- docs/integrations/bigquery-agent-analytics.md | 21 ++++++- examples/kotlin/build.gradle.kts | 5 ++ .../integrations/BigQueryAnalyticsExample.kt | 62 +++++++++++++++++++ tools/kotlin-snippets/files_to_test.txt | 1 + 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt diff --git a/docs/integrations/bigquery-agent-analytics.md b/docs/integrations/bigquery-agent-analytics.md index 37ae1e7dad..4da3587673 100644 --- a/docs/integrations/bigquery-agent-analytics.md +++ b/docs/integrations/bigquery-agent-analytics.md @@ -8,7 +8,7 @@ catalog_tags: ["observability", "google"] # BigQuery Agent Analytics plugin for ADK
- Supported in ADKPython v1.21.0Java v1.5.0 + Supported in ADKPython v1.21.0Java v1.5.0Kotlin v0.8.0
The BigQuery Agent Analytics Plugin significantly enhances Agent Development Kit @@ -193,6 +193,25 @@ shows the BigQuery view optionally created when } ``` +=== "Kotlin" + + Add the plugin to your agent's `App` object. For prerequisites, see + [Prerequisites](#prerequisites). The plugin ships outside core, in + `com.google.adk:google-adk-kotlin-integrations`, and is JVM-only. + + ```kotlin title="BigQueryAnalyticsExample.kt" + --8<-- "examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt:quickstart" + ``` + + The Kotlin plugin logs a deliberately narrow slice of what the Python and + Java plugins do. It records `INVOCATION_STARTING` and `INVOCATION_COMPLETED` + only — none of the LLM, tool, state or HITL events in the table above — and + populates the identity columns plus `content`, leaving `trace_id`, + `latency_ms`, `attributes` and the rest null. Rows are written one at a time + with the `insertAll` streaming API, synchronously on the invocation path, so + each write adds latency to the turn rather than being batched away by the + Storage Write API. + ### Run and test agent diff --git a/examples/kotlin/build.gradle.kts b/examples/kotlin/build.gradle.kts index b057c5cd7f..bbca101f99 100644 --- a/examples/kotlin/build.gradle.kts +++ b/examples/kotlin/build.gradle.kts @@ -30,6 +30,11 @@ dependencies { // own catalog; the spec and jsonrpc transport arrive transitively. implementation("com.google.adk:google-adk-kotlin-a2a:0.8.0") implementation("org.a2aproject.sdk:a2a-java-sdk-client:1.0.0.Final") + // BigQueryAgentAnalyticsPlugin lives in the integrations module. Unlike the + // a2a artifact above, this one publishes google-cloud-bigquery and + // google-auth on jvmApiElements, so the BigQuery types its constructor + // defaults name arrive on the compile classpath with no second line. + implementation("com.google.adk:google-adk-kotlin-integrations:0.8.0") implementation("com.google.cloud:google-cloud-storage:2.48.2") implementation("io.opentelemetry:opentelemetry-sdk:1.56.0") implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.56.0") diff --git a/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt b/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt new file mode 100644 index 0000000000..bce499703c --- /dev/null +++ b/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2026 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. + */ + +package com.google.adk.kt.examples.integrations + +// --8<-- [start:quickstart] +import com.google.adk.kt.agents.Instruction +import com.google.adk.kt.agents.LlmAgent +import com.google.adk.kt.apps.App +import com.google.adk.kt.models.Gemini +import com.google.adk.kt.plugins.agentanalytics.BigQueryAgentAnalyticsPlugin +import com.google.adk.kt.plugins.agentanalytics.BigQueryLoggerConfig + +val analyticsAgent = + LlmAgent( + name = "my_agent", + model = Gemini(name = "gemini-flash-latest"), + instruction = Instruction("You are a helpful assistant."), + ) + +/** + * Wraps [analyticsAgent] in an [App] whose invocations are logged to BigQuery. + * + * The plugin creates the day-partitioned table on first use, so the credentials + * in scope need permission to create a table in the dataset, not only to insert + * rows. Without explicit `credentials`, application default credentials are used. + */ +fun analyticsApp( + projectId: String, + datasetId: String, +): App { + val plugin = + BigQueryAgentAnalyticsPlugin( + config = + BigQueryLoggerConfig( + projectId = projectId, + datasetId = datasetId, + // Optional; defaults to "agent_events". + tableName = "agent_events", + ), + ) + + return App( + appName = "my_agent", + rootAgent = analyticsAgent, + plugins = listOf(plugin), + ) +} +// --8<-- [end:quickstart] diff --git a/tools/kotlin-snippets/files_to_test.txt b/tools/kotlin-snippets/files_to_test.txt index b1902c362c..794f972e9d 100644 --- a/tools/kotlin-snippets/files_to_test.txt +++ b/tools/kotlin-snippets/files_to_test.txt @@ -40,3 +40,4 @@ snippets/tools/overview/UserPreferenceTools.kt snippets/tools/overview/CustomerSupport.kt snippets/tools/overview/DocAnalysisTools.kt snippets/tools/overview/OrderTools.kt +snippets/integrations/BigQueryAnalyticsExample.kt From 8881ac5ed560ecaa1265c4532944aa361e6cddf3 Mon Sep 17 00:00:00 2001 From: Shahin Saadati Date: Wed, 19 Aug 2026 12:33:31 -0700 Subject: [PATCH 2/2] Scope the Kotlin BigQuery claims to what the plugin actually does Review of the branch turned up five over-claims, all of the same kind: the page describes the Python and Java plugins, and adding a Kotlin badge and tab quietly extended every one of those promises to Kotlin. - The page-level badge advertised Kotlin next to Python and Java on a page whose opening promises Auto Schema Upgrade, tool provenance, HITL tracing, view creation, ADK 2.0 workflow events and drop stats. Kotlin implements none of them: BigQueryAgentAnalyticsPlugin overrides two Plugin callbacks. The correction lived only inside the Kotlin tab, which a reader on the Python tab never renders, so it moves to a page-level "Kotlin support" note next to the pricing warning, following the "Java support" note this page already uses. - The page says ingestion goes through the Storage Write API and links its pricing. Kotlin calls tabledata.insertAll, a different billing line: charged per inserted row with a 1 KB minimum and no monthly free tier, so cost tracks invocation count, not bytes. - BigQuerySchema creates no views, so the v_* names in the captured-events table do not exist for Kotlin. A reader would have queried v_invocation_completed and got a not-found. - Configuration options is Python and Java only. Kotlin's whole surface is BigQueryLoggerConfig's six fields, now listed, and `location` (default "US") was undiscoverable - the snippet takes it as a parameter instead of pinning a no-op tableName that already matches the default. - Every logging failure is swallowed: a table that cannot be created or a row that cannot be inserted is logged and the turn continues, so a misconfigured agent looks healthy while writing nothing. A second review pass caught a defect in the first pass's own fix: it told readers to raise the log level for `bigquery_agent_analytics`, which is the plugin's ADK name, not its logger. FloggerLoggingProvider names loggers with kClass.java.name, so the text now gives the class name. Verified: ./tools/kotlin-snippets/runner.sh build and lint both PASS on the snippet (JDK 17), check_kotlin_snippets.sh passes, verify_snippets.py L0-L6 all pass, and the page was rendered with the repo's own markdown extension set to confirm the Kotlin tab joins the Python/Java tabbed set and the note renders as an admonition rather than stray text. --- docs/integrations/bigquery-agent-analytics.md | 50 +++++++++++++++---- .../integrations/BigQueryAnalyticsExample.kt | 8 ++- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/docs/integrations/bigquery-agent-analytics.md b/docs/integrations/bigquery-agent-analytics.md index 4da3587673..832c222233 100644 --- a/docs/integrations/bigquery-agent-analytics.md +++ b/docs/integrations/bigquery-agent-analytics.md @@ -7,7 +7,7 @@ catalog_tags: ["observability", "google"] # BigQuery Agent Analytics plugin for ADK -
+
Supported in ADKPython v1.21.0Java v1.5.0Kotlin v0.8.0
@@ -58,6 +58,22 @@ The plugin includes three reliability and observability fixes: For information on costs, see the [BigQuery documentation](https://cloud.google.com/bigquery/pricing?e=48754805&hl=en#data-ingestion-pricing). +!!! note "Kotlin support" + + The **Kotlin** plugin covers a small subset of this page. It logs + `INVOCATION_STARTING` and `INVOCATION_COMPLETED` only; it fills the identity + columns and `content`, leaving `trace_id`, `span_id`, `latency_ms`, + `attributes` and the rest null; and it creates **no views**, so the `v_*` + views in the table below do not exist for Kotlin. Auto Schema Upgrade, tool + provenance, HITL tracing, drop stats and the ADK 2.0 workflow events are not + implemented. + + It also ingests differently: rows go one at a time through + `tabledata.insertAll`, synchronously on the invocation path, not through the + gRPC Storage Write API described above. Those are separate billing lines: + inserted rows are charged with a 1 KB minimum each and no monthly free tier, + so cost scales with invocation count rather than bytes. + ## Use cases - **Agent workflow debugging and analysis:** Capture a wide range of *plugin @@ -196,21 +212,33 @@ shows the BigQuery view optionally created when === "Kotlin" Add the plugin to your agent's `App` object. For prerequisites, see - [Prerequisites](#prerequisites). The plugin ships outside core, in - `com.google.adk:google-adk-kotlin-integrations`, and is JVM-only. + [Prerequisites](#prerequisites). The plugin is JVM-only and ships outside + core, so add the integrations artifact: + + ```kotlin title="build.gradle.kts" + implementation("com.google.adk:google-adk-kotlin-integrations:0.8.0") + ``` ```kotlin title="BigQueryAnalyticsExample.kt" --8<-- "examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt:quickstart" ``` - The Kotlin plugin logs a deliberately narrow slice of what the Python and - Java plugins do. It records `INVOCATION_STARTING` and `INVOCATION_COMPLETED` - only — none of the LLM, tool, state or HITL events in the table above — and - populates the identity columns plus `content`, leaving `trace_id`, - `latency_ms`, `attributes` and the rest null. Rows are written one at a time - with the `insertAll` streaming API, synchronously on the invocation path, so - each write adds latency to the turn rather than being batched away by the - Storage Write API. + `BigQueryLoggerConfig` is the whole Kotlin configuration surface — + `projectId`, `datasetId`, `enabled` (default `true`), `location` (default + `"US"`, passed to the BigQuery client), `tableName` (default + `"agent_events"`) and `credentials` (default: application default + credentials). The options under [Configuration + options](#configuration-options) are Python and Java only. + + **Logging failures are swallowed.** If the table cannot be created or a row + cannot be inserted, the plugin logs and the invocation continues, so a + misconfigured agent looks healthy while writing nothing. When rows are + missing, raise the log level for + `com.google.adk.kt.plugins.agentanalytics.BigQueryAgentAnalyticsPlugin` — + logs are emitted under that class name, not under the plugin's ADK name. + Note also that Kotlin writes `content` as + `{"message": "Invocation started"}` rather than the `{}` shown for these two + event types below. ### Run and test agent diff --git a/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt b/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt index bce499703c..eeafd52b9d 100644 --- a/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt +++ b/examples/kotlin/snippets/integrations/BigQueryAnalyticsExample.kt @@ -37,10 +37,14 @@ val analyticsAgent = * The plugin creates the day-partitioned table on first use, so the credentials * in scope need permission to create a table in the dataset, not only to insert * rows. Without explicit `credentials`, application default credentials are used. + * + * Logging failures never fail the turn: a table that cannot be created, or a row + * that cannot be inserted, is logged and the invocation carries on. */ fun analyticsApp( projectId: String, datasetId: String, + datasetLocation: String, ): App { val plugin = BigQueryAgentAnalyticsPlugin( @@ -48,8 +52,8 @@ fun analyticsApp( BigQueryLoggerConfig( projectId = projectId, datasetId = datasetId, - // Optional; defaults to "agent_events". - tableName = "agent_events", + // Defaults to "US"; pass your dataset's location instead. + location = datasetLocation, ), )