-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Python: Add Python parity sample for invoking Foundry Toolbox tools from declarative workflows #5933
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Python: Add Python parity sample for invoking Foundry Toolbox tools from declarative workflows #5933
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |
| ) | ||
|
|
||
| from .._loader import AgentFactory | ||
| from .._models import _safe_mode_context # type: ignore[reportPrivateUsage] | ||
| from ._declarative_builder import DeclarativeWorkflowBuilder | ||
| from ._errors import DeclarativeWorkflowError | ||
| from ._http_handler import HttpRequestHandler | ||
|
|
@@ -93,6 +94,7 @@ def __init__( | |
| max_iterations: int | None = None, | ||
| http_request_handler: HttpRequestHandler | None = None, | ||
| mcp_tool_handler: MCPToolHandler | None = None, | ||
| safe_mode: bool = True, | ||
| ) -> None: | ||
| """Initialize the workflow factory. | ||
|
|
||
|
|
@@ -119,6 +121,15 @@ def __init__( | |
| for a default backed by :class:`agent_framework.MCPStreamableHTTPTool`, | ||
| or supply your own implementation to enforce SSRF guards, allowlisting, | ||
| or auth/connection resolution. | ||
| safe_mode: Whether to run in safe mode, default is True. | ||
| When safe_mode is True, environment variables are NOT accessible from | ||
| PowerFx expressions in the workflow YAML (e.g. ``=Env.SOME_VAR`` will | ||
| fail to resolve and the original expression string is preserved). | ||
| When safe_mode is False, the full ``os.environ`` snapshot is exposed | ||
| via the ``Env`` symbol in every PowerFx evaluation. Set safe_mode to | ||
| False ONLY when you fully trust the YAML source. The flag is also | ||
| forwarded to the internally-constructed :class:`AgentFactory` so | ||
| inline agent definitions follow the same policy. | ||
|
|
||
| Examples: | ||
| .. code-block:: python | ||
|
|
@@ -152,7 +163,8 @@ def __init__( | |
| env_file=".env", | ||
| ) | ||
| """ | ||
| self._agent_factory = agent_factory or AgentFactory(env_file_path=env_file) | ||
| self.safe_mode = safe_mode | ||
| self._agent_factory = agent_factory or AgentFactory(env_file_path=env_file, safe_mode=safe_mode) | ||
| self._agents: dict[str, SupportsAgentRun | AgentExecutor] = dict(agents) if agents else {} | ||
| self._bindings: dict[str, Any] = dict(bindings) if bindings else {} | ||
| self._tools: dict[str, Any] = {} # Tool registry for InvokeFunctionTool actions | ||
|
|
@@ -338,6 +350,15 @@ def create_workflow_from_definition( | |
| # Validate the workflow definition | ||
| self._validate_workflow_def(workflow_def) | ||
|
|
||
| # Set safe_mode context before evaluating any PowerFx expressions. The | ||
| # contextvar gates ``Env`` exposure inside ``DeclarativeWorkflowState`` | ||
| # symbols and inside ``_try_powerfx_eval``; both check | ||
| # ``_safe_mode_context.get()`` at evaluation time. Because the | ||
| # contextvar propagates through asyncio tasks spawned from the current | ||
| # context, this value persists into ``workflow.run(...)`` invocations | ||
| # made on the same coroutine. | ||
| _safe_mode_context.set(self.safe_mode) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be holding the reset token here? The comment promises propagation into Could we instead stash |
||
|
|
||
| # Extract workflow metadata | ||
| # Support both "name" field and trigger.id for workflow name | ||
| name: str = workflow_def.get("name", "") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we thought about scoping this to only the env vars the YAML references?
dict(os.environ)hands every PowerFx expression a full snapshot, so any expression in any action of an opted-in workflow can readAZURE_CLIENT_SECRET,OPENAI_API_KEY, etc. Documented as trust-the-YAML, but a single=Concat(Env.SOME_SECRET, ...)slipping into a logged field or a tool argument now exfiltrates. Could we parse referencedEnv.Xnames up front and expose only those, or wrap in a lazy view that records access for audit?