chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from semantic_kernel.processes.kernel_process.kernel_process import KernelProcess
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_event import (
|
||||
KernelProcessEvent,
|
||||
KernelProcessEventVisibility,
|
||||
)
|
||||
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_metadata import kernel_process_step_metadata
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state_metadata import KernelProcessStateMetadata
|
||||
|
||||
__all__ = [
|
||||
"KernelProcess",
|
||||
"KernelProcessEvent",
|
||||
"KernelProcessEventVisibility",
|
||||
"KernelProcessStateMetadata",
|
||||
"KernelProcessStep",
|
||||
"KernelProcessStepContext",
|
||||
"KernelProcessStepState",
|
||||
"kernel_process_step_metadata",
|
||||
]
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_edge import KernelProcessEdge
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_state import KernelProcessState
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_info import KernelProcessStepInfo
|
||||
from semantic_kernel.processes.process_state_metadata_utils import kernel_process_to_process_state_metadata
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_edge import KernelProcessEdge
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state_metadata import KernelProcessStateMetadata
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcess(KernelProcessStepInfo):
|
||||
"""A kernel process."""
|
||||
|
||||
steps: list[KernelProcessStepInfo] = Field(default_factory=list)
|
||||
factories: dict[str, Callable] = Field(default_factory=dict)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
state: KernelProcessState,
|
||||
steps: list[KernelProcessStepInfo],
|
||||
edges: dict[str, list["KernelProcessEdge"]] | None = None,
|
||||
factories: dict[str, Callable] | None = None,
|
||||
):
|
||||
"""Initialize the kernel process.
|
||||
|
||||
Args:
|
||||
state: The state of the process.
|
||||
steps: The steps of the process.
|
||||
edges: The edges of the process. Defaults to None.
|
||||
factories: The factories of the process. This allows for the creation of
|
||||
steps that require complex dependencies that cannot be JSON serialized or deserialized.
|
||||
"""
|
||||
if not state:
|
||||
raise ValueError("state cannot be None")
|
||||
if not steps:
|
||||
raise ValueError("steps cannot be None")
|
||||
if not state.name:
|
||||
raise ValueError("state.Name cannot be None")
|
||||
|
||||
process_steps = []
|
||||
process_steps.extend(steps)
|
||||
|
||||
args: dict[str, Any] = {
|
||||
"steps": process_steps,
|
||||
"inner_step_type": KernelProcess,
|
||||
"state": state,
|
||||
"output_edges": edges or {},
|
||||
}
|
||||
|
||||
if factories:
|
||||
args["factories"] = factories
|
||||
|
||||
super().__init__(**args)
|
||||
|
||||
def to_process_state_metadata(self) -> "KernelProcessStateMetadata":
|
||||
"""Converts a kernel process to process state metadata."""
|
||||
return kernel_process_to_process_state_metadata(self)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_function_target import KernelProcessFunctionTarget
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessEdge(KernelBaseModel):
|
||||
"""Represents an edge between steps."""
|
||||
|
||||
source_step_id: str
|
||||
output_target: KernelProcessFunctionTarget
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
from pydantic import ConfigDict
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessEventVisibility(Enum):
|
||||
"""Visibility of a kernel process event."""
|
||||
|
||||
# The event is visible inside the process as well as outside the process. This is useful
|
||||
# when the event is intended to be consumed by other processes or external systems.
|
||||
Public = "Public"
|
||||
|
||||
# The event is only visible to steps within the same process.
|
||||
Internal = "Internal"
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessEvent(KernelBaseModel):
|
||||
"""A kernel process event."""
|
||||
|
||||
id: str
|
||||
data: Any | None = None
|
||||
visibility: KernelProcessEventVisibility = KernelProcessEventVisibility.Internal
|
||||
|
||||
model_config = ConfigDict(use_enum_values=False)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessFunctionTarget(KernelBaseModel):
|
||||
"""The target of a function call in a kernel process."""
|
||||
|
||||
step_id: str
|
||||
function_name: str
|
||||
parameter_name: str | None = None
|
||||
target_event_id: str | None = None
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from semantic_kernel.processes.local_runtime.local_event import KernelProcessEvent
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessMessageChannel(ABC):
|
||||
"""Abstract base class for emitting events from a step."""
|
||||
|
||||
@abstractmethod
|
||||
async def emit_event(self, process_event: "KernelProcessEvent") -> None:
|
||||
"""Emits the specified event from the step."""
|
||||
pass
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessState(KernelProcessStepState):
|
||||
"""The state of a kernel process."""
|
||||
|
||||
type: Literal["KernelProcessState"] = Field(default="KernelProcessState") # type: ignore
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from abc import ABC
|
||||
from typing import TYPE_CHECKING, Generic, TypeVar
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
|
||||
|
||||
TState = TypeVar("TState")
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessStep(ABC, KernelBaseModel, Generic[TState]):
|
||||
"""A KernelProcessStep Base class for process steps."""
|
||||
|
||||
state: TState | None = None
|
||||
|
||||
async def activate(self, state: "KernelProcessStepState[TState]"):
|
||||
"""Activates the step and sets the state."""
|
||||
pass # pragma: no cover
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from semantic_kernel.exceptions.process_exceptions import ProcessEventUndefinedException
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_message_channel import KernelProcessMessageChannel
|
||||
from semantic_kernel.processes.local_runtime.local_event import KernelProcessEvent
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessStepContext(KernelBaseModel):
|
||||
"""The context of a step in a kernel process."""
|
||||
|
||||
step_message_channel: KernelProcessMessageChannel
|
||||
|
||||
def __init__(self, channel: KernelProcessMessageChannel):
|
||||
"""Initialize the step context."""
|
||||
super().__init__(step_message_channel=channel) # type: ignore
|
||||
|
||||
async def emit_event(self, process_event: "KernelProcessEvent | str | Enum | None", **kwargs) -> None:
|
||||
"""Emit an event from the current step.
|
||||
|
||||
It is possible to either specify a `KernelProcessEvent` object or the ID of the event
|
||||
along with the `data` and optional `visibility` keyword arguments.
|
||||
|
||||
Args:
|
||||
process_event (KernelProcessEvent | str): The event to emit.
|
||||
**kwargs: Additional keyword arguments to pass to the event.
|
||||
"""
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_event import KernelProcessEvent
|
||||
|
||||
if process_event is None:
|
||||
raise ProcessEventUndefinedException("Process event cannot be None")
|
||||
|
||||
if not isinstance(process_event, KernelProcessEvent):
|
||||
process_event = KernelProcessEvent(
|
||||
id=process_event.value if isinstance(process_event, Enum) else process_event, **kwargs
|
||||
)
|
||||
|
||||
await self.step_message_channel.emit_event(process_event)
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_edge import KernelProcessEdge
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessStepInfo(KernelBaseModel):
|
||||
"""Information about a step in a kernel process."""
|
||||
|
||||
inner_step_type: type
|
||||
state: KernelProcessStepState
|
||||
output_edges: dict[str, list[KernelProcessEdge]]
|
||||
|
||||
@property
|
||||
def edges(self) -> dict[str, list["KernelProcessEdge"]]:
|
||||
"""The edges of the step."""
|
||||
return {k: v.copy() for k, v in self.output_edges.items()}
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
|
||||
@experimental
|
||||
@dataclass(frozen=True)
|
||||
class KernelProcessStepMetadataAttribute:
|
||||
"""Metadata describing the version of the Step's implementation for serialization and replay."""
|
||||
|
||||
version: str = "v1"
|
||||
|
||||
|
||||
@experimental
|
||||
def kernel_process_step_metadata(version: str = "v1"):
|
||||
"""Decorator to attach a version string representing the Step's implementation version.
|
||||
|
||||
This version is serialized in `versionInfo` for each step, enabling replay
|
||||
and process recovery to instantiate the correct Step variant.
|
||||
|
||||
The version string used in @kernel_process_step_metadata must uniquely identify the Step class'
|
||||
behavior and contract version. Different versions imply incompatible step behavior, state schema,
|
||||
or function/event definitions.
|
||||
|
||||
Example usage:
|
||||
@kernel_process_step_metadata("CutFoodStep.V2")
|
||||
class CutFoodWithSharpeningStep(KernelProcessStep[MyState]):
|
||||
...
|
||||
"""
|
||||
|
||||
def decorator(cls):
|
||||
setattr(cls, "_kernel_process_step_metadata", KernelProcessStepMetadataAttribute(version))
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from typing import Generic, Literal, TypeVar
|
||||
|
||||
from pydantic import ConfigDict, Field
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
TState = TypeVar("TState")
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessStepState(KernelBaseModel, Generic[TState]):
|
||||
"""The state of a step in a kernel process."""
|
||||
|
||||
type: Literal["KernelProcessStepState"] = Field(default="KernelProcessStepState") # type: ignore
|
||||
|
||||
name: str
|
||||
version: str
|
||||
id: str | None = None
|
||||
state: TState | None = None
|
||||
|
||||
model_config = ConfigDict(extra="ignore")
|
||||
@@ -0,0 +1,74 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import ClassVar, Generic, Literal, TypeVar
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.utils.feature_stage_decorator import experimental
|
||||
|
||||
TState = TypeVar("TState")
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessStepStateMetadata(KernelBaseModel, Generic[TState]):
|
||||
"""Process state used for State Persistence serialization."""
|
||||
|
||||
type_: Literal["Step", "Process"] = Field(
|
||||
"Step", alias="$type"
|
||||
) # satisfies mypy to have `Steps` and `Process` as type
|
||||
id: str | None = Field(None, alias="id")
|
||||
name: str | None = Field(None, alias="name")
|
||||
version_info: str | None = Field(None, alias="versionInfo")
|
||||
state: TState | None = Field(None, alias="state") # type: ignore[valid-type]
|
||||
|
||||
|
||||
@experimental
|
||||
class KernelProcessStateMetadata(KernelProcessStepStateMetadata[TState]):
|
||||
"""Process state used for State Persistence serialization."""
|
||||
|
||||
type_: Literal["Process"] = Field("Process", alias="$type")
|
||||
steps_state: dict[str, "KernelProcessStateMetadata | KernelProcessStepStateMetadata"] = Field(
|
||||
default_factory=dict, alias="stepsState"
|
||||
)
|
||||
|
||||
model_config: ClassVar = {
|
||||
"populate_by_name": True,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def load_from_file(
|
||||
cls,
|
||||
json_filename: str,
|
||||
directory: str | Path,
|
||||
encoding: str = "utf-8",
|
||||
) -> "KernelProcessStateMetadata | None":
|
||||
"""Loads a KernelProcessStateMetadata instance from a JSON file.
|
||||
|
||||
Args:
|
||||
json_filename (str): Name of the JSON file to load.
|
||||
directory (str | Path): Base directory where the file resides.
|
||||
Can be relative or absolute.
|
||||
encoding (str, optional): Encoding to use when reading the file. Defaults to 'utf-8'.
|
||||
|
||||
Returns:
|
||||
KernelProcessStateMetadata | None: Loaded process state metadata or None on failure.
|
||||
"""
|
||||
base_dir = Path(directory)
|
||||
file_path = base_dir / json_filename
|
||||
|
||||
if not file_path.exists():
|
||||
logger.warning(f"File not found: '{file_path.resolve()}'")
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(file_path, encoding=encoding) as f:
|
||||
file_contents = f.read()
|
||||
return cls.model_validate_json(file_contents)
|
||||
except Exception as ex:
|
||||
logger.error(f"Error reading file '{file_path.resolve()}': {ex}")
|
||||
return None
|
||||
Reference in New Issue
Block a user