Python: Fix as_agent function invocation config#6322
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR adds support for passing a per-call function_invocation_configuration from agents into the function-invocation layer, allowing agent instances (and as_agent(...)) to override tool/function-calling behavior for a single run without mutating the underlying client.
Changes:
- Adds
function_invocation_configurationas an optional parameter toFunctionInvocationLayer.get_response(...)and threads it through tool-calling logic via an “effective config”. - Introduces agent-level
function_invocation_configurationplumbing and forwards it to clients that support it. - Adds a unit test verifying that
as_agent(...)forwards configuration without mutating the base client’s configuration.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| python/packages/core/tests/core/test_clients.py | Adds a regression test ensuring agent forwarding of function invocation config. |
| python/packages/core/agent_framework/_tools.py | Adds per-call override support for function invocation configuration inside get_response. |
| python/packages/core/agent_framework/_agents.py | Adds agent-level config storage and conditional forwarding to get_response. |
Comments suppressed due to low confidence (1)
python/packages/core/agent_framework/_agents.py:2
- Agent currently normalizes
function_invocation_configurationeven when the constructor argument isNone, and then always forwards it toFunctionInvocationLayer.get_response. This unintentionally overrides the client’s ownself.function_invocation_configuration(e.g., a client configured withenabled=Falsewould be re-enabled by the agent defaults). To preserve existing behavior, keep the agent’s configuration asNoneunless explicitly provided, and only forwardfunction_invocation_configurationwhen an override is present (and adjust the run-context type accordingly).
| self.function_invocation_configuration = normalize_function_invocation_configuration( | ||
| function_invocation_configuration | ||
| ) |
| def _accepts_function_invocation_configuration(client: SupportsChatGetResponse[Any]) -> bool: | ||
| """Return whether the client's get_response accepts per-call function invocation config.""" | ||
| try: | ||
| return "function_invocation_configuration" in signature(client.get_response).parameters | ||
| except (TypeError, ValueError): | ||
| return False |
| function_invocation_configuration_kwargs: dict[str, Any] = {} | ||
| if isinstance(self.client, FunctionInvocationLayer) and _accepts_function_invocation_configuration(self.client): |
| async def test_base_client_as_agent_forwards_function_invocation_configuration( | ||
| chat_client_base: SupportsChatGetResponse, | ||
| ) -> None: |
| agent = chat_client_base.as_agent(function_invocation_configuration={"include_detailed_errors": True}) | ||
|
|
||
| await agent.run("hello") |
|
|
||
| assert captured_config["include_detailed_errors"] is True | ||
| assert captured_config["enabled"] is True | ||
| assert chat_client_base.function_invocation_configuration["include_detailed_errors"] is False # type: ignore[attr-defined] |
|
@puneetdixit200 please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
Motivation and Context
Fixes #6313.
BaseChatClient.as_agent()acceptsfunction_invocation_configuration, but passing it currently raisesAgent.__init__() got an unexpected keyword argument 'function_invocation_configuration'before an agent can be created.Description
RawAgentandAgent.get_response()accepts a per-call function invocation override, without mutating shared client defaults.FunctionInvocationLayer.get_response().as_agent(function_invocation_configuration=...).Focused local tests for core clients, agents, and function invocation passed, and targeted Ruff format/lint checks passed. A local
pyright -P corerun is blocked in this environment by missing optional OTLP exporter imports inobservability.py.Contribution Checklist