From 2699b8528509063c2104806f209d225bc9dd7cad Mon Sep 17 00:00:00 2001 From: Evan Mattson <35585003+moonbox3@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:06:22 +0900 Subject: [PATCH] Python: Update getting started with workflows sample structure and README (#653) * Update getting started with workflows sample structure and README * Small updates * Adjust getting started samples. Fix agent executor bug. Add workflow tests to unit test file. * Fix resource links --- .github/workflows/python-tests.yml | 27 +++++++ .../agent_framework_workflow/_executor.py | 8 ++ .../getting_started/workflow/README.md | 79 +++++++++---------- .../step1_executors_and_edges.py | 0 .../step2_agents_in_a_workflow.py | 0 .../step3_streaming.py | 0 .../sub_workflow_basics.py | 0 .../sub_workflow_parallel_requests.py | 0 .../sub_workflow_request_interception.py | 0 .../edge_condition.py | 0 .../multi_selection_edge_group.py | 7 +- .../sequential_executors.py | 0 .../sequential_streaming.py | 0 .../{loop => control-flow}/simple_loop.py | 0 .../switch_case_edge_group.py | 5 +- .../guessing_game_with_human_input.py | 0 .../tracing_basics.py | 0 .../fan_out_fan_in_edges.py | 0 .../map_reduce_and_visualization.py | 0 .../shared_states_with_agents.py | 38 ++++----- 20 files changed, 103 insertions(+), 61 deletions(-) rename python/samples/getting_started/workflow/{foundational => _start-here}/step1_executors_and_edges.py (100%) rename python/samples/getting_started/workflow/{foundational => _start-here}/step2_agents_in_a_workflow.py (100%) rename python/samples/getting_started/workflow/{foundational => _start-here}/step3_streaming.py (100%) rename python/samples/getting_started/workflow/{sub_workflow => composition}/sub_workflow_basics.py (100%) rename python/samples/getting_started/workflow/{sub_workflow => composition}/sub_workflow_parallel_requests.py (100%) rename python/samples/getting_started/workflow/{sub_workflow => composition}/sub_workflow_request_interception.py (100%) rename python/samples/getting_started/workflow/{conditional_edges => control-flow}/edge_condition.py (100%) rename python/samples/getting_started/workflow/{conditional_edges => control-flow}/multi_selection_edge_group.py (97%) rename python/samples/getting_started/workflow/{sequential => control-flow}/sequential_executors.py (100%) rename python/samples/getting_started/workflow/{sequential => control-flow}/sequential_streaming.py (100%) rename python/samples/getting_started/workflow/{loop => control-flow}/simple_loop.py (100%) rename python/samples/getting_started/workflow/{conditional_edges => control-flow}/switch_case_edge_group.py (97%) rename python/samples/getting_started/workflow/{human_in_the_loop => human-in-the-loop}/guessing_game_with_human_input.py (100%) rename python/samples/getting_started/workflow/{tracing => observability}/tracing_basics.py (100%) rename python/samples/getting_started/workflow/{fan_out_fan_in => parallelism}/fan_out_fan_in_edges.py (100%) rename python/samples/getting_started/workflow/{fan_out_fan_in => parallelism}/map_reduce_and_visualization.py (100%) rename python/samples/getting_started/workflow/{shared_states => state-management}/shared_states_with_agents.py (89%) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 2001cb9997..6552b7829c 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -37,6 +37,7 @@ jobs: - name: Install the project run: | uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit + # Main package tests - name: Set environment variables - main - win if: ${{ matrix.os == 'windows-latest' }} @@ -58,6 +59,7 @@ jobs: with: name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + # Azure package tests - name: Set environment variables - azure - win if: ${{ matrix.os == 'windows-latest' }} @@ -79,6 +81,7 @@ jobs: with: name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + # Foundry package tests - name: Set environment variables - foundry - win if: ${{ matrix.os == 'windows-latest' }} @@ -100,6 +103,30 @@ jobs: with: name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + + # Workflow package tests + - name: Set environment variables - workflow - win + if: ${{ matrix.os == 'windows-latest' }} + run: | + echo "PACKAGE_NAME=workflow" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Set environment variables - workflow + if: ${{ matrix.os != 'windows-latest' }} + run: | + echo "PACKAGE_NAME=workflow" >> $GITHUB_ENV + - name: Test with pytest - workflow + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + working-directory: ./python + - name: Move coverage file - workflow + run: | + mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + working-directory: ./python + - name: Upload coverage artifact - workflow + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} + path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + + # Surface failing tests - name: Surface failing tests if: always() uses: pmeier/pytest-results-action@v0.7.2 diff --git a/python/packages/workflow/agent_framework_workflow/_executor.py b/python/packages/workflow/agent_framework_workflow/_executor.py index 2cd1b1623a..0a7135047f 100644 --- a/python/packages/workflow/agent_framework_workflow/_executor.py +++ b/python/packages/workflow/agent_framework_workflow/_executor.py @@ -902,6 +902,14 @@ class AgentExecutor(Executor): ) await ctx.add_event(AgentRunEvent(self.id, response)) + full_conversation: list[ChatMessage] | None = None + if self._cache: + full_conversation = list(self._cache) + list(response.messages) + + agent_response = AgentExecutorResponse(self.id, response, full_conversation=full_conversation) + await ctx.send_message(agent_response) + self._cache.clear() + @handler async def from_response(self, prior: AgentExecutorResponse, ctx: WorkflowContext[AgentExecutorResponse]) -> None: """Enable seamless chaining: accept a prior AgentExecutorResponse as input. diff --git a/python/samples/getting_started/workflow/README.md b/python/samples/getting_started/workflow/README.md index c453877414..744ad3c9c0 100644 --- a/python/samples/getting_started/workflow/README.md +++ b/python/samples/getting_started/workflow/README.md @@ -2,13 +2,13 @@ ## Installation -To install the base `agent_framework.workflow` package, please run: +To install the base `agent_framework.workflow` package, run: ```bash pip install agent-framework-workflow ``` -You can install the workflow package with visualization dependency: +To install with visualization support: ```bash pip install agent-framework-workflow[viz] @@ -20,15 +20,15 @@ To export visualization images you also need to [install GraphViz](https://graph ## Foundational Concepts - Start Here -Begin with the `foundational` folder in order. These three samples introduce the core ideas of executors, edges, agents in workflows, and streaming. +Begin with the `_start-here` folder in order. These three samples introduce the core ideas of executors, edges, agents in workflows, and streaming. | Sample | File | Concepts | |--------|------|----------| -| Executors and Edges | [foundational/step1_executors_and_edges.py](./foundational/step1_executors_and_edges.py) | Minimal workflow with basic executors and edges | -| Agents in a Workflow | [foundational/step2_agents_in_a_workflow.py](./foundational/step2_agents_in_a_workflow.py) | Introduces `AgentExecutor`; calling agents inside a workflow | -| Streaming | [foundational/step3_streaming.py](./foundational/step3_streaming.py) | Extends workflows with event streaming | +| Executors and Edges | [_start-here/step1_executors_and_edges.py](./_start-here/step1_executors_and_edges.py) | Minimal workflow with basic executors and edges | +| Agents in a Workflow | [_start-here/step2_agents_in_a_workflow.py](./_start-here/step2_agents_in_a_workflow.py) | Introduces adding Agents as nodes; calling agents inside a workflow | +| Streaming (Basics) | [_start-here/step3_streaming.py](./_start-here/step3_streaming.py) | Extends workflows with event streaming | -Once comfortable with these, explore the rest of the samples. +Once comfortable with these, explore the rest of the samples below. --- @@ -37,39 +37,43 @@ Once comfortable with these, explore the rest of the samples. ### agents | Sample | File | Concepts | |---|---|---| -| Azure Chat Agents Streaming | [agents/azure_chat_agents_streaming.py](./agents/azure_chat_agents_streaming.py) | Directly adds Azure agents as edges and handling streaming events | +| Azure Chat Agents (Streaming) | [agents/azure_chat_agents_streaming.py](./agents/azure_chat_agents_streaming.py) | Add Azure agents as edges and handle streaming events | | Custom Agent Executors | [agents/custom_agent_executors.py](./agents/custom_agent_executors.py) | Create executors to handle agent run methods | -| Foundry Chat Agents Streaming | [agents/foundry_chat_agents_streaming.py](./agents/foundry_chat_agents_streaming.py) | Directly adds Foundry agents as edges and handling streaming events | -| Workflow as Agent | [ai_agent/workflow_as_agent.py](./agents/workflow_as_agent.py) | Wrap a workflow so it can behave like an agent | -| Workflow as Agent + HITL | [ai_agent/workflow_as_agent_human_in_the_loop.py](./agents/workflow_as_agent_human_in_the_loop.py) | Extend workflow-as-agent with human-in-the-loop capability | +| Foundry Chat Agents (Streaming) | [agents/foundry_chat_agents_streaming.py](./agents/foundry_chat_agents_streaming.py) | Add Foundry agents as edges and handle streaming events | +| Workflow as Agent (Reflection Pattern) | [agents/workflow_as_agent_reflection_pattern.py](./agents/workflow_as_agent_reflection_pattern.py) | Wrap a workflow so it can behave like an agent (reflection pattern) | +| Workflow as Agent + HITL | [agents/workflow_as_agent_human_in_the_loop.py](./agents/workflow_as_agent_human_in_the_loop.py) | Extend workflow-as-agent with human-in-the-loop capability | ### checkpoint | Sample | File | Concepts | |---|---|---| | Checkpoint & Resume | [checkpoint/checkpoint_with_resume.py](./checkpoint/checkpoint_with_resume.py) | Create checkpoints, inspect them, and resume execution | -### conditional_edges +### composition | Sample | File | Concepts | |---|---|---| -| Edge Condition | [conditional_edges/edge_condition.py](./conditional_edges/edge_condition.py) | Conditional routing based on agent classification | -| Switch-Case Edge Group | [conditional_edges/switch_case_edge_group.py](./conditional_edges/switch_case_edge_group.py) | Switch-case branching using classifier outputs | -| Multi-Selection Edge Group | [conditional_edges/multi_selection_edge_group.py](./conditional_edges/multi_selection_edge_group.py) | Select one or many targets dynamically (subset fan-out) | +| Sub-Workflow (Basics) | [composition/sub_workflow_basics.py](./composition/sub_workflow_basics.py) | Wrap a workflow as an executor and orchestrate sub-workflows | +| Sub-Workflow: Request Interception | [composition/sub_workflow_request_interception.py](./composition/sub_workflow_request_interception.py) | Intercept and forward requests with decorators and request handling | +| Sub-Workflow: Parallel Requests | [composition/sub_workflow_parallel_requests.py](./composition/sub_workflow_parallel_requests.py) | Multi-type interception and external forwarding patterns | -### fan_out_fan_in +### control-flow | Sample | File | Concepts | |---|---|---| -| Concurrent (Fan-out/Fan-in) | [fan_out_fan_in/fan_out_fan_in_edges.py](./fan_out_fan_in/fan_out_fan_in_edges.py) | Dispatch to multiple executors and aggregate results | -| Map-Reduce with Visualization | [fan_out_fan_in/map_reduce_and_visualization.py](./fan_out_fan_in/map_reduce_and_visualization.py) | Fan-out/fan-in pattern with GraphViz/diagram export | +| Sequential Executors | [control-flow/sequential_executors.py](./control-flow/sequential_executors.py) | Sequential workflow with explicit executor setup | +| Sequential (Streaming) | [control-flow/sequential_streaming.py](./control-flow/sequential_streaming.py) | Stream events from a simple sequential run | +| Edge Condition | [control-flow/edge_condition.py](./control-flow/edge_condition.py) | Conditional routing based on agent classification | +| Switch-Case Edge Group | [control-flow/switch_case_edge_group.py](./control-flow/switch_case_edge_group.py) | Switch-case branching using classifier outputs | +| Multi-Selection Edge Group | [control-flow/multi_selection_edge_group.py](./control-flow/multi_selection_edge_group.py) | Select one or many targets dynamically (subset fan-out) | +| Simple Loop | [control-flow/simple_loop.py](./control-flow/simple_loop.py) | Feedback loop where an agent judges ABOVE/BELOW/MATCHED | -### human_in_the_loop +### human-in-the-loop | Sample | File | Concepts | |---|---|---| -| Human-In-The-Loop (Guessing Game) | [human_in_the_loop/guessing_game_with_human_input.py](./human_in_the_loop/guessing_game_with_human_input.py) | Interactive request/response prompts with a human | +| Human-In-The-Loop (Guessing Game) | [human-in-the-loop/guessing_game_with_human_input.py](./human-in-the-loop/guessing_game_with_human_input.py) | Interactive request/response prompts with a human | -### loop +### observability | Sample | File | Concepts | |---|---|---| -| Simple Loop | [loop/simple_loop.py](./loop/simple_loop.py) | Feedback loop where an agent judges ABOVE/BELOW/MATCHED | +| Tracing (Basics) | [observability/tracing_basics.py](./observability/tracing_basics.py) | Use basic tracing for workflow telemetry | ### orchestration | Sample | File | Concepts | @@ -77,36 +81,31 @@ Once comfortable with these, explore the rest of the samples. | Magentic Workflow (Multi-Agent) | [orchestration/magentic.py](./orchestration/magentic.py) | Orchestrate multiple agents with Magentic manager and streaming | | Magentic + Human Plan Review | [orchestration/magentic_human_plan_update.py](./orchestration/magentic_human_plan_update.py) | Human reviews/updates the plan before execution | -### sequential +### parallelism | Sample | File | Concepts | |---|---|---| -| Sequential Executors | [sequential/sequential_executors.py](./sequential/sequential_executors.py) | Sequential workflow with explicit executor setup | -| Sequential (Streaming) | [sequential/sequential_streaming.py](./sequential/sequential_streaming.py) | Stream events from a simple sequential run | +| Concurrent (Fan-out/Fan-in) | [parallelism/fan_out_fan_in_edges.py](./parallelism/fan_out_fan_in_edges.py) | Dispatch to multiple executors and aggregate results | +| Map-Reduce with Visualization | [parallelism/map_reduce_and_visualization.py](./parallelism/map_reduce_and_visualization.py) | Fan-out/fan-in pattern with diagram export | -### shared_states +### state-management | Sample | File | Concepts | |---|---|---| -| Shared States | [shared_states/shared_states_with_agents.py](./shared_states/shared_states_with_agents.py) | Store in shared state once and later reuse across agents | - -### sub_workflow -| Sample | File | Concepts | -|---|---|---| -| Sub-Workflow (Basics) | [sub_workflow/sub_workflow_basics.py](./sub_workflow/sub_workflow.py) | Wrap a workflow as an executor and orchestrate sub-workflows | -| Sub-Workflow: Request Interception | [sub_workflow/sub_workflow_request_interception.py](./sub_workflow/sub_workflow_request_interception.py) | Intercept/forward requests with decorators and request handling | -| Sub-Workflow: Parallel Requests | [sub_workflow/sub_workflow_parallel_requests.py](./sub_workflow/sub_workflow_parallel_requests.py) | Multi-type interception and external forwarding patterns | - -### tracing -| Sample | File | Concepts | -|---|---|---| -| Tracing (Basics) | [tracing/tracing_basics.py](./tracing/tracing_basics.py) | Use basic tracing for workflow telemetry | +| Shared States | [state-management/shared_states_with_agents.py](./state-management/shared_states_with_agents.py) | Store in shared state once and later reuse across agents | ### visualization | Sample | File | Concepts | |---|---|---| | Concurrent with Visualization | [visualization/concurrent_with_visualization.py](./visualization/concurrent_with_visualization.py) | Fan-out/fan-in workflow with diagram export | +### resources +- Sample text inputs used by certain workflows: + - [resources/long_text.txt](./resources/long_text.txt) + - [resources/email.txt](./resources/email.txt) + - [resources/spam.txt](./resources/spam.txt) + - [resources/ambiguous_email.txt](./resources/ambiguous_email.txt) + Notes -- Agent‑based samples use provider SDKs (Azure/OpenAI, etc.). Ensure credentials are configured, or adapt agents accordingly. +- Agent-based samples use provider SDKs (Azure/OpenAI, etc.). Ensure credentials are configured, or adapt agents accordingly. ### Environment Variables diff --git a/python/samples/getting_started/workflow/foundational/step1_executors_and_edges.py b/python/samples/getting_started/workflow/_start-here/step1_executors_and_edges.py similarity index 100% rename from python/samples/getting_started/workflow/foundational/step1_executors_and_edges.py rename to python/samples/getting_started/workflow/_start-here/step1_executors_and_edges.py diff --git a/python/samples/getting_started/workflow/foundational/step2_agents_in_a_workflow.py b/python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py similarity index 100% rename from python/samples/getting_started/workflow/foundational/step2_agents_in_a_workflow.py rename to python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py diff --git a/python/samples/getting_started/workflow/foundational/step3_streaming.py b/python/samples/getting_started/workflow/_start-here/step3_streaming.py similarity index 100% rename from python/samples/getting_started/workflow/foundational/step3_streaming.py rename to python/samples/getting_started/workflow/_start-here/step3_streaming.py diff --git a/python/samples/getting_started/workflow/sub_workflow/sub_workflow_basics.py b/python/samples/getting_started/workflow/composition/sub_workflow_basics.py similarity index 100% rename from python/samples/getting_started/workflow/sub_workflow/sub_workflow_basics.py rename to python/samples/getting_started/workflow/composition/sub_workflow_basics.py diff --git a/python/samples/getting_started/workflow/sub_workflow/sub_workflow_parallel_requests.py b/python/samples/getting_started/workflow/composition/sub_workflow_parallel_requests.py similarity index 100% rename from python/samples/getting_started/workflow/sub_workflow/sub_workflow_parallel_requests.py rename to python/samples/getting_started/workflow/composition/sub_workflow_parallel_requests.py diff --git a/python/samples/getting_started/workflow/sub_workflow/sub_workflow_request_interception.py b/python/samples/getting_started/workflow/composition/sub_workflow_request_interception.py similarity index 100% rename from python/samples/getting_started/workflow/sub_workflow/sub_workflow_request_interception.py rename to python/samples/getting_started/workflow/composition/sub_workflow_request_interception.py diff --git a/python/samples/getting_started/workflow/conditional_edges/edge_condition.py b/python/samples/getting_started/workflow/control-flow/edge_condition.py similarity index 100% rename from python/samples/getting_started/workflow/conditional_edges/edge_condition.py rename to python/samples/getting_started/workflow/control-flow/edge_condition.py diff --git a/python/samples/getting_started/workflow/conditional_edges/multi_selection_edge_group.py b/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py similarity index 97% rename from python/samples/getting_started/workflow/conditional_edges/multi_selection_edge_group.py rename to python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py index afed90325b..d14a1ab539 100644 --- a/python/samples/getting_started/workflow/conditional_edges/multi_selection_edge_group.py +++ b/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py @@ -250,11 +250,16 @@ async def main() -> None: ) # Read an email sample - resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "email.txt") + resources_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), + "resources", + "email.txt", + ) if os.path.exists(resources_path): with open(resources_path, encoding="utf-8") as f: # noqa: ASYNC230 email = f.read() else: + print("Unable to find resource file, using default text.") email = "Hello team, here are the updates for this week..." async for event in workflow.run_stream(email): diff --git a/python/samples/getting_started/workflow/sequential/sequential_executors.py b/python/samples/getting_started/workflow/control-flow/sequential_executors.py similarity index 100% rename from python/samples/getting_started/workflow/sequential/sequential_executors.py rename to python/samples/getting_started/workflow/control-flow/sequential_executors.py diff --git a/python/samples/getting_started/workflow/sequential/sequential_streaming.py b/python/samples/getting_started/workflow/control-flow/sequential_streaming.py similarity index 100% rename from python/samples/getting_started/workflow/sequential/sequential_streaming.py rename to python/samples/getting_started/workflow/control-flow/sequential_streaming.py diff --git a/python/samples/getting_started/workflow/loop/simple_loop.py b/python/samples/getting_started/workflow/control-flow/simple_loop.py similarity index 100% rename from python/samples/getting_started/workflow/loop/simple_loop.py rename to python/samples/getting_started/workflow/control-flow/simple_loop.py diff --git a/python/samples/getting_started/workflow/conditional_edges/switch_case_edge_group.py b/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py similarity index 97% rename from python/samples/getting_started/workflow/conditional_edges/switch_case_edge_group.py rename to python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py index ba6eed5fc2..b23ffafb08 100644 --- a/python/samples/getting_started/workflow/conditional_edges/switch_case_edge_group.py +++ b/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py @@ -201,11 +201,14 @@ async def main(): ) # Read ambiguous email if available. Otherwise use a simple inline sample. - resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "ambiguous_email.txt") + resources_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "resources", "ambiguous_email.txt" + ) if os.path.exists(resources_path): with open(resources_path, encoding="utf-8") as f: # noqa: ASYNC230 email = f.read() else: + print("Unable to find resource file, using default text.") email = ( "Hey there, I noticed you might be interested in our latest offer—no pressure, but it expires soon. " "Let me know if you'd like more details." diff --git a/python/samples/getting_started/workflow/human_in_the_loop/guessing_game_with_human_input.py b/python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py similarity index 100% rename from python/samples/getting_started/workflow/human_in_the_loop/guessing_game_with_human_input.py rename to python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py diff --git a/python/samples/getting_started/workflow/tracing/tracing_basics.py b/python/samples/getting_started/workflow/observability/tracing_basics.py similarity index 100% rename from python/samples/getting_started/workflow/tracing/tracing_basics.py rename to python/samples/getting_started/workflow/observability/tracing_basics.py diff --git a/python/samples/getting_started/workflow/fan_out_fan_in/fan_out_fan_in_edges.py b/python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py similarity index 100% rename from python/samples/getting_started/workflow/fan_out_fan_in/fan_out_fan_in_edges.py rename to python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py diff --git a/python/samples/getting_started/workflow/fan_out_fan_in/map_reduce_and_visualization.py b/python/samples/getting_started/workflow/parallelism/map_reduce_and_visualization.py similarity index 100% rename from python/samples/getting_started/workflow/fan_out_fan_in/map_reduce_and_visualization.py rename to python/samples/getting_started/workflow/parallelism/map_reduce_and_visualization.py diff --git a/python/samples/getting_started/workflow/shared_states/shared_states_with_agents.py b/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py similarity index 89% rename from python/samples/getting_started/workflow/shared_states/shared_states_with_agents.py rename to python/samples/getting_started/workflow/state-management/shared_states_with_agents.py index 82777dd63e..45c71b516c 100644 --- a/python/samples/getting_started/workflow/shared_states/shared_states_with_agents.py +++ b/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py @@ -9,7 +9,6 @@ from uuid import uuid4 from agent_framework import ChatMessage, Role from agent_framework.azure import AzureChatClient from agent_framework.workflow import ( - AgentExecutor, AgentExecutorRequest, AgentExecutorResponse, WorkflowBuilder, @@ -158,26 +157,22 @@ async def main() -> None: # Create chat client and agents. response_format enforces structured JSON from each agent. chat_client = AzureChatClient(credential=AzureCliCredential()) - spam_detection_agent = AgentExecutor( - chat_client.create_agent( - instructions=( - "You are a spam detection assistant that identifies spam emails. " - "Always return JSON with fields is_spam (bool) and reason (string)." - ), - response_format=DetectionResultAgent, + spam_detection_agent = chat_client.create_agent( + instructions=( + "You are a spam detection assistant that identifies spam emails. " + "Always return JSON with fields is_spam (bool) and reason (string)." ), - id="spam_detection_agent", + response_format=DetectionResultAgent, + name="spam_detection_agent", ) - email_assistant_agent = AgentExecutor( - chat_client.create_agent( - instructions=( - "You are an email assistant that helps users draft responses to emails with professionalism. " - "Return JSON with a single field 'response' containing the drafted reply." - ), - response_format=EmailResponse, + email_assistant_agent = chat_client.create_agent( + instructions=( + "You are an email assistant that helps users draft responses to emails with professionalism. " + "Return JSON with a single field 'response' containing the drafted reply." ), - id="email_assistant_agent", + response_format=EmailResponse, + name="email_assistant_agent", ) # Build the workflow graph with conditional edges. @@ -198,17 +193,22 @@ async def main() -> None: ) # Read an email from resources/spam.txt if available; otherwise use a default sample. - resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "spam.txt") + resources_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), + "resources", + "spam.txt", + ) if os.path.exists(resources_path): with open(resources_path, encoding="utf-8") as f: # noqa: ASYNC230 email = f.read() else: + print("Unable to find resource file, using default text.") email = "You are a WINNER! Click here for a free lottery offer!!!" # Run and print the terminal result. Streaming surfaces intermediate execution events as well. async for event in workflow.run_stream(email): if isinstance(event, WorkflowCompletedEvent): - print(f"{event}") + print(event) """ Sample Output: