Skip to content

Python: fix: give the GroupChat orchestrator agent the workflow run kwargs - #8312

Open
Jeremy Schoemaker (shoemoney) wants to merge 1 commit into
microsoft:mainfrom
shoemoney:fix/groupchat-orchestrator-run-kwargs
Open

Python: fix: give the GroupChat orchestrator agent the workflow run kwargs#8312
Jeremy Schoemaker (shoemoney) wants to merge 1 commit into
microsoft:mainfrom
shoemoney:fix/groupchat-orchestrator-run-kwargs

Conversation

@shoemoney

Copy link
Copy Markdown

Motivation & Context

workflow.run(function_invocation_kwargs=..., client_kwargs=...) is stored in workflow state under WORKFLOW_RUN_KWARGS_KEY, and AgentExecutor forwards it to every participant agent. The GroupChat orchestrator agent runs outside AgentExecutor: AgentBasedGroupChatOrchestrator._invoke_agent called self._agent.run() with only messages, session and options.

So a host that puts request-scoped values there, a user id for tool ACL or an id for metrics, had them reach the participants and silently not reach the orchestrator. The orchestrator is an agent like any other, with its own tools and middleware that can read AgentContext.function_invocation_kwargs and FunctionInvocationContext.kwargs, so it is the one agent in the group chat that sees a different view of the run.

Description & Review Guide

  • What are the major changes?

    _invoke_agent now takes the WorkflowContext that both of its callers already hold, reads the same state key AgentExecutor reads, and forwards function_invocation_kwargs and client_kwargs to self._agent.run(...).

    The resolution is not reimplemented. AgentExecutor._prepare_agent_run_args and _resolve_executor_kwargs depended on nothing but self.id, so they move to _agent_utils as prepare_agent_run_args(executor_id, ...) and resolve_executor_kwargs(executor_id, ...), and both AgentExecutor methods stay as one-line delegates. Behavior there is unchanged, including the warning paths for non-dict kwargs.

  • What is the impact of these changes?

    The orchestrator agent gets the same resolution participants get. It is itself an Executor, so passing self.id means __global__ kwargs apply to it, per-executor entries are keyed by its own id, and specific values override global ones, exactly as for participants. A run that declares no kwargs still invokes it with both values as None rather than {}, which the second test pins.

    Two tests are added in packages/orchestrations/tests/test_group_chat.py. The first fails against unpatched sources with

    AssertionError: assert None == {'user_id': 'user-123'}
    

    the orchestrator having been invoked with only {'options': {'response_format': AgentOrchestrationOutput}}.

  • Not changed: the Magentic manager.

    The issue names MagenticManagerBase._complete as the same pattern at a separate call site. Its callers are plan, replan, create_progress_ledger and prepare_final_answer, methods on MagenticManagerBase, which is not an Executor and holds no WorkflowContext. Reaching the run kwargs there means widening a public extension point rather than reading state that is already in hand, so I left it alone; that shape is a call for the team. Happy to follow up with whatever signature you would want.

    Two other notes on scope. The issue is assigned to Eduard van Valkenburg (@eavanvalkenburg), but he currently has 78 open issues assigned, so I read that as triage routing rather than a claim; say the word and I will close this. And python/AGENTS.md asks external contributors to check with the core team before picking up function-calling-loop work. This change does not touch that loop, it only forwards the same two parameters AgentExecutor already forwards, but function_invocation_kwargs is adjacent enough that it is worth saying out loud.

Related Issue

Fixes #8304

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes agent-invocation behavior on a core, cross-cutting path shared by all orchestration patterns, so a human should confirm the run-kwargs forwarding has no unintended downstream effects.

Pull request overview

This PR fixes a bug where the GroupChat orchestrator agent did not receive the per-run function_invocation_kwargs / client_kwargs that Workflow.run stores in state and AgentExecutor forwards to every participant. Because the orchestrator runs outside AgentExecutor (via a direct agent.run(...)), its tools/middleware silently saw a different, incomplete view of request-scoped values (e.g. a user_id for tool ACL). The fix threads the WorkflowContext (already held by both callers) into _invoke_agent, reads the same WORKFLOW_RUN_KWARGS_KEY, and forwards the resolved kwargs. To avoid reimplementing resolution, the two AgentExecutor helpers that depended only on self.id are extracted to shared functions in _agent_utils, with the executor methods kept as thin delegates so existing behavior and tests are preserved.

