Skip to content
3 changes: 2 additions & 1 deletion python/packages/core/agent_framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@
"validate_workflow_graph",
),
"._workflows._viz": ("WorkflowViz",),
"._workflows._workflow": ("Workflow", "WorkflowRunResult"),
"._workflows._workflow": ("Workflow", "WorkflowInvocationKwargs", "WorkflowRunResult"),
"._workflows._workflow_builder": ("WorkflowBuilder",),
"._workflows._workflow_context": ("WorkflowContext",),
"._workflows._workflow_executor": (
Expand Down Expand Up @@ -646,6 +646,7 @@
"WorkflowEventType",
"WorkflowException",
"WorkflowExecutor",
"WorkflowInvocationKwargs",
"WorkflowMessage",
"WorkflowRunResult",
"WorkflowRunState",
Expand Down
3 changes: 2 additions & 1 deletion python/packages/core/agent_framework/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ from ._workflows._validation import (
validate_workflow_graph,
)
from ._workflows._viz import WorkflowViz
from ._workflows._workflow import Workflow, WorkflowRunResult
from ._workflows._workflow import Workflow, WorkflowInvocationKwargs, WorkflowRunResult
from ._workflows._workflow_builder import WorkflowBuilder
from ._workflows._workflow_context import WorkflowContext
from ._workflows._workflow_executor import SubWorkflowRequestMessage, SubWorkflowResponseMessage, WorkflowExecutor
Expand Down Expand Up @@ -603,6 +603,7 @@ __all__ = [
"WorkflowExecutor",
"WorkflowMessage",
"WorkflowRunResult",
"WorkflowInvocationKwargs",
"WorkflowRunState",
"WorkflowRunnerException",
"WorkflowValidationError",
Expand Down
50 changes: 34 additions & 16 deletions python/packages/core/agent_framework/_workflows/_agent.py
Comment thread
droideronline marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from typing_extensions import TypedDict # pragma: no cover

if TYPE_CHECKING:
from ._workflow import Workflow
from ._workflow import Workflow, WorkflowInvocationKwargs

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -157,9 +157,12 @@ def run(
checkpoint_id: str | None = None,
checkpoint_storage: CheckpointStorage | None = None,
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
function_invocation_kwargs: Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Any] | None = None,
) -> Awaitable[AgentResponse[Any]]: ...
function_invocation_kwargs: WorkflowInvocationKwargs
| Mapping[str, Mapping[str, Any]]
| Mapping[str, Any]
| None = None,
client_kwargs: WorkflowInvocationKwargs | Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
) -> ResponseStream[AgentResponseUpdate, AgentResponse]: ...

@overload
def run(
Expand All @@ -171,9 +174,12 @@ def run(
checkpoint_id: str | None = None,
checkpoint_storage: CheckpointStorage | None = None,
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
function_invocation_kwargs: Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Any] | None = None,
) -> ResponseStream[AgentResponseUpdate, AgentResponse[Any]]: ...
function_invocation_kwargs: WorkflowInvocationKwargs
| Mapping[str, Mapping[str, Any]]
| Mapping[str, Any]
| None = None,
client_kwargs: WorkflowInvocationKwargs | Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
) -> ResponseStream[AgentResponseUpdate, AgentResponse]: ...

def run(
self,
Expand All @@ -184,9 +190,12 @@ def run(
checkpoint_id: str | None = None,
checkpoint_storage: CheckpointStorage | None = None,
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
function_invocation_kwargs: Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Any] | None = None,
) -> ResponseStream[AgentResponseUpdate, AgentResponse[Any]] | Awaitable[AgentResponse[Any]]:
function_invocation_kwargs: WorkflowInvocationKwargs
| Mapping[str, Mapping[str, Any]]
| Mapping[str, Any]
| None = None,
client_kwargs: WorkflowInvocationKwargs | Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
) -> ResponseStream[AgentResponseUpdate, AgentResponse] | Awaitable[AgentResponse]:
"""Get a response from the workflow agent.

Args:
Expand Down Expand Up @@ -254,8 +263,11 @@ async def _run_impl(
checkpoint_id: str | None = None,
checkpoint_storage: CheckpointStorage | None = None,
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
function_invocation_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
function_invocation_kwargs: WorkflowInvocationKwargs
| Mapping[str, Mapping[str, Any]]
| Mapping[str, Any]
| None = None,
client_kwargs: WorkflowInvocationKwargs | Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
) -> AgentResponse:
"""Internal implementation of non-streaming execution.

