chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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"]
|
||||
+107
@@ -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
|
||||
+129
@@ -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
|
||||
+83
@@ -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
|
||||
+122
@@ -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
|
||||
+60
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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. Fried‑Fish — 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. Fish‑Sandwich — nested Fried‑Fish low‑stock #
|
||||
# ------------------------------------------------------------------ #
|
||||
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)
|
||||
+89
@@ -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,
|
||||
)
|
||||
Reference in New Issue
Block a user