Changes:

  • Extracted prepare_agent_run_args(executor_id, ...) and resolve_executor_kwargs(executor_id, ...) into _agent_utils.py; AgentExecutor methods now delegate to them.
  • AgentBasedGroupChatOrchestrator._invoke_agent now takes WorkflowContext, resolves run kwargs via the shared helper, and forwards function_invocation_kwargs / client_kwargs to the orchestrator agent.run(...).
  • Added two tests pinning that the orchestrator receives the supplied kwargs, and receives None/None (not {}) when nothing is declared.
File summaries
File Description
python/packages/core/agent_framework/_workflows/_agent_utils.py Adds shared resolve_executor_kwargs and prepare_agent_run_args, moved from AgentExecutor; imports GLOBAL_KWARGS_KEY and a module logger.
python/packages/core/agent_framework/_workflows/_agent_executor.py Reduces the two run-arg helpers to one-line delegates; trims the now-unused GLOBAL_KWARGS_KEY import.
python/packages/orchestrations/agent_framework_orchestrations/_group_chat.py Threads WorkflowContext into _invoke_agent, resolves and forwards run kwargs to the orchestrator agent; both callers updated.
python/packages/orchestrations/tests/test_group_chat.py Adds a recording manager agent and two tests covering the kwargs-forwarding and no-kwargs (None) cases.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

…ow run kwargs 🔌

Fixes microsoft#8304.

workflow.run(function_invocation_kwargs=..., client_kwargs=...) is stored in
workflow state under WORKFLOW_RUN_KWARGS_KEY and AgentExecutor forwards it to
every participant agent. The GroupChat orchestrator agent runs outside
AgentExecutor, in AgentBasedGroupChatOrchestrator._invoke_agent, which called
self._agent.run() with only messages, session and options. So a host that puts
request-scoped values there (a user id for tool ACL, an id for metrics) had
them reach the participants and silently not reach the orchestrator, even
though the orchestrator is an agent with its own tools and middleware reading
AgentContext.function_invocation_kwargs.

_invoke_agent now takes the WorkflowContext both of its callers already hold,
reads the same state key AgentExecutor reads, and forwards both kwargs.

The resolution itself is not reimplemented. AgentExecutor._prepare_agent_run_args
and _resolve_executor_kwargs depended on nothing but self.id, so they move to
_agent_utils as prepare_agent_run_args(executor_id, ...) and
resolve_executor_kwargs(executor_id, ...), and both AgentExecutor methods stay
as one-line delegates. The orchestrator is itself an Executor, so passing
self.id gives it the same semantics participants get: __global__ kwargs apply,
per-executor entries are keyed by the orchestrator's own id, and specific
values override global ones.

Not changed: the Magentic manager's _complete, named in the issue as the same
pattern at a separate call site. Its callers are plan/replan/
create_progress_ledger/prepare_final_answer on MagenticManagerBase, which is
not an Executor and holds no WorkflowContext, so reaching the run kwargs there
means widening a public extension point rather than reading state that is
already in hand. That is a design call for the team, not a drive-by.

2 tests added in packages/orchestrations/tests/test_group_chat.py. The first
fails against unpatched sources with

  AssertionError: assert None == {'user_id': 'user-123'}

the orchestrator having been invoked with only
{'options': {'response_format': AgentOrchestrationOutput}}. The second pins the
no-kwargs case so the resolution keeps returning None rather than an empty
dict, which also holds before the change.

packages/core/tests and packages/orchestrations/tests: 5381 tests, 0 failures,
0 errors, 142 skipped. poe syntax and poe test-typing clean for both packages;
poe pyright reports the same 155 pre-existing core errors before and after,
none in the touched files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: GroupChat orchestrator agent.run() drops function_invocation_kwargs / client_kwargs

2 participants