chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,246 @@
# Semantic Kernel Processes - Getting Started
This project contains a step by step guide to get started with _Semantic Kernel Processes_.
#### PyPI:
- The initial release of the Python Process Framework was in the Semantic Kernel pypi version 1.12.0.
#### Sources
- [Semantic Kernel Process Framework](../../semantic_kernel/processes/)
- [Semantic Kernel Processes - Kernel Process](../../semantic_kernel/processes/kernel_process/)
- [Semantic Kernel Processes - Local Runtime](../../semantic_kernel/processes/local_runtime/)
The examples can be run as scripts and the code can also be copied to stand-alone projects, using the proper package imports.
## Examples
The getting started with processes examples include:
Example|Description
---|---
[step01_processes](../getting_started_with_processes/step01/step01_processes.py)|How to create a simple process with a loop and a conditional exit|
[step03a_food_preparation](../getting_started_with_processes/step03/step03a_food_preparation.py)|Showcasing reuse of steps, creation of processes, spawning of multiple events, use of stateful steps with food preparation samples.
[step03b_food_ordering](../getting_started_with_processes/step03/step03b_food_ordering.py)|Showcasing use of subprocesses as steps, spawning of multiple events conditionally reusing the food preparation samples.
### step01_processes
```mermaid
flowchart LR
Intro(Intro)--> UserInput(User Input)
UserInput-->|User message == 'exit'| Exit(Exit)
UserInput-->|User message| AssistantResponse(Assistant Response)
AssistantResponse--> UserInput
```
### step03a_food_preparation
This tutorial contains a set of food recipes associated with the Food Preparation Processes of a restaurant.
The following recipes for preparation of Order Items are defined as SK Processes:
#### Product Preparation Processes
##### Stateless Product Preparation Processes
###### Potato Fries Preparation Process
``` mermaid
flowchart LR
PreparePotatoFriesEvent([Prepare Potato <br/> Fries Event])
PotatoFriesReadyEvent([Potato Fries <br/> Ready Event])
GatherIngredientsStep[Gather Ingredients <br/> Step]
CutStep[Cut Food <br/> Step]
FryStep[Fry Food <br/> Step]
PreparePotatoFriesEvent --> GatherIngredientsStep -->| Slice Potatoes <br/> _Ingredients Gathered_ | CutStep --> |**Potato Sliced Ready** <br/> _Food Sliced Ready_ | FryStep --> |_Fried Food Ready_|PotatoFriesReadyEvent
FryStep -->|Fried Potato Ruined <br/> _Fried Food Ruined_| GatherIngredientsStep
```
###### Fried Fish Preparation Process
``` mermaid
flowchart LR
PrepareFriedFishEvent([Prepare Fried <br/> Fish Event])
FriedFishReadyEvent([Fried Fish <br/> Ready Event])
GatherIngredientsStep[Gather Ingredients <br/> Step]
CutStep[Cut Food <br/> Step]
FryStep[Fry Food <br/> Step]
PrepareFriedFishEvent --> GatherIngredientsStep -->| Chop Fish <br/> _Ingredients Gathered_ | CutStep --> |**Fish Chopped Ready** <br/> _Food Chopped Ready_| FryStep --> |_Fried Food Ready_ | FriedFishReadyEvent
FryStep -->|**Fried Fish Ruined** <br/> _Fried Food Ruined_| GatherIngredientsStep
```
###### Fish Sandwich Preparation Process
``` mermaid
flowchart LR
PrepareFishSandwichEvent([Prepare Fish <br/> Sandwich Event])
FishSandwichReadyEvent([Fish Sandwich <br/> Ready Event])
FriedFishStep[[Fried Fish <br/> Process Step]]
AddBunsStep[Add Buns <br/> Step]
AddSpecialSauceStep[Add Special <br/> Sauce Step]
PrepareFishSandwichEvent -->|Prepare Fried Fish| FriedFishStep -->|Fried Fish Ready| AddBunsStep --> |Buns Added | AddSpecialSauceStep --> |Special Sauce Added | FishSandwichReadyEvent
```
###### Fish And Chips Preparation Process
``` mermaid
flowchart LR
PrepareFishAndChipsEvent([Prepare <br/> Fish And Chips <br/> Event])
FishAndChipsReadyEvent([Fish And Chips <br/> Ready Event])
FriedFishStep[[Fried Fish <br/> Process Step]]
PotatoFriesStep[[Potato Fries <br/> Process Step]]
AddCondiments[Add Condiments <br/> Step ]
PrepareFishAndChipsEvent -->|Prepare Fried Fish| FriedFishStep --> |Fried Fish Ready| AddCondiments
PrepareFishAndChipsEvent -->|Prepare Potato Fries| PotatoFriesStep -->|Potato Fries Ready| AddCondiments
AddCondiments -->|Condiments Added| FishAndChipsReadyEvent
```
##### Stateful Product Preparation Processes
The processes in this subsection contain the following modifications/additions to previously used food preparation processes:
- The `Gather Ingredients Step` is now stateful and has a predefined number of initial ingredients that are used as orders are prepared. When there are no ingredients left, it emits the `Out of Stock Event`.
- The `Cut Food Step` is now a stateful component which has a `Knife Sharpness State` that tracks the Knife Sharpness.
- As the `Slice Food` and `Chop Food` Functions get invoked, the Knife Sharpness deteriorates.
- The `Cut Food Step` has an additional input function `Sharpen Knife Function`.
- The new `Sharpen Knife Function` sharpens the knife and increases the Knife Sharpness - Knife Sharpness State.
- From time to time, the `Cut Food Step`'s functions `SliceFood` and `ChopFood` will fail and emit a `Knife Needs Sharpening Event` that then triggers the `Sharpen Knife Function`.
###### Potato Fries Preparation With Knife Sharpening and Ingredient Stock Process
The following processes is a modification on the process [Potato Fries Preparation](#potato-fries-preparation-process)
with the the stateful steps mentioned previously.
``` mermaid
flowchart LR
PreparePotatoFriesEvent([Prepare Potato <br/> Fries Event])
PotatoFriesReadyEvent([Potato Fries <br/> Ready Event])
OutOfStock([Ingredients <br/> Out of Stock <br/> Event])
FryStep[Fry Food <br/> Step]
subgraph GatherIngredientsStep[Gather Ingredients Step]
GatherIngredientsFunction[Gather Potato <br/> Function]
IngredientsState[(Ingredients <br/> Stock <br/> State)]
end
subgraph CutStep ["Cut Food Step"]
direction LR
SliceFoodFunction[Slice Food <br/> Function]
SharpenKnifeFunction[Sharpen Knife <br/> Function]
CutState[(Knife <br/> Sharpness <br/> State)]
end
CutStep --> |**Potato Sliced Ready** <br/> _Food Sliced Ready_ | FryStep --> |_Fried Food Ready_|PotatoFriesReadyEvent
FryStep -->|Fried Potato Ruined <br/> _Fried Food Ruined_| GatherIngredientsStep
GatherIngredientsStep --> OutOfStock
SliceFoodFunction --> |Knife Needs Sharpening| SharpenKnifeFunction
SharpenKnifeFunction --> |Knife Sharpened| SliceFoodFunction
GatherIngredientsStep -->| Slice Potatoes <br/> _Ingredients Gathered_ | CutStep
PreparePotatoFriesEvent --> GatherIngredientsStep
```
###### Fried Fish Preparation With Knife Sharpening and Ingredient Stock Process
The following process is a modification on the process [Fried Fish Preparation](#fried-fish-preparation-process)
with the the stateful steps mentioned previously.
``` mermaid
flowchart LR
PrepareFriedFishEvent([Prepare Fried <br/> Fish Event])
FriedFishReadyEvent([Fried Fish <br/> Ready Event])
OutOfStock([Ingredients <br/> Out of Stock <br/> Event])
FryStep[Fry Food <br/> Step]
subgraph GatherIngredientsStep[Gather Ingredients Step]
GatherIngredientsFunction[Gather Fish <br/> Function]
IngredientsState[(Ingredients <br/> Stock <br/> State)]
end
subgraph CutStep ["Cut Food Step"]
direction LR
ChopFoodFunction[Chop Food <br/> Function]
SharpenKnifeFunction[Sharpen Knife <br/> Function]
CutState[(Knife <br/> Sharpness <br/> State)]
end
CutStep --> |**Fish Chopped Ready** <br/> _Food Chopped Ready_| FryStep --> |_Fried Food Ready_|FriedFishReadyEvent
FryStep -->|**Fried Fish Ruined** <br/> _Fried Food Ruined_| GatherIngredientsStep
GatherIngredientsStep --> OutOfStock
ChopFoodFunction --> |Knife Needs Sharpening| SharpenKnifeFunction
SharpenKnifeFunction --> |Knife Sharpened| ChopFoodFunction
GatherIngredientsStep -->| Chop Fish <br/> _Ingredients Gathered_ | CutStep
PrepareFriedFishEvent --> GatherIngredientsStep
```
### step03b_food_ordering
#### Single Order Preparation Process
Now with the existing product preparation processes, they can be used to create an even more complex process that can decide what product order to dispatch.
```mermaid
graph TD
PrepareSingleOrderEvent([Prepare Single Order <br/> Event])
SingleOrderReadyEvent([Single Order <br/> Ready Event])
OrderPackedEvent([Order Packed <br/> Event])
DispatchOrderStep{{Dispatch <br/> Order Step}}
FriedFishStep[[Fried Fish <br/> Process Step]]
PotatoFriesStep[[Potato Fries <br/> Process Step]]
FishSandwichStep[[Fish Sandwich <br/> Process Step]]
FishAndChipsStep[[Fish & Chips <br/> Process Step]]
PackFoodStep[Pack Food <br/> Step]
PrepareSingleOrderEvent -->|Order Received| DispatchOrderStep
DispatchOrderStep -->|Prepare Fried Fish| FriedFishStep -->|Fried Fish Ready| SingleOrderReadyEvent
DispatchOrderStep -->|Prepare Potato Fries| PotatoFriesStep -->|Potato Fries Ready| SingleOrderReadyEvent
DispatchOrderStep -->|Prepare Fish Sandwich| FishSandwichStep -->|Fish Sandwich Ready| SingleOrderReadyEvent
DispatchOrderStep -->|Prepare Fish & Chips| FishAndChipsStep -->|Fish & Chips Ready| SingleOrderReadyEvent
SingleOrderReadyEvent-->PackFoodStep --> OrderPackedEvent
```
## Configuring the Kernel
Similar to the Semantic Kernel Python concept samples, it is necessary to configure the secrets
and keys used by the kernel. See the follow "Configuring the Kernel" [guide](../concepts/README.md#configuring-the-kernel) for
more information.
## Configuring Max Supersteps
Process execution is run with a configuration of `max_supersteps`. By default, the `max_supersteps` is configured at `100`.
To adjust the value, pass the `max_supersteps` keyword argument to the `start` method for the given runtime:
```python
from semantic_kernel.processes.local_runtime.local_kernel_process import start
async with await start(
process=kernel_process,
kernel=kernel,
initial_event=KernelProcessEvent(id=CommonEvents.StartProcess),
max_supersteps=50, # Configure the max number of supersteps for process run
) as process_context:
process_state = await process_context.get_state()
# Handle process state...
```
## Running Concept Samples
Concept samples can be run in an IDE or via the command line. After setting up the required api key
for your AI connector, the samples run without any extra command line arguments.
@@ -0,0 +1,204 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from enum import Enum
from typing import ClassVar
from pydantic import Field
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.contents import ChatHistory
from semantic_kernel.functions import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.processes.kernel_process.kernel_process_step import KernelProcessStep
from semantic_kernel.processes.kernel_process.kernel_process_step_context import KernelProcessStepContext
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
from semantic_kernel.processes.local_runtime.local_event import KernelProcessEvent
from semantic_kernel.processes.local_runtime.local_kernel_process import start
from semantic_kernel.processes.process_builder import ProcessBuilder
class CommonEvents(Enum):
UserInputReceived = "UserInputReceived"
AssistantResponseGenerated = "AssistantResponseGenerated"
class ChatBotEvents(Enum):
StartProcess = "startProcess"
IntroComplete = "introComplete"
AssistantResponseGenerated = "assistantResponseGenerated"
Exit = "exit"
class UserInputState(KernelBaseModel):
user_inputs: list[str] = []
current_input_index: int = 0
class UserInputStep(KernelProcessStep[UserInputState]):
GET_USER_INPUT: ClassVar[str] = "get_user_input"
def create_default_state(self) -> "UserInputState":
"""Creates the default UserInputState."""
return UserInputState()
def populate_user_inputs(self):
"""Method to be overridden by the user to populate with custom user messages."""
pass
async def activate(self, state: KernelProcessStepState[UserInputState]):
"""Activates the step and sets the state."""
state.state = state.state or self.create_default_state()
self.state = state.state
self.populate_user_inputs()
@kernel_function(name=GET_USER_INPUT)
async def get_user_input(self, context: KernelProcessStepContext):
"""Gets the user input."""
if not self.state:
raise ValueError("State has not been initialized")
user_message = input("USER: ")
# print(f"USER: {user_message}")
if "exit" in user_message:
await context.emit_event(process_event=ChatBotEvents.Exit, data=None)
return
self.state.current_input_index += 1
# Emit the user input event
await context.emit_event(process_event=CommonEvents.UserInputReceived, data=user_message)
class ScriptedInputStep(UserInputStep):
def populate_user_inputs(self):
"""Override the method to populate user inputs for the chat step."""
if self.state is not None:
self.state.user_inputs.append("Hello")
self.state.user_inputs.append("How tall is the tallest mountain?")
self.state.user_inputs.append("How low is the lowest valley?")
self.state.user_inputs.append("How wide is the widest river?")
self.state.user_inputs.append("exit")
@kernel_function
async def get_user_input(self, context: KernelProcessStepContext):
"""Gets the user input."""
if not self.state:
raise ValueError("State has not been initialized")
user_message = self.state.user_inputs[self.state.current_input_index]
print(f"USER: {user_message}")
if "exit" in user_message:
await context.emit_event(process_event=ChatBotEvents.Exit, data=None)
return
self.state.current_input_index += 1
# Emit the user input event
await context.emit_event(process_event=CommonEvents.UserInputReceived, data=user_message)
class IntroStep(KernelProcessStep):
@kernel_function
async def print_intro_message(self):
print("Welcome to Processes in Semantic Kernel.\n")
class ChatBotState(KernelBaseModel):
"""The state object for ChatBotResponseStep."""
chat_messages: list = []
SERVICE_ID = "default"
class ChatBotResponseStep(KernelProcessStep[ChatBotState]):
GET_CHAT_RESPONSE: ClassVar[str] = "get_chat_response"
state: ChatBotState = Field(default_factory=ChatBotState)
async def activate(self, state: "KernelProcessStepState[ChatBotState]"):
"""Activates the step and initializes the state object."""
self.state = state.state or ChatBotState()
self.state.chat_messages = self.state.chat_messages or []
@kernel_function(name=GET_CHAT_RESPONSE)
async def get_chat_response(self, context: "KernelProcessStepContext", user_message: str, kernel: "Kernel"):
"""Generates a response from the chat completion service."""
# Add user message to the state
self.state.chat_messages.append({"role": "user", "message": user_message})
# Get chat completion service and generate a response
chat_service: ChatCompletionClientBase = kernel.get_service(service_id=SERVICE_ID)
settings = chat_service.instantiate_prompt_execution_settings(service_id=SERVICE_ID)
chat_history = ChatHistory()
chat_history.add_user_message(user_message)
response = await chat_service.get_chat_message_contents(chat_history=chat_history, settings=settings)
if response is None:
raise ValueError("Failed to get a response from the chat completion service.")
answer = response[0].content
print(f"ASSISTANT: {answer}")
# Update state with the response
self.state.chat_messages.append(answer)
# Emit an event: assistantResponse
await context.emit_event(process_event=ChatBotEvents.AssistantResponseGenerated, data=answer)
kernel = Kernel()
async def step01_processes(scripted: bool = True):
kernel.add_service(OpenAIChatCompletion(service_id="default"))
process = ProcessBuilder(name="ChatBot")
# Define the steps on the process builder based on their types, not concrete objects
intro_step = process.add_step(IntroStep)
user_input_step = process.add_step(ScriptedInputStep if scripted else UserInputStep)
response_step = process.add_step(ChatBotResponseStep)
# Define the input event that starts the process and where to send it
process.on_input_event(event_id=ChatBotEvents.StartProcess).send_event_to(target=intro_step)
# Define the event that triggers the next step in the process
intro_step.on_function_result(function_name=IntroStep.print_intro_message.__name__).send_event_to(
target=user_input_step
)
# Define the event that triggers the process to stop
user_input_step.on_event(event_id=ChatBotEvents.Exit).stop_process()
# For the user step, send the user input to the response step
user_input_step.on_event(event_id=CommonEvents.UserInputReceived).send_event_to(
target=response_step, parameter_name="user_message"
)
# For the response step, send the response back to the user input step
response_step.on_event(event_id=ChatBotEvents.AssistantResponseGenerated).send_event_to(target=user_input_step)
# Build the kernel process
kernel_process = process.build()
# Start the process
await start(
process=kernel_process,
kernel=kernel,
initial_event=KernelProcessEvent(id=ChatBotEvents.StartProcess, data=None),
)
if __name__ == "__main__":
# if you want to run this sample with your won input, set the below parameter to False
asyncio.run(step01_processes(scripted=False))
@@ -0,0 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
from samples.getting_started_with_processes.step03.models.food_ingredients import FoodIngredients
from samples.getting_started_with_processes.step03.models.food_order_item import FoodItem
__all__ = ["FoodIngredients", "FoodItem"]
@@ -0,0 +1,15 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
class FoodIngredients(str, Enum):
POTATOES = "Potatoes"
FISH = "Fish"
BUNS = "Buns"
SAUCE = "Sauce"
CONDIMENTS = "Condiments"
NONE = "None"
def to_friendly_string(self) -> str:
return self.value
@@ -0,0 +1,13 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
class FoodItem(str, Enum):
POTATO_FRIES = "Potato Fries"
FRIED_FISH = "Fried Fish"
FISH_SANDWICH = "Fish Sandwich"
FISH_AND_CHIPS = "Fish & Chips"
def to_friendly_string(self) -> str:
return self.value
@@ -0,0 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from samples.getting_started_with_processes.step03.processes.fish_and_chips_process import FishAndChipsProcess
from samples.getting_started_with_processes.step03.processes.fish_sandwich_process import FishSandwichProcess
from samples.getting_started_with_processes.step03.processes.fried_fish_process import FriedFishProcess
__all__ = ["FishAndChipsProcess", "FriedFishProcess", "FishSandwichProcess"]
@@ -0,0 +1,107 @@
# Copyright (c) Microsoft. All rights reserved.
import json
from enum import Enum
from samples.getting_started_with_processes.step03.processes.fried_fish_process import (
FriedFishProcess,
)
from samples.getting_started_with_processes.step03.processes.potato_fries_process import (
PotatoFriesProcess,
)
from samples.getting_started_with_processes.step03.steps.external_step import ExternalStep
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.processes import ProcessBuilder
from semantic_kernel.processes.kernel_process import (
KernelProcessStep,
KernelProcessStepContext,
)
class AddFishAndChipsCondimentsStep(KernelProcessStep):
class Functions(Enum):
AddCondiments = "AddCondiments"
class OutputEvents(Enum):
CondimentsAdded = "CondimentsAdded"
@kernel_function(name=Functions.AddCondiments)
async def add_condiments(
self, context: KernelProcessStepContext, fish_actions: list[str], potato_actions: list[str]
):
print(
f"ADD_CONDIMENTS: Added condiments to Fish & Chips - "
f"Fish: {json.dumps(fish_actions)}, Potatoes: {json.dumps(potato_actions)}"
)
fish_actions.extend(potato_actions)
fish_actions.append("Condiments")
await context.emit_event(
process_event=self.OutputEvents.CondimentsAdded,
data=fish_actions,
)
class FishAndChipsProcess:
class ProcessEvents(Enum):
PrepareFishAndChips = "PrepareFishAndChips"
FishAndChipsReady = "FishAndChipsReady"
FishAndChipsIngredientOutOfStock = "FishAndChipsIngredientOutOfStock"
class ExternalFishAndChipsStep(ExternalStep):
def __init__(self) -> None:
super().__init__(FishAndChipsProcess.ProcessEvents.FishAndChipsReady)
@staticmethod
def create_process(process_name: str = "FishAndChipsProcess") -> ProcessBuilder:
process_builder = ProcessBuilder(process_name)
make_fish_step = process_builder.add_step_from_process(FriedFishProcess.create_process())
make_potato_step = process_builder.add_step_from_process(PotatoFriesProcess.create_process())
add_condiments_step = process_builder.add_step(AddFishAndChipsCondimentsStep)
external_step = process_builder.add_step(FishAndChipsProcess.ExternalFishAndChipsStep)
process_builder.on_input_event(FishAndChipsProcess.ProcessEvents.PrepareFishAndChips).send_event_to(
make_fish_step.where_input_event_is(FriedFishProcess.ProcessEvents.PrepareFriedFish)
).send_event_to(make_potato_step.where_input_event_is(PotatoFriesProcess.ProcessEvents.PreparePotatoFries))
make_fish_step.on_event(FriedFishProcess.ProcessEvents.FriedFishReady).send_event_to(
add_condiments_step, parameter_name="fishActions"
)
make_potato_step.on_event(PotatoFriesProcess.ProcessEvents.PotatoFriesReady).send_event_to(
add_condiments_step, parameter_name="potatoActions"
)
add_condiments_step.on_event(AddFishAndChipsCondimentsStep.OutputEvents.CondimentsAdded).send_event_to(
external_step
)
return process_builder
@staticmethod
def create_process_with_stateful_steps(
process_name: str = "FishAndChipsWithStatefulStepsProcess",
) -> ProcessBuilder:
process_builder = ProcessBuilder(process_name)
make_fish_step = process_builder.add_step_from_process(FriedFishProcess.create_process_with_stateful_steps_v1())
make_potato_step = process_builder.add_step_from_process(
PotatoFriesProcess.create_process_with_stateful_steps()
)
add_condiments_step = process_builder.add_step(AddFishAndChipsCondimentsStep)
external_step = process_builder.add_step(FishAndChipsProcess.ExternalFishAndChipsStep)
process_builder.on_input_event(FishAndChipsProcess.ProcessEvents.PrepareFishAndChips).send_event_to(
make_fish_step.where_input_event_is(FriedFishProcess.ProcessEvents.PrepareFriedFish)
).send_event_to(make_potato_step.where_input_event_is(PotatoFriesProcess.ProcessEvents.PreparePotatoFries))
make_fish_step.on_event(FriedFishProcess.ProcessEvents.FriedFishReady).send_event_to(
add_condiments_step, parameter_name="fishActions"
)
make_potato_step.on_event(PotatoFriesProcess.ProcessEvents.PotatoFriesReady).send_event_to(
add_condiments_step, parameter_name="potatoActions"
)
add_condiments_step.on_event(AddFishAndChipsCondimentsStep.OutputEvents.CondimentsAdded).send_event_to(
external_step
)
return process_builder
@@ -0,0 +1,129 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from samples.getting_started_with_processes.step03.processes.fried_fish_process import (
FriedFishProcess,
)
from samples.getting_started_with_processes.step03.steps.external_step import ExternalStep
from semantic_kernel.functions import kernel_function
from semantic_kernel.processes import ProcessBuilder
from semantic_kernel.processes.kernel_process import (
KernelProcessEventVisibility,
KernelProcessStep,
KernelProcessStepContext,
)
class AddBunStep(KernelProcessStep):
class Functions(Enum):
AddBuns = "AddBuns"
class OutputEvents(Enum):
BunsAdded = "BunsAdded"
@kernel_function(name=Functions.AddBuns)
async def slice_food(self, context: KernelProcessStepContext, food_actions: list[str]):
print(f"BUNS_ADDED_STEP: Buns added to ingredient {food_actions[0]}")
food_actions.append("Buns")
await context.emit_event(process_event=self.OutputEvents.BunsAdded, data=food_actions)
class AddSpecialSauceStep(KernelProcessStep):
class Functions(Enum):
AddSpecialSauce = "AddSpecialSauce"
class OutputEvents(Enum):
SpecialSauceAdded = "SpecialSauceAdded"
@kernel_function(name=Functions.AddSpecialSauce)
async def slice_food(self, context: KernelProcessStepContext, food_actions: list[str]):
print(f"SPECIAL_SAUCE_ADDED: Special sauce added to ingredient {food_actions[0]}")
food_actions.append("Sauce")
await context.emit_event(
process_event=self.OutputEvents.SpecialSauceAdded,
data=food_actions,
visibility=KernelProcessEventVisibility.Public,
)
class ExternalFriedFishStep(ExternalStep):
def __init__(self) -> None:
super().__init__(FishSandwichProcess.ProcessEvents.FishSandwichReady)
class FishSandwichProcess:
class ProcessEvents(Enum):
PrepareFishSandwich = "PrepareFishSandwich"
FishSandwichReady = "FishSandwichReady"
@staticmethod
def create_process(process_name: str = "FishSandwichProcess") -> ProcessBuilder:
process_builder = ProcessBuilder(process_name)
make_fried_fish_step = process_builder.add_step_from_process(FriedFishProcess.create_process())
add_buns_step = process_builder.add_step(AddBunStep)
add_sauce_step = process_builder.add_step(AddSpecialSauceStep)
external_step = process_builder.add_step(ExternalFriedFishStep)
process_builder.on_input_event(FishSandwichProcess.ProcessEvents.PrepareFishSandwich).send_event_to(
make_fried_fish_step.where_input_event_is(FriedFishProcess.ProcessEvents.PrepareFriedFish)
)
make_fried_fish_step.on_event(FriedFishProcess.ProcessEvents.FriedFishReady).send_event_to(add_buns_step)
add_buns_step.on_event(AddBunStep.OutputEvents.BunsAdded).send_event_to(add_sauce_step)
add_sauce_step.on_event(AddSpecialSauceStep.OutputEvents.SpecialSauceAdded).send_event_to(external_step)
return process_builder
@staticmethod
def create_process_with_stateful_steps_v1(
process_name: str = "FishSandwichWithStatefulStepsProcess",
) -> ProcessBuilder:
process_builder = ProcessBuilder(process_name, version="FishSandwich.V1")
make_fried_fish_step = process_builder.add_step_from_process(
FriedFishProcess.create_process_with_stateful_steps_v1()
)
add_buns_step = process_builder.add_step(AddBunStep)
add_sauce_step = process_builder.add_step(AddSpecialSauceStep)
external_step = process_builder.add_step(ExternalFriedFishStep)
process_builder.on_input_event(FishSandwichProcess.ProcessEvents.PrepareFishSandwich).send_event_to(
make_fried_fish_step.where_input_event_is(FriedFishProcess.ProcessEvents.PrepareFriedFish)
)
make_fried_fish_step.on_event(FriedFishProcess.ProcessEvents.FriedFishReady).send_event_to(add_buns_step)
add_buns_step.on_event(AddBunStep.OutputEvents.BunsAdded).send_event_to(add_sauce_step)
add_sauce_step.on_event(AddSpecialSauceStep.OutputEvents.SpecialSauceAdded).send_event_to(external_step)
return process_builder
@staticmethod
def create_process_with_stateful_steps_v2(
process_name: str = "FishSandwichWithStatefulStepsProcess",
) -> ProcessBuilder:
process_builder = ProcessBuilder(process_name, version="FishSandwich.V2")
make_fried_fish_step = process_builder.add_step_from_process(
FriedFishProcess.create_process_with_stateful_steps_v2(),
aliases=["FriedFishWithStatefulStepsProcess"],
)
add_buns_step = process_builder.add_step(AddBunStep)
add_sauce_step = process_builder.add_step(AddSpecialSauceStep)
external_step = process_builder.add_step(ExternalFriedFishStep)
process_builder.on_input_event(FishSandwichProcess.ProcessEvents.PrepareFishSandwich).send_event_to(
make_fried_fish_step.where_input_event_is(FriedFishProcess.ProcessEvents.PrepareFriedFish)
)
make_fried_fish_step.on_event(FriedFishProcess.ProcessEvents.FriedFishReady).send_event_to(add_buns_step)
add_buns_step.on_event(AddBunStep.OutputEvents.BunsAdded).send_event_to(add_sauce_step)
add_sauce_step.on_event(AddSpecialSauceStep.OutputEvents.SpecialSauceAdded).send_event_to(external_step)
return process_builder
@@ -0,0 +1,118 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from samples.getting_started_with_processes.step03.models import FoodIngredients
from samples.getting_started_with_processes.step03.steps import (
CutFoodStep,
FryFoodStep,
GatherIngredientsStep,
)
from samples.getting_started_with_processes.step03.steps.cut_food_with_sharpening_step import (
CutFoodWithSharpeningStep,
)
from samples.getting_started_with_processes.step03.steps.gather_ingredients_step import (
GatherIngredientsWithStockStep,
)
from semantic_kernel.processes import ProcessBuilder
from semantic_kernel.processes.kernel_process import (
kernel_process_step_metadata,
)
@kernel_process_step_metadata("GatherFishIngredient.V1")
class GatherFriedFishIngredientsStep(GatherIngredientsStep):
def __init__(self) -> None:
super().__init__(FoodIngredients.FISH)
@kernel_process_step_metadata("GatherFishIngredient.V2")
class GatherFriedFishIngredientsWithStockStep(GatherIngredientsWithStockStep):
def __init__(self) -> None:
super().__init__(FoodIngredients.FISH)
class FriedFishProcess:
class ProcessEvents(Enum):
PrepareFriedFish = "PrepareFriedFish"
FriedFishReady = FryFoodStep.OutputEvents.FriedFoodReady.value
@staticmethod
def create_process(process_name: str = "FriedFishProcess") -> ProcessBuilder:
process_builder = ProcessBuilder(process_name)
gather = process_builder.add_step(GatherFriedFishIngredientsStep)
chop = process_builder.add_step(CutFoodStep, name="chopStep")
fry = process_builder.add_step(FryFoodStep)
process_builder.on_input_event(FriedFishProcess.ProcessEvents.PrepareFriedFish).send_event_to(gather)
gather.on_event(GatherFriedFishIngredientsStep.OutputEvents.IngredientsGathered).send_event_to(
chop, function_name=CutFoodStep.Functions.ChopFood
)
chop.on_event(CutFoodStep.OutputEvents.ChoppingReady).send_event_to(fry)
fry.on_event(FryFoodStep.OutputEvents.FoodRuined).send_event_to(gather)
return process_builder
@staticmethod
def create_process_with_stateful_steps_v1(
process_name: str = "FriedFishWithStatefulStepsProcess",
) -> ProcessBuilder:
process_builder = ProcessBuilder(name=process_name, version="FriedFishProcess.v1")
gather = process_builder.add_step(GatherFriedFishIngredientsWithStockStep)
chop = process_builder.add_step(CutFoodWithSharpeningStep, name="chopStep")
fry = process_builder.add_step(FryFoodStep)
process_builder.on_input_event(FriedFishProcess.ProcessEvents.PrepareFriedFish).send_event_to(gather)
gather.on_event(GatherFriedFishIngredientsWithStockStep.OutputEvents.IngredientsGathered).send_event_to(
chop, function_name=CutFoodWithSharpeningStep.Functions.ChopFood
)
gather.on_event(GatherFriedFishIngredientsWithStockStep.OutputEvents.IngredientsOutOfStock).stop_process()
chop.on_event(CutFoodWithSharpeningStep.OutputEvents.ChoppingReady).send_event_to(fry)
chop.on_event(CutFoodWithSharpeningStep.OutputEvents.KnifeNeedsSharpening).send_event_to(
chop, function_name=CutFoodWithSharpeningStep.Functions.SharpenKnife
)
chop.on_event(CutFoodWithSharpeningStep.OutputEvents.KnifeSharpened).send_event_to(
chop, function_name=CutFoodWithSharpeningStep.Functions.ChopFood
)
fry.on_event(FryFoodStep.OutputEvents.FoodRuined).send_event_to(gather)
return process_builder
@staticmethod
def create_process_with_stateful_steps_v2(
process_name: str = "FriedFishWithStatefulStepsProcess",
) -> ProcessBuilder:
process_builder = ProcessBuilder(name=process_name, version="FriedFishProcess.v2")
gather = process_builder.add_step(
GatherFriedFishIngredientsWithStockStep,
name="gatherFishIngredientStep",
aliases=["GatherFriedFishIngredientsWithStockStep"],
)
chop = process_builder.add_step(
CutFoodWithSharpeningStep,
name="chopFishStep",
aliases=["CutFoodStep"],
)
fry = process_builder.add_step(FryFoodStep, name="fryFishStep", aliases=["FryFoodStep"])
process_builder.on_input_event(FriedFishProcess.ProcessEvents.PrepareFriedFish).send_event_to(gather)
gather.on_event(GatherFriedFishIngredientsWithStockStep.OutputEvents.IngredientsGathered).send_event_to(
chop, function_name=CutFoodWithSharpeningStep.Functions.ChopFood
)
gather.on_event(GatherFriedFishIngredientsWithStockStep.OutputEvents.IngredientsOutOfStock).stop_process()
chop.on_event(CutFoodWithSharpeningStep.OutputEvents.ChoppingReady).send_event_to(fry)
chop.on_event(CutFoodWithSharpeningStep.OutputEvents.KnifeNeedsSharpening).send_event_to(
chop, function_name=CutFoodWithSharpeningStep.Functions.SharpenKnife
)
chop.on_event(CutFoodWithSharpeningStep.OutputEvents.KnifeSharpened).send_event_to(
chop, function_name=CutFoodWithSharpeningStep.Functions.ChopFood
)
fry.on_event(FryFoodStep.OutputEvents.FoodRuined).send_event_to(gather)
return process_builder
@@ -0,0 +1,83 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from samples.getting_started_with_processes.step03.models.food_ingredients import FoodIngredients
from samples.getting_started_with_processes.step03.steps.cut_food_step import CutFoodStep
from samples.getting_started_with_processes.step03.steps.cut_food_with_sharpening_step import CutFoodWithSharpeningStep
from samples.getting_started_with_processes.step03.steps.fry_food_step import FryFoodStep
from samples.getting_started_with_processes.step03.steps.gather_ingredients_step import (
GatherIngredientsStep,
GatherIngredientsWithStockStep,
)
from semantic_kernel.processes import ProcessBuilder
class GatherPotatoFriesIngredientsStep(GatherIngredientsStep):
def __init__(self):
super().__init__(FoodIngredients.POTATOES)
class GatherPotatoFriesIngredientsWithStockStep(GatherIngredientsWithStockStep):
def __init__(self):
super().__init__(FoodIngredients.POTATOES)
class PotatoFriesProcess:
class ProcessEvents(Enum):
PreparePotatoFries = "PreparePotatoFries"
PotatoFriesReady = FryFoodStep.OutputEvents.FriedFoodReady.value
@staticmethod
def create_process(process_name: str = "PotatoFriesProcess"):
process_builder = ProcessBuilder(process_name)
gather_ingredients_step = process_builder.add_step(GatherPotatoFriesIngredientsStep)
slice_step = process_builder.add_step(CutFoodStep, name="sliceStep")
fry_step = process_builder.add_step(FryFoodStep)
process_builder.on_input_event(PotatoFriesProcess.ProcessEvents.PreparePotatoFries).send_event_to(
gather_ingredients_step
)
gather_ingredients_step.on_event(
GatherPotatoFriesIngredientsStep.OutputEvents.IngredientsGathered
).send_event_to(slice_step, function_name=CutFoodStep.Functions.SliceFood)
slice_step.on_event(CutFoodStep.OutputEvents.SlicingReady).send_event_to(fry_step)
fry_step.on_event(FryFoodStep.OutputEvents.FoodRuined).send_event_to(gather_ingredients_step)
return process_builder
@staticmethod
def create_process_with_stateful_steps(process_name: str = "PotatoFriesWithStatefulStepsProcess"):
process_builder = ProcessBuilder(process_name)
gather_ingredients_step = process_builder.add_step(GatherPotatoFriesIngredientsWithStockStep)
slice_step = process_builder.add_step(CutFoodWithSharpeningStep, name="sliceStep")
fry_step = process_builder.add_step(FryFoodStep)
process_builder.on_input_event(PotatoFriesProcess.ProcessEvents.PreparePotatoFries).send_event_to(
gather_ingredients_step
)
gather_ingredients_step.on_event(
GatherPotatoFriesIngredientsWithStockStep.OutputEvents.IngredientsGathered
).send_event_to(slice_step, function_name=CutFoodWithSharpeningStep.Functions.SliceFood)
gather_ingredients_step.on_event(
GatherPotatoFriesIngredientsWithStockStep.OutputEvents.IngredientsOutOfStock
).stop_process()
slice_step.on_event(CutFoodWithSharpeningStep.OutputEvents.SlicingReady).send_event_to(fry_step)
slice_step.on_event(CutFoodWithSharpeningStep.OutputEvents.KnifeNeedsSharpening).send_event_to(
slice_step, function_name=CutFoodWithSharpeningStep.Functions.SharpenKnife
)
slice_step.on_event(CutFoodWithSharpeningStep.OutputEvents.KnifeSharpened).send_event_to(
slice_step, function_name=CutFoodWithSharpeningStep.Functions.SliceFood
)
fry_step.on_event(FryFoodStep.OutputEvents.FoodRuined).send_event_to(gather_ingredients_step)
return process_builder
@@ -0,0 +1,122 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from samples.getting_started_with_processes.step03.models.food_order_item import FoodItem
from samples.getting_started_with_processes.step03.processes.fish_and_chips_process import FishAndChipsProcess
from samples.getting_started_with_processes.step03.processes.fish_sandwich_process import FishSandwichProcess
from samples.getting_started_with_processes.step03.processes.fried_fish_process import FriedFishProcess
from samples.getting_started_with_processes.step03.processes.potato_fries_process import PotatoFriesProcess
from samples.getting_started_with_processes.step03.steps.external_step import ExternalStep
from semantic_kernel.functions import kernel_function
from semantic_kernel.processes import ProcessBuilder
from semantic_kernel.processes.kernel_process import KernelProcessStep, KernelProcessStepContext
class PackOrderStep(KernelProcessStep):
class Functions(Enum):
PackFood = "PackFood"
class OutputEvents(Enum):
FoodPacked = "FoodPacked"
@kernel_function(name=Functions.PackFood)
async def pack_food(self, context: KernelProcessStepContext, food_actions: list[str]):
print(f"PACKING_FOOD: Food {food_actions[0]} Packed! - {food_actions}")
await context.emit_event(process_event=PackOrderStep.OutputEvents.FoodPacked)
class ExternalSingleOrderStep(ExternalStep):
def __init__(self):
super().__init__(SingleFoodItemProcess.ProcessEvents.SingleOrderReady)
class DispatchSingleOrderStep(KernelProcessStep):
class Functions(Enum):
PrepareSingleOrder = "PrepareSingleOrder"
class OutputEvents(Enum):
PrepareFries = "PrepareFries"
PrepareFriedFish = "PrepareFriedFish"
PrepareFishSandwich = "PrepareFishSandwich"
PrepareFishAndChips = "PrepareFishAndChips"
@kernel_function(name=Functions.PrepareSingleOrder)
async def dispatch_single_order(self, context: KernelProcessStepContext, food_item: FoodItem):
food_name = food_item.to_friendly_string()
print(f"DISPATCH_SINGLE_ORDER: Dispatching '{food_name}'!")
food_actions = []
if food_item == FoodItem.POTATO_FRIES:
await context.emit_event(process_event=DispatchSingleOrderStep.OutputEvents.PrepareFries, data=food_actions)
elif food_item == FoodItem.FRIED_FISH:
await context.emit_event(
process_event=DispatchSingleOrderStep.OutputEvents.PrepareFriedFish, data=food_actions
)
elif food_item == FoodItem.FISH_SANDWICH:
await context.emit_event(
process_event=DispatchSingleOrderStep.OutputEvents.PrepareFishSandwich, data=food_actions
)
elif food_item == FoodItem.FISH_AND_CHIPS:
await context.emit_event(
process_event=DispatchSingleOrderStep.OutputEvents.PrepareFishAndChips, data=food_actions
)
class SingleFoodItemProcess:
class ProcessEvents(Enum):
SingleOrderReceived = "SingleOrderReceived"
SingleOrderReady = "SingleOrderReady"
@staticmethod
def create_process(process_name: str = "SingleFoodItemProcess"):
process_builder = ProcessBuilder(process_name)
dispatch_order_step = process_builder.add_step(DispatchSingleOrderStep)
make_fried_fish_step = process_builder.add_step_from_process(FriedFishProcess.create_process())
make_potato_fries_step = process_builder.add_step_from_process(PotatoFriesProcess.create_process())
make_fish_sandwich_step = process_builder.add_step_from_process(FishSandwichProcess.create_process())
make_fish_and_chips_step = process_builder.add_step_from_process(FishAndChipsProcess.create_process())
pack_order_step = process_builder.add_step(PackOrderStep)
external_step = process_builder.add_step(ExternalSingleOrderStep)
process_builder.on_input_event(SingleFoodItemProcess.ProcessEvents.SingleOrderReceived).send_event_to(
dispatch_order_step
)
dispatch_order_step.on_event(DispatchSingleOrderStep.OutputEvents.PrepareFriedFish).send_event_to(
make_fried_fish_step.where_input_event_is(FriedFishProcess.ProcessEvents.PrepareFriedFish)
)
dispatch_order_step.on_event(DispatchSingleOrderStep.OutputEvents.PrepareFries).send_event_to(
make_potato_fries_step.where_input_event_is(PotatoFriesProcess.ProcessEvents.PreparePotatoFries)
)
dispatch_order_step.on_event(DispatchSingleOrderStep.OutputEvents.PrepareFishSandwich).send_event_to(
make_fish_sandwich_step.where_input_event_is(FishSandwichProcess.ProcessEvents.PrepareFishSandwich)
)
dispatch_order_step.on_event(DispatchSingleOrderStep.OutputEvents.PrepareFishAndChips).send_event_to(
make_fish_and_chips_step.where_input_event_is(FishAndChipsProcess.ProcessEvents.PrepareFishAndChips)
)
make_fried_fish_step.on_event(FriedFishProcess.ProcessEvents.FriedFishReady).send_event_to(pack_order_step)
make_potato_fries_step.on_event(PotatoFriesProcess.ProcessEvents.PotatoFriesReady).send_event_to(
pack_order_step
)
make_fish_sandwich_step.on_event(FishSandwichProcess.ProcessEvents.FishSandwichReady).send_event_to(
pack_order_step
)
make_fish_and_chips_step.on_event(FishAndChipsProcess.ProcessEvents.FishAndChipsReady).send_event_to(
pack_order_step
)
pack_order_step.on_event(PackOrderStep.OutputEvents.FoodPacked).send_event_to(external_step)
return process_builder
@@ -0,0 +1,60 @@
{
"$type": "Process",
"id": "612ad7ba84ea4b4593cfccd0092bc429",
"name": "FishSandwichWithStatefulStepsProcess",
"versionInfo": "FishSandwich.V1",
"stepsState": {
"FriedFishWithStatefulStepsProcess": {
"$type": "Process",
"id": "1632a2f0cec7417b825768625f8444e7",
"name": "FriedFishWithStatefulStepsProcess",
"versionInfo": "FriedFishProcess.v1",
"stepsState": {
"GatherFriedFishIngredientsWithStockStep": {
"$type": "Step",
"id": "b146fbb374ae4751b8e2bdf029b6cd69",
"name": "GatherFriedFishIngredientsWithStockStep",
"versionInfo": "GatherFishIngredient.V2",
"state": {
"IngredientsStock": 3
}
},
"chopStep": {
"$type": "Step",
"id": "5be2e3825a7948d9bfa494aa907166d3",
"name": "chopStep",
"versionInfo": "CutFoodStep.V2",
"state": {
"KnifeSharpness": 3,
"NeedsSharpeningLimit": 3,
"SharpeningBoost": 5
}
},
"FryFoodStep": {
"$type": "Step",
"id": "a975d1b33c634cf093179fea9d271b84",
"name": "FryFoodStep",
"versionInfo": "FryFoodStep.V1"
}
}
},
"AddBunStep": {
"$type": "Step",
"id": "5f4f96f48a1b4e83b4101688550491d3",
"name": "AddBunStep",
"versionInfo": "v1"
},
"AddSpecialSauceStep": {
"$type": "Step",
"id": "67e4b77e63cb447ea774fe9665019e71",
"name": "AddSpecialSauceStep",
"versionInfo": "v1"
},
"ExternalFriedFishStep": {
"$type": "Step",
"id": "9ecebab987cf4667b9abe48640034707",
"name": "ExternalFriedFishStep",
"versionInfo": "v1"
}
}
}
@@ -0,0 +1,59 @@
{
"$type": "Process",
"name": "FishSandwichWithStatefulStepsProcess",
"versionInfo": "FishSandwich.V1",
"stepsState": {
"FriedFishWithStatefulStepsProcess": {
"$type": "Process",
"id": "d4dc8776d4574b95a4cc39e2e7667018",
"name": "FriedFishWithStatefulStepsProcess",
"versionInfo": "FriedFishProcess.v1",
"stepsState": {
"GatherFriedFishIngredientsWithStockStep": {
"$type": "Step",
"id": "94d2df838d314d4e8fd694398f6a3ada",
"name": "GatherFriedFishIngredientsWithStockStep",
"versionInfo": "GatherFishIngredient.V2",
"state": {
"IngredientsStock": 1
}
},
"chopStep": {
"$type": "Step",
"id": "467e52a1187f497d865b897d56b68db0",
"name": "chopStep",
"versionInfo": "CutFoodStep.V2",
"state": {
"KnifeSharpness": 5,
"NeedsSharpeningLimit": 3,
"SharpeningBoost": 5
}
},
"FryFoodStep": {
"$type": "Step",
"id": "ba3a93b5885e4b75af0c9a46015e3e66",
"name": "FryFoodStep",
"versionInfo": "FryFoodStep.V1"
}
}
},
"AddBunStep": {
"$type": "Step",
"id": "36448a39e963432caded6ca3206849e6",
"name": "AddBunStep",
"versionInfo": "v1"
},
"AddSpecialSauceStep": {
"$type": "Step",
"id": "d1ac856e1e5f49e6a9f389c870bc9aa1",
"name": "AddSpecialSauceStep",
"versionInfo": "v1"
},
"ExternalFriedFishStep": {
"$type": "Step",
"id": "ea1a9b2f08d7407b84093ffb4f34daca",
"name": "ExternalFriedFishStep",
"versionInfo": "v1"
}
}
}
@@ -0,0 +1,34 @@
{
"$type": "Process",
"id": "e0dd9eb8160e480daaaef49a14bcf551",
"name": "FriedFishWithStatefulStepsProcess",
"versionInfo": "FriedFishProcess.v1",
"stepsState": {
"GatherFriedFishIngredientsWithStockStep": {
"$type": "Step",
"id": "480bfb877a9c48a599e956312747879b",
"name": "GatherFriedFishIngredientsWithStockStep",
"versionInfo": "GatherFishIngredient.V2",
"state": {
"IngredientsStock": 4
}
},
"chopStep": {
"$type": "Step",
"id": "63b794c970ca4bf4816545bff7395c1c",
"name": "chopStep",
"versionInfo": "CutFoodStep.V2",
"state": {
"KnifeSharpness": 4,
"NeedsSharpeningLimit": 3,
"SharpeningBoost": 5
}
},
"FryFoodStep": {
"$type": "Step",
"id": "66545dcca7394071a434ec1e66116517",
"name": "FryFoodStep",
"versionInfo": "FryFoodStep.V1"
}
}
}
@@ -0,0 +1,33 @@
{
"$type": "Process",
"name": "FriedFishWithStatefulStepsProcess",
"versionInfo": "FriedFishProcess.v1",
"stepsState": {
"GatherFriedFishIngredientsWithStockStep": {
"$type": "Step",
"id": "9f4974949a2e4a54a67ccaf83ca4396b",
"name": "GatherFriedFishIngredientsWithStockStep",
"versionInfo": "GatherFishIngredient.V2",
"state": {
"IngredientsStock": 1
}
},
"chopStep": {
"$type": "Step",
"id": "c6669a103319475c92cdfc10aa30548a",
"name": "chopStep",
"versionInfo": "CutFoodStep.V2",
"state": {
"KnifeSharpness": 5,
"NeedsSharpeningLimit": 3,
"SharpeningBoost": 5
}
},
"FryFoodStep": {
"$type": "Step",
"id": "bd9f5eb584bc40bcbb3c79d9565efd86",
"name": "FryFoodStep",
"versionInfo": "FryFoodStep.V1"
}
}
}
@@ -0,0 +1,33 @@
{
"$type": "Process",
"name": "FriedFishWithStatefulStepsProcess",
"versionInfo": "FriedFishProcess.v1",
"stepsState": {
"GatherFriedFishIngredientsWithStockStep": {
"$type": "Step",
"id": "9f4974949a2e4a54a67ccaf83ca4396b",
"name": "GatherFriedFishIngredientsWithStockStep",
"versionInfo": "GatherFishIngredient.V2",
"state": {
"IngredientsStock": 0
}
},
"chopStep": {
"$type": "Step",
"id": "c6669a103319475c92cdfc10aa30548a",
"name": "chopStep",
"versionInfo": "CutFoodStep.V2",
"state": {
"KnifeSharpness": 5,
"NeedsSharpeningLimit": 3,
"SharpeningBoost": 5
}
},
"FryFoodStep": {
"$type": "Step",
"id": "bd9f5eb584bc40bcbb3c79d9565efd86",
"name": "FryFoodStep",
"versionInfo": "FryFoodStep.V1"
}
}
}
@@ -0,0 +1,444 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import json
from enum import Enum
from pathlib import Path
from samples.getting_started_with_processes.step03.processes.fish_and_chips_process import FishAndChipsProcess
from samples.getting_started_with_processes.step03.processes.fish_sandwich_process import FishSandwichProcess
from samples.getting_started_with_processes.step03.processes.fried_fish_process import FriedFishProcess
from samples.getting_started_with_processes.step03.processes.potato_fries_process import PotatoFriesProcess
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.kernel import Kernel
from semantic_kernel.processes.kernel_process import KernelProcess, KernelProcessEvent, KernelProcessStateMetadata
from semantic_kernel.processes.local_runtime.local_kernel_process import start
from semantic_kernel.processes.process_builder import ProcessBuilder
"""
Demonstrate the creation of KernelProcess and eliciting different food related events.
This includes load/save steps for state management.
For visual reference of the processes used here check the diagram in:
https://github.com/microsoft/semantic-kernel/tree/main/python/samples/
getting_started_with_processes#step03b_food_ordering
"""
# region Helper Methods
def _create_kernel_with_chat_completion(service_id: str = "sample") -> Kernel:
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(service_id=service_id), overwrite=True)
return kernel
async def use_prepare_specific_product(process: ProcessBuilder, external_trigger_event: Enum):
"""
Helper function that:
1. Builds a KernelProcess from a ProcessBuilder
2. Starts it with the given external event
3. Waits for completion
"""
kernel = _create_kernel_with_chat_completion("sample")
kernel_process = process.build()
print(f"=== Start SK Process '{process.name}' ===")
async with await start(
kernel_process,
kernel,
KernelProcessEvent(id=external_trigger_event.value, data=[]),
):
pass
print(f"=== End SK Process '{process.name}' ===\n")
async def execute_process_with_state(
process: KernelProcess, kernel: Kernel, external_trigger_event: Enum, order_label: str
) -> KernelProcess:
"""
Starts the provided KernelProcess (with optional existing state),
returns the updated state after the run.
"""
print(f"=== {order_label} ===")
async with await start(
process,
kernel,
KernelProcessEvent(id=external_trigger_event.value, data=[]),
) as running_process:
return await running_process.get_state()
# endregion
# region Local JSON file handling for process state
BASE_DIR = Path(__file__).resolve().parent.parent
# Build the path to "step03/processes_states"
PROCESS_STATE_DIRECTORY = BASE_DIR / "step03" / "processes_states"
PROCESS_STATE_DIRECTORY.mkdir(parents=True, exist_ok=True)
def dump_process_state_metadata_locally(process_state: KernelProcessStateMetadata, json_filename: str) -> None:
"""
Saves the ProcessStateMetadata to a local JSON file in step03/processes_states,
relative to the current script's grandparent folder.
"""
file_path = PROCESS_STATE_DIRECTORY / json_filename
with open(file_path, "w", encoding="utf-8") as f:
json.dump(process_state.model_dump(exclude_none=True, by_alias=True, mode="json"), f, indent=4)
print(f"Process state saved to '{file_path.resolve()}'")
def load_process_state_metadata(json_filename: str) -> KernelProcessStateMetadata | None:
"""
Loads the ProcessStateMetadata from step03/processes_states if it exists.
Returns None if the file doesn't exist or fails to parse.
"""
file_path = PROCESS_STATE_DIRECTORY / json_filename
if not file_path.exists():
print(f"No such file: '{file_path.resolve()}'")
return None
try:
with open(file_path, encoding="utf-8") as f:
return KernelProcessStateMetadata.model_validate_json(f)
except Exception as ex:
print(f"Error reading state file '{file_path.resolve()}': {ex}")
return None
# endregion
# region Stateless Processes
async def use_prepare_fried_fish_process():
process = FriedFishProcess.create_process()
await use_prepare_specific_product(process, FriedFishProcess.ProcessEvents.PrepareFriedFish)
async def use_prepare_potato_fries_process():
process = PotatoFriesProcess.create_process()
await use_prepare_specific_product(process, PotatoFriesProcess.ProcessEvents.PreparePotatoFries)
async def use_prepare_fish_sandwich_process():
process = FishSandwichProcess.create_process()
await use_prepare_specific_product(process, FishSandwichProcess.ProcessEvents.PrepareFishSandwich)
async def use_prepare_fish_and_chips_process():
process = FishAndChipsProcess.create_process()
await use_prepare_specific_product(process, FishAndChipsProcess.ProcessEvents.PrepareFishAndChips)
# endregion
# region Stateful Processes
async def use_prepare_stateful_fried_fish_process_no_shared_state():
"""
Showcases building the stateful process multiple times.
Each new build has fresh/independent state.
"""
builder = FriedFishProcess.create_process_with_stateful_steps_v1()
event_name = FriedFishProcess.ProcessEvents.PrepareFriedFish
kernel = _create_kernel_with_chat_completion()
print(f"=== Start SK Process '{builder.name}' ===")
# Each build() -> new instance
await execute_process_with_state(builder.build(), kernel, event_name, "Order 1")
await execute_process_with_state(builder.build(), kernel, event_name, "Order 2")
print(f"=== End SK Process '{builder.name}' ===\n")
async def use_prepare_stateful_fried_fish_process_shared_state():
"""
Showcases building the stateful process once and reusing it,
meaning runs #2 and #3 continue from the prior run's state.
"""
builder = FriedFishProcess.create_process_with_stateful_steps_v2()
kernel = _create_kernel_with_chat_completion()
event_name = FriedFishProcess.ProcessEvents.PrepareFriedFish
single_process = builder.build() # one instance => shared state
print(f"=== Start SK Process '{builder.name}' ===")
await execute_process_with_state(single_process, kernel, event_name, "Order 1")
await execute_process_with_state(single_process, kernel, event_name, "Order 2")
await execute_process_with_state(single_process, kernel, event_name, "Order 3")
print(f"=== End SK Process '{builder.name}' ===\n")
async def use_prepare_stateful_potato_fries_process_shared_state():
builder = PotatoFriesProcess.create_process_with_stateful_steps()
kernel = _create_kernel_with_chat_completion()
event_name = PotatoFriesProcess.ProcessEvents.PreparePotatoFries
shared_process = builder.build()
print(f"=== Start SK Process '{builder.name}' ===")
await execute_process_with_state(shared_process, kernel, event_name, "Order 1")
await execute_process_with_state(shared_process, kernel, event_name, "Order 2")
await execute_process_with_state(shared_process, kernel, event_name, "Order 3")
print(f"=== End SK Process '{builder.name}' ===\n")
# endregion
# region Filenames for sample states
STATEFUL_FRIED_FISH_PROCESS_FILENAME = "FriedFishProcessStateSuccess.json"
STATEFUL_FRIED_FISH_LOWSTOCK_FILENAME = "FriedFishProcessStateSuccessLowStock.json"
STATEFUL_FRIED_FISH_NOSTOCK_FILENAME = "FriedFishProcessStateSuccessNoStock.json"
STATEFUL_FISH_SANDWICH_PROCESS_FILENAME = "FishSandwichStateProcessSuccess.json"
STATEFUL_FISH_SANDWICH_LOWSTOCK_FILENAME = "FishSandwichStateProcessSuccessLowStock.json"
# endregion
# region Reading/Writing process state from/to file
async def run_and_store_stateful_fried_fish_process_state():
"""
Runs the FriedFish stateful process once, then stores its state to JSON.
"""
kernel = _create_kernel_with_chat_completion()
builder = FriedFishProcess.create_process_with_stateful_steps_v1()
fried_fish_process = builder.build()
print("=== Start run_and_store_stateful_fried_fish_process_state ===")
final_state = await execute_process_with_state(
fried_fish_process, kernel, FriedFishProcess.ProcessEvents.PrepareFriedFish, "Order 1"
)
process_state_metadata = final_state.to_process_state_metadata()
dump_process_state_metadata_locally(process_state_metadata, STATEFUL_FRIED_FISH_PROCESS_FILENAME)
print("=== End run_and_store_stateful_fried_fish_process_state ===\n")
async def run_and_store_stateful_fish_sandwich_process_state():
"""
Runs the FishSandwich stateful process once, then stores its state to JSON.
"""
kernel = _create_kernel_with_chat_completion()
builder = FishSandwichProcess.create_process_with_stateful_steps_v1()
fish_sandwich_process = builder.build()
print("=== Start run_and_store_stateful_fish_sandwich_process_state ===")
final_state = await execute_process_with_state(
fish_sandwich_process, kernel, FishSandwichProcess.ProcessEvents.PrepareFishSandwich, "Order 1"
)
process_state_metadata = final_state.to_process_state_metadata()
dump_process_state_metadata_locally(process_state_metadata, STATEFUL_FISH_SANDWICH_PROCESS_FILENAME)
print("=== End run_and_store_stateful_fish_sandwich_process_state ===\n")
async def run_stateful_fried_fish_process_from_file():
loaded_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FRIED_FISH_PROCESS_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not loaded_metadata:
return
kernel = _create_kernel_with_chat_completion()
builder = FriedFishProcess.create_process_with_stateful_steps_v1()
process_from_file = builder.build(state_metadata=loaded_metadata)
print("=== Start run_stateful_fried_fish_process_from_file ===")
await execute_process_with_state(
process_from_file,
kernel,
FriedFishProcess.ProcessEvents.PrepareFriedFish,
"Using Stored State",
)
print("=== End run_stateful_fried_fish_process_from_file ===\n")
async def run_stateful_fried_fish_process_with_low_stock_from_file():
loaded_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FRIED_FISH_LOWSTOCK_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not loaded_metadata:
return
kernel = _create_kernel_with_chat_completion()
builder = FriedFishProcess.create_process_with_stateful_steps_v1()
process_from_file = builder.build(state_metadata=loaded_metadata)
print("=== Start run_stateful_fried_fish_process_with_low_stock_from_file ===")
await execute_process_with_state(
process_from_file,
kernel,
FriedFishProcess.ProcessEvents.PrepareFriedFish,
"Using Low Stock State",
)
print("=== End run_stateful_fried_fish_process_with_low_stock_from_file ===\n")
async def run_stateful_fried_fish_process_with_no_stock_from_file():
loaded_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FRIED_FISH_NOSTOCK_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not loaded_metadata:
return
kernel = _create_kernel_with_chat_completion()
builder = FriedFishProcess.create_process_with_stateful_steps_v1()
process_from_file = builder.build(state_metadata=loaded_metadata)
print("=== Start run_stateful_fried_fish_process_with_no_stock_from_file ===")
await execute_process_with_state(
process_from_file,
kernel,
FriedFishProcess.ProcessEvents.PrepareFriedFish,
"Using No Stock State",
)
print("=== End run_stateful_fried_fish_process_with_no_stock_from_file ===\n")
async def run_stateful_fish_sandwich_process_from_file():
loaded_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FISH_SANDWICH_PROCESS_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not loaded_metadata:
return
kernel = _create_kernel_with_chat_completion()
builder = FishSandwichProcess.create_process()
process_from_file = builder.build(state_metadata=loaded_metadata)
print("=== Start run_stateful_fish_sandwich_process_from_file ===")
await execute_process_with_state(
process_from_file,
kernel,
FishSandwichProcess.ProcessEvents.PrepareFishSandwich,
"Using Stored State",
)
print("=== End run_stateful_fish_sandwich_process_from_file ===\n")
async def run_stateful_fish_sandwich_process_with_low_stock_from_file():
loaded_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FISH_SANDWICH_LOWSTOCK_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not loaded_metadata:
return
kernel = _create_kernel_with_chat_completion()
builder = FishSandwichProcess.create_process()
process_from_file = builder.build(state_metadata=loaded_metadata)
print("=== Start run_stateful_fish_sandwich_process_with_low_stock_from_file ===")
await execute_process_with_state(
process_from_file,
kernel,
FishSandwichProcess.ProcessEvents.PrepareFishSandwich,
"Using Low Stock State",
)
print("=== End run_stateful_fish_sandwich_process_with_low_stock_from_file ===\n")
async def run_stateful_fried_fish_v2_with_low_stock_v1_state() -> None:
state_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FRIED_FISH_LOWSTOCK_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not state_metadata:
return
kernel = _create_kernel_with_chat_completion()
process = FriedFishProcess.create_process_with_stateful_steps_v2().build(state_metadata=state_metadata)
await execute_process_with_state(
process,
kernel,
FriedFishProcess.ProcessEvents.PrepareFriedFish,
"V2+low-stockV1state",
)
async def run_stateful_fish_sandwich_v2_with_low_stock_v1_state() -> None:
state_metadata = KernelProcessStateMetadata.load_from_file(
json_filename=STATEFUL_FISH_SANDWICH_LOWSTOCK_FILENAME, directory=PROCESS_STATE_DIRECTORY
)
if not state_metadata:
return
kernel = _create_kernel_with_chat_completion()
process = FishSandwichProcess.create_process_with_stateful_steps_v2().build(state_metadata=state_metadata)
await execute_process_with_state(
process,
kernel,
FishSandwichProcess.ProcessEvents.PrepareFishSandwich,
"V2+low-stockV1state",
)
# endregion
async def _make_low_and_no_stock_states() -> None:
"""Create the three 'low/no stock' JSON files expected by the sample."""
# ------------------------------------------------------------------ #
# 1. FriedFish — base stateful process (V1) #
# ------------------------------------------------------------------ #
ff_builder = FriedFishProcess.create_process_with_stateful_steps_v1()
ff_state_meta = ff_builder.build().to_process_state_metadata()
gather_meta = ff_state_meta.steps_state["GatherFriedFishIngredientsWithStockStep"]
gather_meta.state.ingredients_stock = 1
dump_process_state_metadata_locally(ff_state_meta, STATEFUL_FRIED_FISH_LOWSTOCK_FILENAME)
gather_meta.state.ingredients_stock = 0
dump_process_state_metadata_locally(ff_state_meta, STATEFUL_FRIED_FISH_NOSTOCK_FILENAME)
# ------------------------------------------------------------------ #
# 2. FishSandwich — nested FriedFish lowstock #
# ------------------------------------------------------------------ #
fs_builder = FishSandwichProcess.create_process_with_stateful_steps_v1()
fs_state_meta = fs_builder.build().to_process_state_metadata()
nested_ff_meta = fs_state_meta.steps_state["FriedFishWithStatefulStepsProcess"].steps_state[
"GatherFriedFishIngredientsWithStockStep"
]
nested_ff_meta.state.ingredients_stock = 1
dump_process_state_metadata_locally(fs_state_meta, STATEFUL_FISH_SANDWICH_LOWSTOCK_FILENAME)
async def main():
# Uncomment the following line to create the low/no stock states
# await _make_low_and_no_stock_states()
# Show basic usage of "stateless" processes
await use_prepare_fried_fish_process()
await use_prepare_potato_fries_process()
await use_prepare_fish_sandwich_process()
await use_prepare_fish_and_chips_process()
# # Show "stateful" processes, not storing or loading yet
await use_prepare_stateful_fried_fish_process_no_shared_state()
await use_prepare_stateful_fried_fish_process_shared_state()
await use_prepare_stateful_potato_fries_process_shared_state()
# Demonstration of storing then reloading state:
await run_and_store_stateful_fried_fish_process_state()
await run_and_store_stateful_fish_sandwich_process_state()
await run_stateful_fried_fish_process_from_file()
await run_stateful_fried_fish_process_with_low_stock_from_file()
await run_stateful_fried_fish_process_with_no_stock_from_file()
await run_stateful_fish_sandwich_process_from_file()
await run_stateful_fish_sandwich_process_with_low_stock_from_file()
await run_stateful_fried_fish_v2_with_low_stock_v1_state()
await run_stateful_fish_sandwich_v2_with_low_stock_v1_state()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,69 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from samples.getting_started_with_processes.step03.models.food_order_item import FoodItem
from samples.getting_started_with_processes.step03.processes.single_food_item_process import SingleFoodItemProcess
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import OpenAIChatCompletion
from semantic_kernel.kernel import Kernel
from semantic_kernel.processes.kernel_process.kernel_process_event import KernelProcessEvent
from semantic_kernel.processes.local_runtime.local_kernel_process import start
"""
Demonstrate the creation of KernelProcess and eliciting different food related events.
For visual reference of the processes used here check the diagram in:
https://github.com/microsoft/semantic-kernel/tree/main/python/samples/
getting_started_with_processes#step03b_food_ordering
"""
def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(service_id=service_id), overwrite=True)
return kernel
async def use_prepare_food_order_process_single_item(food_item: FoodItem):
kernel = _create_kernel_with_chat_completion("sample")
kernel_process = SingleFoodItemProcess.create_process().build()
async with await start(
kernel_process,
kernel,
KernelProcessEvent(id=SingleFoodItemProcess.ProcessEvents.SingleOrderReceived, data=food_item),
) as running_process:
return running_process
async def use_single_order_fried_fish():
await use_prepare_food_order_process_single_item(FoodItem.FRIED_FISH)
async def use_single_order_potato_fries():
await use_prepare_food_order_process_single_item(FoodItem.POTATO_FRIES)
async def use_single_order_fish_sandwich():
await use_prepare_food_order_process_single_item(FoodItem.FISH_SANDWICH)
async def use_single_order_fish_and_chips():
await use_prepare_food_order_process_single_item(FoodItem.FISH_AND_CHIPS)
async def main():
order_methods = [
(use_single_order_fried_fish, "use_single_order_fried_fish"),
(use_single_order_potato_fries, "use_single_order_potato_fries"),
(use_single_order_fish_sandwich, "use_single_order_fish_sandwich"),
(use_single_order_fish_and_chips, "use_single_order_fish_and_chips"),
]
for method, name in order_methods:
print(f"=== Start '{name}' ===")
await method()
print(f"=== End '{name}' ===\n")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,19 @@
# Copyright (c) Microsoft. All rights reserved.
from samples.getting_started_with_processes.step03.steps.cut_food_step import CutFoodStep
from samples.getting_started_with_processes.step03.steps.cut_food_with_sharpening_step import CutFoodWithSharpeningStep
from samples.getting_started_with_processes.step03.steps.external_step import ExternalStep
from samples.getting_started_with_processes.step03.steps.fry_food_step import FryFoodStep
from samples.getting_started_with_processes.step03.steps.gather_ingredients_step import (
GatherIngredientsStep,
GatherIngredientsWithStockStep,
)
__all__ = [
"ExternalStep",
"CutFoodStep",
"GatherIngredientsStep",
"GatherIngredientsWithStockStep",
"CutFoodWithSharpeningStep",
"FryFoodStep",
]
@@ -0,0 +1,38 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from semantic_kernel.functions import kernel_function
from semantic_kernel.processes.kernel_process import (
KernelProcessStep,
KernelProcessStepContext,
kernel_process_step_metadata,
)
@kernel_process_step_metadata("CutFoodStep.V1")
class CutFoodStep(KernelProcessStep):
class Functions(Enum):
ChopFood = "ChopFood"
SliceFood = "SliceFood"
class OutputEvents(Enum):
ChoppingReady = "ChoppingReady"
SlicingReady = "SlicingReady"
def get_action_string(self, food: str, action: str) -> str:
return f"{food}_{action}"
@kernel_function(name=Functions.ChopFood)
async def chop_food(self, context: KernelProcessStepContext, food_actions: list[str]):
food_to_be_cut = food_actions[0]
food_actions.append(self.get_action_string(food_to_be_cut, "chopped"))
print(f"CUTTING_STEP: Ingredient {food_to_be_cut} has been chopped!")
await context.emit_event(process_event=CutFoodStep.OutputEvents.ChoppingReady, data=food_actions)
@kernel_function(name=Functions.SliceFood)
async def slice_food(self, context: KernelProcessStepContext, food_actions: list[str]):
food_to_be_cut = food_actions[0]
food_actions.append(self.get_action_string(food_to_be_cut, "sliced"))
print(f"CUTTING_STEP: Ingredient {food_to_be_cut} has been sliced!")
await context.emit_event(process_event=CutFoodStep.OutputEvents.SlicingReady, data=food_actions)
@@ -0,0 +1,89 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from pydantic import Field
from semantic_kernel.functions import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.processes.kernel_process import (
KernelProcessStep,
KernelProcessStepContext,
KernelProcessStepState,
kernel_process_step_metadata,
)
class CutFoodWithSharpeningState(KernelBaseModel):
knife_sharpness: int = Field(default=5, alias="KnifeSharpness")
needs_sharpening_limit: int = Field(default=3, alias="NeedsSharpeningLimit")
sharpening_boost: int = Field(default=5, alias="SharpeningBoost")
@kernel_process_step_metadata("CutFoodStep.V2")
class CutFoodWithSharpeningStep(KernelProcessStep[CutFoodWithSharpeningState]):
class Functions(Enum):
ChopFood = "ChopFood"
SliceFood = "SliceFood"
SharpenKnife = "SharpenKnife"
class OutputEvents(Enum):
ChoppingReady = "ChoppingReady"
SlicingReady = "SlicingReady"
KnifeNeedsSharpening = "KnifeNeedsSharpening"
KnifeSharpened = "KnifeSharpened"
state: CutFoodWithSharpeningState | None = None
async def activate(self, state: KernelProcessStepState[CutFoodWithSharpeningState]) -> None:
self.state = state.state
def knife_needs_sharpening(self) -> bool:
return self.state.knife_sharpness == self.state.needs_sharpening_limit
def get_action_string(self, food: str, action: str) -> str:
return f"{food}_{action}"
@kernel_function(name=Functions.ChopFood)
async def chop_food(self, context: KernelProcessStepContext, food_actions: list[str]):
food_to_be_cut = food_actions[0]
if self.knife_needs_sharpening():
print(f"CUTTING_STEP: Dull knife, cannot chop {food_to_be_cut} - needs sharpening.")
await context.emit_event(
process_event=CutFoodWithSharpeningStep.OutputEvents.KnifeNeedsSharpening, data=food_actions
)
return
# Update knife sharpness
self.state.knife_sharpness -= 1
food_actions.append(self.get_action_string(food_to_be_cut, "chopped"))
print(
f"CUTTING_STEP: Ingredient {food_to_be_cut} has been chopped! - knife sharpness: {self.state.knife_sharpness}" # noqa: E501
)
await context.emit_event(process_event=CutFoodWithSharpeningStep.OutputEvents.ChoppingReady, data=food_actions)
@kernel_function(name=Functions.SliceFood)
async def slice_food(self, context: KernelProcessStepContext, food_actions: list[str]):
food_to_be_cut = food_actions[0]
if self.knife_needs_sharpening():
print(f"CUTTING_STEP: Dull knife, cannot slice {food_to_be_cut} - needs sharpening.")
await context.emit_event(
process_event=CutFoodWithSharpeningStep.OutputEvents.KnifeNeedsSharpening, data=food_actions
)
return
# Update knife sharpness
self.state.knife_sharpness -= 1
food_actions.append(self.get_action_string(food_to_be_cut, "sliced"))
print(
f"CUTTING_STEP: Ingredient {food_to_be_cut} has been sliced! - knife sharpness: {self.state.knife_sharpness}" # noqa: E501
)
await context.emit_event(process_event=CutFoodWithSharpeningStep.OutputEvents.SlicingReady, data=food_actions)
@kernel_function(name=Functions.SharpenKnife)
async def sharpen_knife(self, context: KernelProcessStepContext, food_actions: list[str]):
self.state.knife_sharpness += self.state.sharpening_boost
print(f"KNIFE SHARPENED: Knife sharpness is now {self.state.knife_sharpness}!")
await context.emit_event(process_event=CutFoodWithSharpeningStep.OutputEvents.KnifeSharpened, data=food_actions)
@@ -0,0 +1,24 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any
from semantic_kernel.functions import kernel_function
from semantic_kernel.processes.kernel_process import (
KernelProcessEventVisibility,
KernelProcessStep,
KernelProcessStepContext,
)
class ExternalStep(KernelProcessStep):
external_event_name: str
def __init__(self, external_event_name: str):
super().__init__(external_event_name=external_event_name)
@kernel_function()
async def emit_external_event(self, context: KernelProcessStepContext, data: Any):
await context.emit_event(
process_event=self.external_event_name, data=data, visibility=KernelProcessEventVisibility.Public
)
@@ -0,0 +1,46 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from random import Random
from pydantic import Field
from semantic_kernel.functions import kernel_function
from semantic_kernel.processes.kernel_process import (
KernelProcessEventVisibility,
KernelProcessStep,
KernelProcessStepContext,
kernel_process_step_metadata,
)
@kernel_process_step_metadata("FryFoodStep.V1")
class FryFoodStep(KernelProcessStep):
class Functions(Enum):
FryFood = "FryFood"
class OutputEvents(Enum):
FoodRuined = "FoodRuined"
FriedFoodReady = "FriedFoodReady"
random_seed: Random = Field(default_factory=Random)
@kernel_function(name=Functions.FryFood)
async def fry_food(self, context: KernelProcessStepContext, food_actions: list[str]):
food_to_fry = food_actions[0]
fryer_malfunction = self.random_seed.randint(0, 10)
# Oh no! Food got burnt :(
if fryer_malfunction < 5:
food_actions.append(f"{food_to_fry}_frying_failed")
print(f"FRYING_STEP: Ingredient {food_to_fry} got burnt while frying :(")
await context.emit_event(process_event=FryFoodStep.OutputEvents.FoodRuined, data=food_actions)
return
food_actions.append(f"{food_to_fry}_frying_succeeded")
print(f"FRYING_STEP: Ingredient {food_to_fry} is ready!")
await context.emit_event(
process_event=FryFoodStep.OutputEvents.FriedFoodReady,
data=food_actions,
visibility=KernelProcessEventVisibility.Public,
)
@@ -0,0 +1,85 @@
# Copyright (c) Microsoft. All rights reserved.
from enum import Enum
from pydantic import Field
from samples.getting_started_with_processes.step03.models.food_ingredients import FoodIngredients
from semantic_kernel.functions import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.processes.kernel_process import KernelProcessStep, KernelProcessStepContext, KernelProcessStepState
class GatherIngredientsStep(KernelProcessStep):
class Functions(Enum):
GatherIngredients = "GatherIngredients"
class OutputEvents(Enum):
IngredientsGathered = "IngredientsGathered"
IngredientsOutOfStock = "IngredientsOutOfStock"
ingredient: FoodIngredients
def __init__(self, ingredient: FoodIngredients):
super().__init__(ingredient=ingredient)
@kernel_function(name=Functions.GatherIngredients)
async def gather_ingredients(self, context: KernelProcessStepContext, food_actions: list[str]):
ingredient = self.ingredient.to_friendly_string()
updated_food_actions = []
updated_food_actions.extend(food_actions)
if len(updated_food_actions) == 0:
updated_food_actions.append(ingredient)
updated_food_actions.append(f"{ingredient}_gathered")
print(f"GATHER_INGREDIENT: Gathered ingredient {ingredient}")
await context.emit_event(
process_event=GatherIngredientsStep.OutputEvents.IngredientsGathered, data=updated_food_actions
)
class GatherIngredientsState(KernelBaseModel):
ingredients_stock: int = Field(default=5, alias="IngredientsStock")
class GatherIngredientsWithStockStep(KernelProcessStep[GatherIngredientsState]):
class Functions(Enum):
GatherIngredients = "GatherIngredients"
class OutputEvents(Enum):
IngredientsGathered = "IngredientsGathered"
IngredientsOutOfStock = "IngredientsOutOfStock"
ingredient: FoodIngredients
state: GatherIngredientsState | None = None
def __init__(self, ingredient: FoodIngredients):
super().__init__(ingredient=ingredient)
async def activate(self, state: KernelProcessStepState[GatherIngredientsState]) -> None:
self.state = state.state
@kernel_function(name=Functions.GatherIngredients)
async def gather_ingredients(self, context: KernelProcessStepContext, food_actions: list[str]):
ingredient = self.ingredient.to_friendly_string()
updated_food_actions = []
updated_food_actions.extend(food_actions)
if self.state.ingredients_stock == 0:
print(f"GATHER_INGREDIENT: Could not gather {ingredient} - OUT OF STOCK!")
await context.emit_event(
process_event=GatherIngredientsWithStockStep.OutputEvents.IngredientsOutOfStock,
data=updated_food_actions,
)
return
if len(updated_food_actions) == 0:
updated_food_actions.append(ingredient)
updated_food_actions.append(f"{ingredient}_gathered")
self.state.ingredients_stock -= 1
print(f"GATHER_INGREDIENT: Gathered ingredient {ingredient} - remaining: {self.state.ingredients_stock}")
await context.emit_event(
process_event=GatherIngredientsWithStockStep.OutputEvents.IngredientsGathered,
data=updated_food_actions,
)