mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Fix declarative package powerfx import crash and response_format kwarg error (#3841)
* Fix declarative package powerfx import crash and response_format kwarg error * Address PR feedback. Propagate kwargs for declarative workflows * move tests * Fix options merge logic
This commit is contained in:
committed by
GitHub
Unverified
parent
692fcd1888
commit
ff91473912
@@ -605,10 +605,11 @@ class AgentFactory:
|
||||
# Parse tools
|
||||
tools = self._parse_tools(prompt_agent.tools) if prompt_agent.tools else None
|
||||
|
||||
# Parse response format
|
||||
response_format = None
|
||||
# Parse response format into default_options
|
||||
default_options: dict[str, Any] | None = None
|
||||
if prompt_agent.outputSchema:
|
||||
response_format = _create_model_from_json_schema("agent", prompt_agent.outputSchema.to_json_schema())
|
||||
default_options = {"response_format": response_format}
|
||||
|
||||
# Create the agent using the provider
|
||||
# The provider's create_agent returns a Agent directly
|
||||
@@ -620,7 +621,7 @@ class AgentFactory:
|
||||
instructions=prompt_agent.instructions,
|
||||
description=prompt_agent.description,
|
||||
tools=tools,
|
||||
response_format=response_format,
|
||||
default_options=default_options,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
+24
-4
@@ -327,6 +327,16 @@ async def handle_invoke_azure_agent(ctx: ActionContext) -> AsyncGenerator[Workfl
|
||||
max_iterations = 100 # Safety limit
|
||||
|
||||
# Start external loop if configured
|
||||
# Build options for kwargs propagation to agent tools
|
||||
run_kwargs = ctx.run_kwargs
|
||||
options: dict[str, Any] | None = None
|
||||
if run_kwargs:
|
||||
# Merge caller-provided options to avoid duplicate keyword argument
|
||||
options = dict(run_kwargs.get("options") or {})
|
||||
options["additional_function_arguments"] = run_kwargs
|
||||
# Exclude 'options' from splat to avoid TypeError on duplicate keyword
|
||||
run_kwargs = {k: v for k, v in run_kwargs.items() if k != "options"}
|
||||
|
||||
while True:
|
||||
# Invoke the agent
|
||||
try:
|
||||
@@ -337,7 +347,7 @@ async def handle_invoke_azure_agent(ctx: ActionContext) -> AsyncGenerator[Workfl
|
||||
updates: list[Any] = []
|
||||
tool_calls: list[Any] = []
|
||||
|
||||
async for chunk in agent.run(messages, stream=True):
|
||||
async for chunk in agent.run(messages, stream=True, options=options, **run_kwargs):
|
||||
updates.append(chunk)
|
||||
|
||||
# Yield streaming events for text chunks
|
||||
@@ -403,7 +413,7 @@ async def handle_invoke_azure_agent(ctx: ActionContext) -> AsyncGenerator[Workfl
|
||||
|
||||
except TypeError:
|
||||
# Agent doesn't support streaming, fall back to non-streaming
|
||||
response = await agent.run(messages)
|
||||
response = await agent.run(messages, options=options, **run_kwargs)
|
||||
|
||||
text = response.text
|
||||
response_messages = response.messages
|
||||
@@ -570,6 +580,16 @@ async def handle_invoke_prompt_agent(ctx: ActionContext) -> AsyncGenerator[Workf
|
||||
|
||||
logger.debug(f"InvokePromptAgent: calling '{agent_name}' with {len(messages)} messages")
|
||||
|
||||
# Build options for kwargs propagation to agent tools
|
||||
prompt_run_kwargs = ctx.run_kwargs
|
||||
prompt_options: dict[str, Any] | None = None
|
||||
if prompt_run_kwargs:
|
||||
# Merge caller-provided options to avoid duplicate keyword argument
|
||||
prompt_options = dict(prompt_run_kwargs.get("options") or {})
|
||||
prompt_options["additional_function_arguments"] = prompt_run_kwargs
|
||||
# Exclude 'options' from splat to avoid TypeError on duplicate keyword
|
||||
prompt_run_kwargs = {k: v for k, v in prompt_run_kwargs.items() if k != "options"}
|
||||
|
||||
# Invoke the agent
|
||||
try:
|
||||
if hasattr(agent, "run"):
|
||||
@@ -577,7 +597,7 @@ async def handle_invoke_prompt_agent(ctx: ActionContext) -> AsyncGenerator[Workf
|
||||
try:
|
||||
updates: list[Any] = []
|
||||
|
||||
async for chunk in agent.run(messages, stream=True):
|
||||
async for chunk in agent.run(messages, stream=True, options=prompt_options, **prompt_run_kwargs):
|
||||
updates.append(chunk)
|
||||
|
||||
if hasattr(chunk, "text") and chunk.text:
|
||||
@@ -607,7 +627,7 @@ async def handle_invoke_prompt_agent(ctx: ActionContext) -> AsyncGenerator[Workf
|
||||
|
||||
except TypeError:
|
||||
# Agent doesn't support streaming, fall back to non-streaming
|
||||
response = await agent.run(messages)
|
||||
response = await agent.run(messages, options=prompt_options, **prompt_run_kwargs)
|
||||
text = response.text
|
||||
response_messages = response.messages
|
||||
|
||||
|
||||
+16
-2
@@ -37,7 +37,13 @@ from agent_framework._workflows import (
|
||||
WorkflowContext,
|
||||
)
|
||||
from agent_framework._workflows._state import State
|
||||
from powerfx import Engine
|
||||
|
||||
try:
|
||||
from powerfx import Engine
|
||||
except (ImportError, RuntimeError):
|
||||
# ImportError: powerfx package not installed
|
||||
# RuntimeError: .NET runtime not available or misconfigured
|
||||
Engine = None # type: ignore[assignment, misc]
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import TypedDict # type: ignore # pragma: no cover
|
||||
@@ -339,7 +345,8 @@ class DeclarativeWorkflowState:
|
||||
undefined variables (matching legacy fallback parser behavior).
|
||||
|
||||
Raises:
|
||||
ImportError: If the powerfx package is not installed.
|
||||
RuntimeError: If the powerfx package is not installed and the
|
||||
expression requires PowerFx evaluation.
|
||||
"""
|
||||
if not expression:
|
||||
return expression
|
||||
@@ -363,6 +370,13 @@ class DeclarativeWorkflowState:
|
||||
# Replace them with their evaluated results before sending to PowerFx
|
||||
formula = self._preprocess_custom_functions(formula)
|
||||
|
||||
if Engine is None:
|
||||
raise RuntimeError(
|
||||
f"PowerFx is not available (dotnet runtime not installed). "
|
||||
f"Expression '={formula[:80]}' cannot be evaluated. "
|
||||
f"Install dotnet and the powerfx package for full PowerFx support."
|
||||
)
|
||||
|
||||
engine = Engine()
|
||||
symbols = self._to_powerfx_symbols()
|
||||
try:
|
||||
|
||||
+13
-1
@@ -656,10 +656,22 @@ class InvokeAzureAgentExecutor(DeclarativeActionExecutor):
|
||||
if isinstance(messages_for_agent, list) and messages_for_agent:
|
||||
_validate_conversation_history(messages_for_agent, agent_name)
|
||||
|
||||
# Retrieve kwargs passed to workflow.run() so they propagate to agent tools
|
||||
from agent_framework._workflows._const import WORKFLOW_RUN_KWARGS_KEY
|
||||
|
||||
run_kwargs: dict[str, Any] = ctx.get_state(WORKFLOW_RUN_KWARGS_KEY, {})
|
||||
options: dict[str, Any] | None = None
|
||||
if run_kwargs:
|
||||
# Merge caller-provided options to avoid duplicate keyword argument
|
||||
options = dict(run_kwargs.get("options") or {})
|
||||
options["additional_function_arguments"] = run_kwargs
|
||||
# Exclude 'options' from splat to avoid TypeError on duplicate keyword
|
||||
run_kwargs = {k: v for k, v in run_kwargs.items() if k != "options"}
|
||||
|
||||
# Use run() method to get properly structured messages (including tool calls and results)
|
||||
# This is critical for multi-turn conversations where tool calls must be followed
|
||||
# by their results in the message history
|
||||
result: Any = await agent.run(messages_for_agent)
|
||||
result: Any = await agent.run(messages_for_agent, options=options, **run_kwargs)
|
||||
if hasattr(result, "text") and result.text:
|
||||
accumulated_response = str(result.text)
|
||||
if auto_send:
|
||||
|
||||
@@ -10,7 +10,7 @@ has a corresponding handler registered via the @action_handler decorator.
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncGenerator, Callable
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
||||
|
||||
from agent_framework import get_logger
|
||||
@@ -44,6 +44,9 @@ class ActionContext:
|
||||
bindings: dict[str, Any]
|
||||
"""Function bindings for tool calls."""
|
||||
|
||||
run_kwargs: dict[str, Any] = field(default_factory=dict)
|
||||
"""Kwargs from workflow.run() to forward to agent invocations."""
|
||||
|
||||
@property
|
||||
def action_id(self) -> str | None:
|
||||
"""Get the action's unique identifier."""
|
||||
|
||||
Reference in New Issue
Block a user