Merge https://github.com/google/adk-python/pull/6698
Adds a `retention` parameter to `MultimodalToolResultsPlugin` to allow
tool-returned parts to persist across turns in a session.
Fixes#6695
PiperOrigin-RevId: 968187977
Add an unconditional per-row event_id assigned before enqueue so Storage Write
API retry duplicates are identifiable, and add an opt-in exactly_once_delivery
mode that uses one loop-local committed stream with explicit offsets, sticky
ambiguous-send state, an offset_conflict drop bucket, and non-blocking stream
rotation. Expose finish_reason and sanitized error_message on final LLM
responses only, so progressive SSE does not double count. Emit NODE_OUTPUT and
NODE_ERROR for final workflow-node results while keeping model finish and block
diagnostics classified as LLM_RESPONSE. Remove the dead module-level
OpenTelemetry tracer allocation.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 962420983
Cached token counts alone cannot distinguish Gemini provider-side
implicit prefix caching from ADK-managed explicit CachedContent. Derive a
cache_type (explicit/implicit/none) on the final response and expose it
in the BigQuery analytics view so the two can be reported separately.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 962267586
Implements an architectural fix to eliminate the 10-second post-agent latency bottleneck. Skips the synchronous flush() during the run-end callbacks when the decoupling feature flag is enabled. The gRPC response returns instantly, and logs are safely drained by the autonomous background batch processor.
Note on presubmits: The failure in local_integration_test_guitar (test_freeform_chat_discovery_multi_query) is a known baseline flake that passes on retry and is unrelated to this change.
PiperOrigin-RevId: 960491696
- Wrap main Click group execution with a custom TelemetryGroup class to track CLI execution metrics.
- Record command name, subcommand, flags, duration, exit code, and exception type when consent is enabled.
- Exclude 'telemetry' command from metrics logging.
Co-authored-by: Lucas Kang <lucaskang@google.com>
PiperOrigin-RevId: 956018260
Five tests passed vacuously. The plugin logs and swallows exceptions from
shutdown() and from on_event_callback, so mocks that violated the real
collaborator contract aborted the code under test instead of failing the test.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 955359560
Restore private `_scoped_failure_counters` and `_lock` properties
on `ReflectAndRetryToolPlugin` for backward compatibility.
Keep scoped failure counters after creation.
Co-authored-by: Jason Zhang <jasoncz@google.com>
PiperOrigin-RevId: 955323115
Harden BigQueryAgentAnalyticsPlugin so it fails closed around formatter and
parser output, redacts sensitive mappings, diagnostic text, signed URIs, and
raw prompt text before inline or GCS storage, validates existing table schema
type and mode recursively at startup, and reports shutdown success only after
all owned work, clients, and executors are drained or closed. Queue accounting
is O(1), JSON nesting is bounded consistently across Python versions, and remote
drains are created and finalized on their owning event loop.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 954858186
Introduces the `ReflectAndRetryModelPlugin`, which provides error recovery
for model failures (such as malformed function calls). When a configured model
error occurs, the plugin intercepts it, provides reflection guidance to
the model via a reserved tool call, and retries the operation.
Key features:
- Configurable max retries for model errors.
- Customizable list of FinishReasons to treat as errors.
- Support for both invocation-level and global-level tracking scopes.
- Interception of direct calls to the reserved retry tool to prevent misuse.
Co-authored-by: Jason Zhang <jasoncz@google.com>
PiperOrigin-RevId: 954695655
27 tests logged an event and then slept for a fixed 50ms before asserting
on what the batch writer had written. The sleep was standing in for a
barrier, so each of these assertions held only while the runner stayed
faster than the guess, and the suite runs on shared runners under
xdist where it sometimes is not.
The plugin already exposes the barrier these tests want: flush() joins
the write queue, and the queue is only marked done after the write
attempt completes. Several tests in this file already used it. Use it
everywhere the sleep was standing in for it.
The sleeps that remain are doing something else: forcing an interleave
between concurrent tasks, simulating setup latency, or hanging a writer
on purpose to exercise a timeout. Those are left alone.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 953909736
Both cross-loop startup tests entered mock.patch.object on the shared
plugin instance from inside each worker thread. patch.object swaps and
restores one attribute on one object and is not thread safe: when two
threads read the original before either installs its mock, both record
that the attribute was absent from the instance, and both delete it on
exit. The second delete raises, so the test failed with
AttributeError: object has no attribute "_lazy_setup"
which is the mock unwinding itself, not anything about coalescing.
Install the mock once from the test thread and let the worker threads
race only on _ensure_started, which is what these tests are for. The
behaviour under test is unchanged: both loops still call in concurrently
and setup still has to coalesce to a single run.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 953746627
Agents that deliver their final answer through a dedicated tool (e.g. a
submit_final_response tool) instead of a plain-text final event do not trigger
the on-event AGENT_RESPONSE path, so the response text is not captured. Add an
opt-in BigQueryLoggerConfig.final_response_tool_names: when a completed tool's
name is in the set, after_tool_callback logs its call args as an AGENT_RESPONSE
event. The default (empty) preserves existing behavior.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 953043855
BatchProcessor.flush() checked Queue.empty(), which only reports whether an item is still waiting, so it could return after a writer dequeued an item but before that write finished. This waits on the queue's unfinished-task count with Queue.join() so flush() blocks until in-flight writes complete.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 951620765
The BigQuery analytics plugin test relied on fixed 10ms sleeps to wait for asynchronous writes, which is not a valid completion signal and produced timing-dependent failures and leaked Queue.get coroutine warnings. This replaces the sleeps with the plugin's flush() synchronization boundary and shuts the loop-owned worker down before closing the event loop.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 951592599
Spawn the threads in these concurrency tests via
google.adk.platform.thread.create_thread instead of constructing
threading.Thread directly. Behavior is unchanged: create_thread returns a
real OS thread, and the tests still use threading.Event/Barrier for
coordination.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 951583146
Close several correctness and privacy gaps in the BigQuery agent analytics
plugin:
- content_formatter failures fail closed with a [FORMATTER_FAILED] sentinel
instead of falling back to the unformatted payload; the exception is logged
without the payload and counted as formatter_failed in get_drop_stats().
- A final sanitizer pass runs over the fully assembled attributes tree before
json.dumps, covering state deltas, custom tags, labels, nested structures,
and temp:-scoped keys. A new _sanitize_json_blob redacts sensitive keys
inside JSON-encoded string blobs (e.g. cached credential JSON): it decodes
container-shaped strings first, enforces max_content_length before
json.loads, and fails closed to [UNPARSEABLE_JSON_BLOB] for anything it
cannot parse and verify.
- GCS offload paths are call-local: parse() and _parse_content_object() take
trace_id/span_id as keyword arguments and build every object path from
those values, so concurrent two-part offloads never collide on an object
name.
- Table readiness becomes a startup requirement with bounded retry:
_ensure_schema_exists raises on failure, and _ensure_started records it,
keeps _started False, and retries on a later event after exponential
backoff (2s to 60s cap). Setup is coalesced across event loops and threads,
so a persistent outage costs one setup RPC per backoff window. Rows
arriving while setup is unavailable are counted as setup_unavailable.
Also: enabled=False is a hard no-op with zero side effects, runtime settings
are validated at construction, and plugin-level drop counters survive
shutdown.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 949235728
Adds two notification-only plugin lifecycle callbacks that close the
error-coverage gap at the agent and runner levels (the model and tool layers
already have error callbacks), giving analytics plugins such as
BigQueryAgentAnalyticsPlugin complete observability of failure paths so
INVOCATION_STARTING / AGENT_STARTING events always get a terminal error event.
Implements RFC #5044; fixes#4863. Fully backward compatible: the new
BasePlugin methods default to no-ops.
Framework:
- BasePlugin: on_agent_error_callback / on_run_error_callback (no-op).
- PluginManager: run_on_agent_error_callback / run_on_run_error_callback plus
_run_notification_callbacks (best-effort: always notifies every plugin,
ignores return values, logs plugin failures, never masks the original
exception).
- base_agent: wrap the full agent lifecycle (before/impl/after callbacks) for
both run_async and run_live and dispatch on_agent_error_callback.
- runners: dispatch on_run_error_callback at every run-level site --
_exec_with_plugin (legacy/live agent), _run_node_async (LlmAgent chat +
workflows) and _run_node_live (live workflows). after_run and compaction
failures on the success path also notify on_run_error. after_* stay
success-only; catches Exception (not BaseException) so cancellation and
early-stop are excluded.
BigQueryAgentAnalyticsPlugin:
- Emit AGENT_ERROR / INVOCATION_ERROR events with error_message and a
(truncatable) error_traceback; failure-path cleanup runs even if logging
fails; v_agent_error / v_invocation_error views expose error_traceback.
- kind-guarded span ownership so a failure in another plugin's
before_agent_callback cannot make BQAA consume the invocation span and
corrupt INVOCATION_ERROR span/latency data.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 945361746
The BigQueryAgentAnalyticsPlugin previously recorded only tool names in the
attributes.tools field of LLM_REQUEST events. Downstream consumers such as
online evaluation need the tool description and parameter schema to judge
whether the model selected and invoked the correct tool.
Emit one structured entry per tool ({name, description?, parameters?}) instead
of a bare name. name is always present; description comes from the tool
(falling back to the FunctionDeclaration description). The parameter schema is
taken from the tool's FunctionDeclaration, preferring parameters_json_schema
(a raw JSON-schema dict) when present -- several tools (MCP, OpenAPI, skill,
node, environment tools) populate only that field and the model adapters prefer
it -- and otherwise falling back to parameters.model_dump(exclude_none=True,
mode="json"). Extraction is best-effort and per-tool, so a tool without a
declaration still contributes name/description and one failing tool never drops
the whole tools attribute. The result is routed through the plugin's existing
truncation + sensitive-key redaction pipeline.
This changes attributes.tools from a JSON array of strings to a JSON array of
objects. No BigQuery table schema migration is required because attributes is a
JSON column and the analytics view reads it with
JSON_QUERY(attributes, '$.tools').
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 943995097
Add usage_thinking_tokens and usage_tool_use_tokens to the LLM_RESPONSE
analytics view, sourced from the usage_metadata proto the plugin already logs
to attributes.usage_metadata (thoughts_token_count and
tool_use_prompt_token_count).
Per the genai GenerateContentResponseUsageMetadata contract, total_token_count =
prompt_token_count + candidates_token_count + tool_use_prompt_token_count +
thoughts_token_count, so thinking and tool-use tokens are separate addends
rather than subsets of prompt/candidates. Surfacing them lets analytics account
for them without double counting. Both fields are optional and resolve to NULL
for models/responses that do not report them, so the change stays
model-agnostic. No plugin logging change is needed since the full usage_metadata
is already persisted to attributes.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 943493796
Add three additive, off-by-default observability controls to
BigQueryAgentAnalyticsPlugin:
- Span-level Cloud Trace correlation: opt in via `enable_otel_correlation`
(default off) to capture the ambient OpenTelemetry span context into
attributes.otel.{span_id,trace_id} when valid (a best-effort join key). The
typed span_id/parent_span_id columns remain the plugin's internal execution
tree; their schema descriptions are corrected to say so and to point
consumers at attributes.otel.span_id for span-level joins. No plugin-owned
span is created.
- custom_metadata_allowlist: capture allowlisted event.custom_metadata keys
(exact keys, or explicit "prefix:*" patterns) into
attributes.custom_metadata.* through the existing truncation + sensitive-key
redaction pipeline. Truncation sets is_truncated; redaction does not.
- payload_column_denylist: project payload columns (content, content_parts,
attributes, latency_ms) out of the table at write time. Applied schema-first
so the table schema, Arrow schema, row dict, and views stay consistent;
identity/correlation columns are protected and raise ValueError. Denying
content_parts disables GCS offload, and denying attributes alongside a
non-empty custom_metadata_allowlist is rejected.
Default behavior is unchanged when none of the three configs are set.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 940629822
This fix allows for more efficient interaction with Context Caching, as Context Windows are removed when they exceed N, rather than immediately when they exceed the desired number.
Adapted to the new baseline (using invocation_start_indices instead of num_model_turns).
Merges https://github.com/google/adk-python/pull/3271
Co-authored-by: Shangjie Chen <deanchen@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3271 from UlookEE:n_sized_sliding_window 25429aa7298c6cdbc93b1ef267475fe358257646
PiperOrigin-RevId: 936165882
Workflow-driven invocations with deterministic nodes leave
InvocationContext.agent as None, so reading ReadonlyContext.agent_name raised
AttributeError and BigQueryAgentAnalyticsPlugin silently dropped the event row.
Resolve the agent column defensively (running agent name, else the source
Event.author, else null) so rows are written regardless of agent being None.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 930097637
Customer-driven minimum producer subset of the ADK 2.0 observability work: the
smallest set of event types and `attributes.adk.*` envelope fields a customer
needs in BigQuery to take ADK 2.0 to production. Producer-only and strictly
additive — no BigQuery row-schema column changes; consumers read base-table JSON.
What lands:
1. `attributes.adk.*` envelope on every ADK-enriched row.
- A1/A2: `schema_version` and `app_name` always.
- A3/C1/C2/C3: `source_event_id`, `node = {path, run_id, parent_run_id}`,
`branch`, `scope = null | {id, kind}` only on rows with an originating
Event. Callback-only rows omit these keys (never fabricated); an omitted
key resolves to SQL NULL via `JSON_VALUE(attributes, '$.adk.<field>')`.
- `node.run_id` / `node.parent_run_id` mirror ADK's `NodeInfo` `@property`
values (parsed from `node_info.path`), read explicitly rather than via
`model_dump`.
2. Four new event types from previously-unlogged `EventActions`/`Event`
surfaces:
- `AGENT_TRANSFER` (`from_agent = event.author`,
`to_agent = actions.transfer_to_agent`).
- `EVENT_COMPACTION` (fractional float-epoch seconds preserved).
- `AGENT_STATE_CHECKPOINT` (both `{agent_state, end_of_agent}` shapes,
inline payload only).
- `TOOL_PAUSED` per `long_running_tool_id`, with HITL-aware `pause_kind`
(via `_HITL_PAUSE_KIND_MAP`, derived from the function-call NAME) and a
`function_call_id` pair key; plus an unmatched-id fallback row.
3. Pair-key resume path: a non-HITL `function_response` arriving in a user
message emits `TOOL_COMPLETED` with `attributes.adk.{pause_kind='tool',
function_call_id}` so the `TOOL_PAUSED` ↔ `TOOL_COMPLETED` join works in
plain SQL.
4. HITL routing preserved: HITL `function_response`s continue routing to
`HITL_*_COMPLETED` only, never `TOOL_COMPLETED`.
5. Action-attribute mirror: `attributes.adk.{route, render_ui_widgets,
rewind_before_invocation_id}` (flat-with-prefix).
6. Cleanup: delete the deprecated, never-invoked `on_state_change_callback`
stub.
`_EVENT_VIEW_DEFS` is extended for the four new types (and `TOOL_COMPLETED`
gains the pair keys), so the plugin's per-event-type views expose the new
fields. `AGENT_RESPONSE` retains its legacy flat `source_event_*` extras for
backward compatibility alongside the canonical `attributes.adk.*` envelope.
Deferred (tracked separately): dedicated `WORKFLOW_NODE_STARTING/COMPLETED`
events, pause-registry `pause_orphan` semantics, oversized-state GCS offload,
OTel `otel_span_id`, and consumer typed views.
Ported from caohy1988/adk-python#6, validated against current ADK HEAD; uses
ADK's `NodeInfo.parent_run_id` for the node envelope's third key.
All paths covered by unit tests.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 929590673
Re-aligns two files with GitHub main so the piper-to-github cutover does not
silently revert them: the _ALLOWED_PICKLE_GLOBALS type annotation and a stray
f-string prefix in skill_toolset.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 928768999
Three related reliability/observability fixes to the BigQuery Agent
Analytics plugin.
1. Dropped-event observability. BigQuery logging is best-effort: events
are dropped when the in-memory queue overflows or a write ultimately
fails, and only a log line records the loss. Track dropped rows in
BatchProcessor by reason (queue_full, arrow_prep_failed,
retry_exhausted, non_retryable, unexpected_error), include the
running total in each drop log line, and expose the counts via
BatchProcessor.get_drop_stats()/dropped_event_count and an
aggregating BigQueryAgentAnalyticsPlugin.get_drop_stats() so a host
can poll them and export to its own monitoring.
2. Cross-region Storage Write API routing. The AppendRows streaming RPC
does not auto-populate the request-routing header, so writes to a
dataset outside the US multiregion could fail with a "session not
found" / stream-not-found error and silently drop every row. Set
x-goog-request-params: write_stream=<stream> on the append_rows call
so the request reaches the region that owns the write stream.
US-multiregion behavior is unchanged.
3. Stop exporting plugin-owned OTel spans. When Agent Engine telemetry
is enabled (GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY=true) with
Cloud Trace export on the global tracer provider, the plugin's
ID-carrier spans were exported alongside the framework's real spans,
producing a duplicate span for every instrumented operation. The
plugin now tracks span_id / trace_id on its own contextvar stack
without creating OTel spans; trace_id is inherited from the ambient
span, so BigQuery rows still join to Cloud Trace by trace_id and the
LLM/tool span_id-sharing contracts are preserved.
All paths covered by unit tests.
Change-Id: Ia7b73d816b14c574ef856a4c88c57243f6f38f7f
Monkey-patches in-scope Python functions to emit an OpenTelemetry span
per call, capturing args/return/exception as span attributes. Scope is
auto-discovered from the agent tree. No-op when the tracer is not
recording.
Change-Id: I103678b2189a75dcd74af51deb75eb4346c20551
AgentTool.run_async spins up a sub-Runner per call that, with the
default include_plugins=True, shares the parent runner's plugin
instances. On exit the sub-Runner's close() tore down those shared
plugins, so long-lived plugins (e.g. observability exporters) hit the
5s plugin_close_timeout and surfaced as
"RuntimeError: Failed to close plugins: 'X': TimeoutError" on the
parent's tool call.
Add PluginManager.set_skip_closing_plugins(value); when set to True,
close() is a no-op. AgentTool calls it on the sub-Runner's
plugin_manager after construction (only when include_plugins=True) so
inherited plugins are no longer closed by the sub-Runner.
Change-Id: I94f81a33e7f6acef728855eaa9237a93a17b66ec
This PR addresses three distinct issues in the BigQuery Agent Analytics Plugin:
1. Fix false-positive fork detection:
When the plugin is deployed via Vertex AI Agent Engine, it undergoes a pickle/unpickle lifecycle which resets `_init_pid` to 0. Previously, `_ensure_started()` would incorrectly detect this as a fork since `os.getpid()` is never 0, causing unnecessary cold-start latency and log noise. The PID check now distinguishes `_init_pid == 0` (unpickled) from a real fork.
2. Correct GCS offload unit mismatch:
Separates the evaluation limits for offloading text content to GCS. It evaluates the byte-based storage guard (`inline_text_limit`) and the character-based truncation limit (`max_length`) independently, preventing mismatched unit comparisons.
3. Add AGENT_RESPONSE logging:
Logs final response events emitted by agents to BigQuery. This explicitly filters out intermediate steps such as function calls/responses, streaming partials, and invisible internal reasoning ("thoughts") so that only the final visible response text is captured.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 910770002
`gemini-1.*` and `gemini-2.0*` models are respectively deprecated and scheduled for shutdown on June 1, 2026. `gemini-2.5*` models are their successors.
No regressions in unit tests:
```
========================================================================================== 5583 passed, 2237 warnings in 84.91s (0:01:24) ===========================================================================================
```
PiperOrigin-RevId: 907663315
Remove `location=self.location` from the `bigquery.Client()` constructor. When the client has no default location and `client.query(sql)` is called without an explicit `location` parameter, the BQ API infers the job location from the dataset referenced in the DDL statement.
Traced through the BQ Python client:
1. `client.query(sql)` — `location` param defaults to `None`, falls back to `self.location` which is also `None`
2. `_to_query_request()` — when `location is None`, the `"location"` key is **not included** in the API request
3. BQ API — infers location from the dataset referenced in `CREATE OR REPLACE VIEW`
**One line of production code changed.** Everything else is test updates.
| Operation | Before | After |
|---|---|---|
| Table CRUD | Works | Same |
| Storage Write API | Works | Same |
| View creation (non-US dataset) | **Silent failure** | **Works** — BQ infers location |
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 905242480
### Changes
**1. Pickle safety — try-preserve, fallback-drop**
`__getstate__` tests whether `_user_credentials` is picklable:
- **Picklable** (service-account, `AnonymousCredentials`): preserved — survives pickle and restored via `__setstate__` so the plugin uses the user's identity after unpickle.
- **Non-picklable** (`compute_engine.Credentials` with `requests.Session`): dropped gracefully — falls back to ADC after unpickle.
`_credentials` (the active/resolved credentials) is always cleared since it may hold resolved ADC state. On unpickle, `__setstate__` restores it from `_user_credentials` when available.
**2. Fork safety — documented user-provided credential limitation**
`_reset_runtime_state()` sets `_credentials = _user_credentials`. For ADC-resolved credentials (`_user_credentials is None`), this clears stale credentials for re-resolution. For user-provided credentials, the original object is kept — we cannot re-create it. The comment documents this: the user is responsible for providing fork-safe credentials.
**3. GCS client — credentials passed correctly**
`GCSOffloader.__init__` always creates a `storage.Client` eagerly (`storage_client or storage.Client(...)` at line 1329). This was also true before the credentials commit. The fix passes explicit credentials when available and lets ADC resolve when not, matching the `bigquery.Client` and `BigQueryWriteAsyncClient` patterns.
**4. Benign race on `_credentials` resolution documented**
When multiple event loops call `_create_loop_state()` concurrently, both can resolve ADC redundantly. This is idempotent and benign, now documented.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 905111076
Default is True to match existing behavior. Setting it to False prevents the model from loading artifact directly from a provided canonical URI, and makes the behavior consistent between InMemoryArtifactService and other services with canonical URIs.
PiperOrigin-RevId: 901061080
Fixes#5073, #5310, and #5311 with three targeted updates to the `BigQueryAgentAnalyticsPlugin` (no changes to ADK core):
- Classifies `TransferToAgentTool` transfers to `RemoteA2aAgent` as `TRANSFER_A2A` instead of the generic `TRANSFER_AGENT` by resolving the target agent at the call level.
- Ensures a self-consistent BigQuery span tree by preferring the plugin's internal span stack over ambient OTel spans, resolving dangling `parent_span_id` references.
- Surfaces remote A2A interaction metadata (`a2a:request`, `a2a:response`, etc.) in BigQuery by detecting them in custom metadata and logging new `A2A_INTERACTION` events.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 900224778
Adds a configurable `view_prefix` field to `BigQueryLoggerConfig` (default `"v"`) so that multiple plugin instances sharing a dataset can use distinct prefixes to avoid overwriting each other's auto-created analytics views
- Validates that `view_prefix` is non-empty at init time
- Wires `view_prefix` into `_create_analytics_views` in place of the hardcoded `"v_"` prefix
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 894331973
Fixes an issue where the Agent Analytics plugin could log plain-text
OAuth credentials and access tokens to BigQuery. Sensitive keys are
now redacted.
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 891963630