Expand Down Expand Up @@ -337,8 +349,11 @@ async def _run_stream_impl(
checkpoint_id: str | None = None,
checkpoint_storage: CheckpointStorage | None = None,
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
function_invocation_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
function_invocation_kwargs: WorkflowInvocationKwargs
| Mapping[str, Mapping[str, Any]]
| Mapping[str, Any]
| None = None,
client_kwargs: WorkflowInvocationKwargs | Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
) -> AsyncIterable[AgentResponseUpdate]:
"""Internal implementation of streaming execution.

Expand Down Expand Up @@ -419,8 +434,11 @@ async def _run_core(
checkpoint_storage: CheckpointStorage | None,
streaming: bool,
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
function_invocation_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
function_invocation_kwargs: WorkflowInvocationKwargs
| Mapping[str, Mapping[str, Any]]
| Mapping[str, Any]
| None = None,
client_kwargs: WorkflowInvocationKwargs | Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
) -> AsyncIterable[WorkflowEvent]:
"""Core implementation that yields workflow events for both streaming and non-streaming modes.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,22 +614,26 @@ def _resolve_executor_kwargs(self, resolved: dict[str, Any] | None) -> dict[str,
"""
if not isinstance(resolved, dict):
return None
# Use explicit key-presence checks so that an empty per-executor dict is
# honoured (e.g. to clear kwargs) instead of falling through to global.
if self.id in resolved:
executor_kwargs = resolved[self.id]
elif GLOBAL_KWARGS_KEY in resolved:
executor_kwargs = resolved[GLOBAL_KWARGS_KEY]
else:
global_kwargs: Any = resolved.get(GLOBAL_KWARGS_KEY)
executor_kwargs: Any = resolved.get(self.id)
if global_kwargs is None and executor_kwargs is None:
return None

if not isinstance(executor_kwargs, dict):
if global_kwargs is not None and not isinstance(global_kwargs, dict):
logger.warning(
"Executor %s expected a dict for its kwargs, but got %s. Ignoring.",
"Executor %s expected a dict for global kwargs, but got %s. Ignoring.",
self.id,
type(executor_kwargs), # type: ignore
cast(type[Any], type(global_kwargs)),
)
return None

if executor_kwargs is not None and not isinstance(executor_kwargs, dict):
logger.warning(
"Executor %s expected a dict for its kwargs, but got %s. Ignoring.",
self.id,
cast(type[Any], type(executor_kwargs)),
)
return None

return executor_kwargs # type: ignore
# Specific values override global values for the same function argument.
return {**(global_kwargs or {}), **(executor_kwargs or {})}
4 changes: 4 additions & 0 deletions python/packages/core/agent_framework/_workflows/_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
# to pass kwargs from workflow.run() through to agent.run() and @tool functions.
WORKFLOW_RUN_KWARGS_KEY = "_workflow_run_kwargs"

# State keys used to preserve caller-provided kwargs for nested workflow routing.
RAW_FUNCTION_INVOCATION_KWARGS_KEY = "_raw_function_invocation_kwargs"
RAW_CLIENT_KWARGS_KEY = "_raw_client_kwargs"

# Sentinel key used in resolved invocation kwargs dicts to denote global kwargs
# that apply to all executors (as opposed to per-executor keyed entries).
GLOBAL_KWARGS_KEY = "__global__"
Expand Down
Loading
Loading