Skip to content

single_turn workflow nodes overwrite explicit include_contents #6046

@spectaclehong

Description

@spectaclehong

🔴 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

Describe the Bug:

When an LlmAgent is run as a workflow node, ADK defaults the agent mode to single_turn when agent.mode is None. That default behavior is useful.

However, the workflow wrapper also forces every single_turn node to include_contents = "none", even when the user explicitly configured include_contents = "default".

This changes the user-provided agent configuration at runtime and makes it impossible for a single_turn workflow node to intentionally receive relevant conversation/session history.

Steps to Reproduce:

  1. Create an LlmAgent with mode="single_turn" and explicit include_contents="default":
from google.adk.agents import LlmAgent

agent = LlmAgent(
    name="planner",
    mode="single_turn",
    include_contents="default",
)
  1. Use the agent as a workflow node.
  2. Run the workflow node through the workflow wrapper.
  3. Inspect the agent's include_contents behavior during execution.

Expected Behavior:

single_turn workflow nodes should default to include_contents="none" only when the user did not explicitly configure include_contents.

If a user explicitly sets include_contents, ADK should preserve that value.

Expected behavior:

Agent config Workflow behavior
mode=None, no explicit include_contents default to mode="single_turn" and include_contents="none"
mode="single_turn", no explicit include_contents default to include_contents="none"
mode="single_turn", include_contents="default" preserve "default"
mode="single_turn", include_contents="none" preserve "none"

Observed Behavior:

The workflow wrapper overwrites the explicit include_contents="default" value with "none".

In src/google/adk/workflow/_llm_agent_wrapper.py:

if agent.mode == 'single_turn':
  agent.include_contents = 'none'

This runs for all single_turn agents, including agents constructed with an explicit include_contents value.

Environment Details:

  • ADK Library Version (pip show google-adk): source checkout from main
  • Desktop OS: macOS
  • Python Version (python -V): Python 3.14.3

Model Information:

  • Are you using LiteLLM: N/A
  • Which model is being used: N/A

🟡 Optional Information

Providing this information greatly speeds up the resolution process.

Regression:

N/A.

Logs:

N/A.

Screenshots / Video:

N/A.

Additional Context:

Some workflow nodes are deterministic one-shot LLM calls, so single_turn mode is semantically correct. But they may still need relevant prior workflow/session events as model context.

Using chat mode as a workaround changes workflow semantics:

  • chat-mode nodes are treated differently by workflow graph construction
  • chat mode can imply wait_for_output=True
  • chat mode is designed for coordinator-style conversation/task delegation
  • the node is no longer clearly expressing "one model call for this workflow step"

So the desired setup is:

LlmAgent(
    name="planner",
    mode="single_turn",
    include_contents="default",
)

That should mean "run once as a workflow node, but include relevant session history." Today it silently becomes include_contents="none".

Root cause: the wrapper applies a mode-based override after resolving default mode:

if agent.mode is None:
  agent.mode = 'single_turn'

...

if agent.mode == 'single_turn':
  agent.include_contents = 'none'

The issue is not the single_turn default itself. The issue is that the override does not distinguish between:

  1. include_contents omitted by the user
  2. include_contents explicitly configured by the user

Since LlmAgent.include_contents has a Pydantic default of "default", a simple truthiness check such as if not agent.include_contents is not sufficient. The code should check whether the field was explicitly set, e.g. via agent.model_fields_set / __pydantic_fields_set__.

Proposed fix:

include_contents_explicit = "include_contents" in agent.model_fields_set

if agent.mode == "single_turn" and not include_contents_explicit:
  agent.include_contents = "none"

This preserves the current default behavior while respecting explicit user configuration.

Minimal Reproduction Code:

from google.adk.agents import LlmAgent

agent = LlmAgent(
    name="planner",
    mode="single_turn",
    include_contents="default",
)

# Use this agent as a workflow node.
# Expected: include_contents remains "default".
# Current behavior: workflow execution forces include_contents to "none".

Suggested tests:

  1. LlmAgent(name="a") used as a workflow node:

    • mode defaults to single_turn
    • include contents defaults to none
  2. LlmAgent(name="a", mode="single_turn"):

    • include contents defaults to none
  3. LlmAgent(name="a", mode="single_turn", include_contents="default"):

    • include contents remains default
  4. LlmAgent(name="a", mode="single_turn", include_contents="none"):

    • include contents remains none

Also verify that cloning through workflow.node(agent) preserves model_fields_set for include_contents. In current behavior, it appears to do so:

node(LlmAgent(name="b", include_contents="default")).model_fields_set
# includes "include_contents"

How often has this issue occurred?:

  • Always (100%)

Metadata

Metadata

Labels

request clarification[Status] The maintainer need clarification or more information from the authorworkflow[Component] This issue is related to ADKworkflow

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions