[BREAKING] Python: Add InvokeFunctionTool action for declarative workflows (#3716)

* add(declarative): Declarative workflow InvokeFunctionTool feature

* Cleanup

* Address PR feedback

* Remove InvokeTool kind, consolidate to InvokeFunctionTool

* Fix sample locations

* pin azure-ai-projects to 2.0.0b3 due to breaking changes
This commit is contained in:
Evan Mattson
2026-02-25 07:54:35 +09:00
committed by GitHub
Unverified
parent f77f40b987
commit 40d2fac29c
23 changed files with 5089 additions and 682 deletions
@@ -0,0 +1,116 @@
# Copyright (c) Microsoft. All rights reserved.
"""Invoke Function Tool sample - demonstrates InvokeFunctionTool workflow actions.
This sample shows how to:
1. Define Python functions that can be called from workflows
2. Register functions with WorkflowFactory.register_tool()
3. Use the InvokeFunctionTool action in YAML to invoke registered functions
4. Pass arguments using expression syntax (=Local.variable)
5. Capture function output in workflow variables
Run with:
python -m samples.03-workflows.declarative.invoke_function_tool.main
"""
import asyncio
from pathlib import Path
from typing import Any
from agent_framework.declarative import WorkflowFactory
# Define the function tools that will be registered with the workflow
def get_weather(location: str, unit: str = "F") -> dict[str, Any]:
"""Get weather information for a location.
This is a mock function that returns simulated weather data.
In a real application, this would call a weather API.
Args:
location: The city or location to get weather for.
unit: Temperature unit ("F" for Fahrenheit, "C" for Celsius).
Returns:
Dictionary with weather information.
"""
# Simulated weather data
weather_data = {
"Seattle": {"temp": 55, "condition": "rainy"},
"New York": {"temp": 70, "condition": "partly cloudy"},
"Los Angeles": {"temp": 85, "condition": "sunny"},
"Chicago": {"temp": 60, "condition": "windy"},
}
data = weather_data.get(location, {"temp": 72, "condition": "unknown"})
# Convert to Celsius if requested
temp = data["temp"]
if unit.upper() == "C":
temp = round((temp - 32) * 5 / 9) # type: ignore
return {
"location": location,
"temp": temp,
"unit": unit.upper(),
"condition": data["condition"],
}
def format_message(template: str, data: dict[str, Any]) -> str:
"""Format a message template with data.
Args:
template: A string template with {key} placeholders.
data: Dictionary of values to substitute.
Returns:
Formatted message string.
"""
try:
return template.format(**data)
except KeyError as e:
return f"Error formatting message: missing key {e}"
async def main():
"""Run the invoke function tool workflow."""
# Get the path to the workflow YAML file
workflow_path = Path(__file__).parent / "workflow.yaml"
# Create the workflow factory and register our tool functions
factory = (
WorkflowFactory().register_tool("get_weather", get_weather).register_tool("format_message", format_message)
)
# Create the workflow from the YAML definition
workflow = factory.create_workflow_from_yaml_path(workflow_path)
print("=" * 60)
print("Invoke Function Tool Workflow Demo")
print("=" * 60)
# Test with different inputs - both location and unit must be provided
# as the workflow expects them in Workflow.Inputs
test_inputs = [
{"location": "Seattle", "unit": "F"},
{"location": "New York", "unit": "C"},
{"location": "Los Angeles", "unit": "F"},
{"location": "Chicago", "unit": "C"},
]
for inputs in test_inputs:
print(f"\nInput: {inputs}")
print("-" * 40)
# Run the workflow
events = await workflow.run(inputs)
# Get the outputs
outputs = events.get_outputs()
for output in outputs:
print(f"Output: {output}")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,51 @@
# Invoke Function Tool Workflow
name: invoke_function_tool_demo
description: Demonstrates the InvokeFunctionTool action for invoking registered functions
actions:
# Set up input location
- kind: SetValue
id: set_location
path: Local.location
value: =If(IsBlank(inputs.location), "Seattle", inputs.location)
# Set up temperature unit
- kind: SetValue
id: set_unit
path: Local.unit
value: =If(IsBlank(inputs.unit), "F", inputs.unit)
# Invoke the get_weather function tool
- kind: InvokeFunctionTool
id: invoke_weather
functionName: get_weather
arguments:
location: =Local.location
unit: =Local.unit
output:
messages: Local.weatherToolCallItems
result: Local.weatherInfo
autoSend: true
# Format a human-readable message using another function
- kind: InvokeFunctionTool
id: format_output
functionName: format_message
arguments:
template: "The weather in {location} is {temp}°{unit}"
data: =Local.weatherInfo
output:
result: Local.formattedMessage
# Output the result
- kind: SendActivity
id: send_weather
activity:
text: =Local.formattedMessage
# Store the result in workflow outputs
- kind: SetValue
id: set_output
path: Workflow.Outputs.weather
value: =Local.weatherInfo