Skip to content

BigQueryAgentAnalyticsPlugin: Pass **kwargs to _log_event for all callbacks (not just model callbacks) #4330

Closed

Description

@maryhamilton

** Please make sure you read the contribution guide and file the issues in the right place. **
Contribution guide.

🔴 Required Information

Please ensure all items in this section are completed to allow for efficient
triaging. Requests without complete information may be rejected / deprioritized.
If an item is not applicable to you - please mark it as N/A

Is your feature request related to a specific problem?

Please describe the problem you are trying to solve. (Ex: "I'm always frustrated
when I have to manually handle X...")

The BigQueryAgentAnalyticsPlugin callback methods inconsistently handle **kwargs. Model callbacks (before_model_callback, after_model_callback) properly pass **kwargs through to _log_event, which serializes them into the BigQuery attributes JSON field. However, agent callbacks, tool callbacks, and run callbacks accept **kwargs but do not pass them to _log_event, effectively dropping any custom attributes.
This makes it impossible to add custom metadata (like customer_id, cost_center, email_address) to non-model events through the documented callback override pattern.

Describe the Solution You'd Like

A clear and concise description of the feature or API change you want.
Be specific about input/outputs if this involves an API change.

All callback methods that accept **kwargs should pass them through to _log_event so custom attributes are serialized into the attributes JSON field for ALL event types.
Affected methods that currently drop **kwargs:

  • before_agent_callback / after_agent_callback
  • before_tool_callback / after_tool_callback
  • before_run_callback / after_run_callback
  • on_user_message_callback
  • on_tool_error_callback

Impact on your work

How does this feature impact your work and what are you trying to achieve?
If this is critical for you, tell us if there is a timeline by when you need
this feature.

We need to track customer_id across all BigQuery analytics events for multi-tenant reporting. Currently, customer_id only appears in LLM_REQUEST and LLM_RESPONSE events, but is missing from AGENT_STARTING, AGENT_COMPLETED, TOOL_STARTING, TOOL_COMPLETED, and INVOCATION_STARTING/COMPLETED events.
This is important for production use but we have a workaround in place.

Willingness to contribute

Are you interested in implementing this feature yourself or submitting a PR?
(Yes/No)

No


🟡 Recommended Information

Describe Alternatives You've Considered

A clear and concise description of any alternative solutions or workarounds
you've considered and why they didn't work for you.

Workaround implemented: We override the internal _log_event method to inject customer_id:

class CustomerAwareBqAnalyticsPlugin(BigQueryAgentAnalyticsPlugin):
    async def _log_event(
        self,
        event_type: str,
        callback_context: CallbackContext,
        raw_content: Any = None,
        is_truncated: bool = False,
        **kwargs: Any,
    ) -> None:
        customer_id = callback_context.state.get("customer_id")
        if customer_id is not None:
            kwargs["customer_id"] = customer_id
        await super()._log_event(event_type, callback_context, raw_content, is_truncated, **kwargs)

This works but is fragile because _log_event is an internal method that may change between versions.

Proposed API / Implementation

If you have ideas on how this should look in code, please share a
pseudo-code example.

For each affected callback, pass **kwargs to _log_event. Example fix for before_agent_callback:

# Current (drops kwargs):
async def before_agent_callback(
    self, *, agent: Any, callback_context: CallbackContext, **kwargs
) -> None:
    TraceManager.init_trace(callback_context)
    TraceManager.push_span(callback_context, "agent")
    await self._log_event(
        "AGENT_STARTING",
        callback_context,
        raw_content=getattr(agent, "instruction", ""),
    )

# Proposed (passes kwargs):
async def before_agent_callback(
    self, *, agent: Any, callback_context: CallbackContext, **kwargs
) -> None:
    TraceManager.init_trace(callback_context)
    TraceManager.push_span(callback_context, "agent")
    await self._log_event(
        "AGENT_STARTING",
        callback_context,
        raw_content=getattr(agent, "instruction", ""),
        **kwargs,  # <-- Add this
    )

Additional Context

Add any other context or screenshots about the feature request here.

The documentation in bigquery_agent_analytics_plugin.py states that any additional keyword arguments should be serialized into the attributes field, but this only works for model callbacks. Making this consistent across all callbacks would match the expected behaviour.

Activity

  1. added
    bq[Component] This issue is related to Big Query integration
    on Jan 30, 2026
  2. added theissue type on Jan 30, 2026
  3. caohy1988 commented on Feb 9, 2026

    @caohy1988

    Thank you for reporting this inconsistency! You are absolutely right—this is a gap in our current implementation.

    Confirmation

    We have verified that the following callbacks indeed drop **kwargs instead of passing them to _log_event, preventing the logging of custom attributes like customer_id for these event types:

    • before_agent_callback / after_agent_callback
    • before_tool_callback / after_tool_callback
    • before_run_callback / after_run_callback
    • on_user_message_callback
    • on_tool_error_callback

    As you noted, only model callbacks (before_model_callback, after_model_callback) currently handle this correctly.

    Potential Fix

    We will implement the fix by updating all affected callbacks to pass **kwargs through to self._log_event.

    Here is how the fix will look for before_agent_callback and before_tool_callback (other callbacks will follow the same pattern):

    # google/adk/plugins/bigquery_agent_analytics_plugin.py
    
        async def before_agent_callback(
            self, *, agent: Any, callback_context: CallbackContext, **kwargs
        ) -> None:
            """Callback before an agent starts processing."""
            TraceManager.init_trace(callback_context)
            TraceManager.push_span(callback_context, "agent")
            await self._log_event(
                "AGENT_STARTING",
                callback_context,
                raw_content=getattr(agent, "instruction", ""),
                **kwargs,  # <--- FIX: Pass kwargs through to capture custom attributes
            )
    
        async def before_tool_callback(
            self,
            *,
            tool: BaseTool,
            tool_args: dict[str, Any],
            tool_context: ToolContext,
            **kwargs,
        ) -> None:
            """Callback before tool execution."""
            args_truncated, is_truncated = _recursive_smart_truncate(
                tool_args, self.config.max_content_length
            )
            content_dict = {"tool": tool.name, "args": args_truncated}
            TraceManager.push_span(tool_context, "tool")
            await self._log_event(
                "TOOL_STARTING",
                tool_context,
                raw_content=content_dict,
                is_truncated=is_truncated,
                **kwargs,  # <--- FIX: Pass kwargs through
            )

    This change ensures that customer_id and any other custom metadata you pass will be correctly serialized into the attributes JSON field in BigQuery for all event types.

    Release Plan

    This fix will be included in the next patch release.

  4. added 3 commits that reference this issue on Feb 10, 2026
    2ce0268
    bb39cb3
    9678094
  5. added 2 commits that reference this issue on Feb 14, 2026
    5b8bf7d
    ff78634
  6. caohy1988 commented on Feb 17, 2026

    @caohy1988

    🔍 Investigation Summary: Callback Dispatch & **kwargs Passthrough

    After further investigation into the ADK callback architecture, we have determined that implementing **kwargs passthrough would be a no-op. The issue stems from a fundamental misunderstanding of how PluginManager dispatches events.

    Why **kwargs passthrough doesn't work

    The ADK BasePlugin defines strict signatures for all callbacks. PluginManager dispatches these events using only these specific named parameters—nothing extra is passed down the chain.

    # BasePlugin signatures — no **kwargs available
    async def before_agent_callback(self, *, agent, callback_context)
    async def after_agent_callback(self, *, agent, callback_context)
    async def before_tool_callback(self, *, tool, tool_args, tool_context)
    async def after_tool_callback(self, *, tool, tool_args, tool_context, result)
    # ... etc

    Even if we were to add **kwargs to our plugin implementations and forward them to _log_event, the resulting dictionary would always be empty because the caller (ADK) never provides additional keyword arguments.

    Note

    Previous observations suggesting that model callbacks passed **kwargs were misleading; in practice, those dictionaries were also empty.


    ✅ Recommended Patterns for Custom Metadata

    If you need to attach metadata to your events, please use one of the following supported patterns:

    1. Static Metadata (Per-Plugin)

    For metadata that remains constant across all events (e.g., environment, team name), use custom_tags in the BigQueryLoggerConfig.

    BigQueryAgentAnalyticsPlugin(
        project_id="my-project",
        dataset_id="analytics",
        config=BigQueryLoggerConfig(
            custom_tags={"cost_center": "team-a", "env": "prod"},
        ),
    )

    2. Dynamic Metadata (Per-Request)

    For metadata that changes based on the request (e.g., customer_id from session state), the correct pattern is to subclass the plugin and override _log_event.

    With the introduction of the EventData dataclass in #4491, _log_event now provides a typed and stable interface for this:

    class CustomerAwareBqPlugin(BigQueryAgentAnalyticsPlugin):
        async def _log_event(self, event_type, callback_context, **kwargs):
            # Extract dynamic data from the context state
            customer_id = callback_context.state.get("customer_id")
            
            if customer_id:
                event_data = kwargs.get("event_data") or EventData()
                event_data.extra_attributes["customer_id"] = customer_id
                kwargs["event_data"] = event_data
                
            await super()._log_event(event_type, callback_context, **kwargs)

    🏁 Resolution: Won't Fix

    We are closing this issue as Won't Fix. Because the ADK callback system does not dispatch extra keyword arguments, the proposed **kwargs passthrough would not provide any additional functionality.

    The requirement for custom per-event metadata is already addressed via the custom_tags configuration and the subclassing pattern demonstrated above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bq[Component] This issue is related to Big Query integration

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions