diff --git a/python/semantic_kernel/kernel.py b/python/semantic_kernel/kernel.py index bb9c47a72a8d..5ddef255f355 100644 --- a/python/semantic_kernel/kernel.py +++ b/python/semantic_kernel/kernel.py @@ -35,6 +35,7 @@ from semantic_kernel.functions.kernel_plugin import KernelPlugin from semantic_kernel.kernel_types import AI_SERVICE_CLIENT_TYPE, OneOrMany from semantic_kernel.prompt_template.const import KERNEL_TEMPLATE_FORMAT_NAME +from semantic_kernel.reliability.kernel_reliability_extension import KernelReliabilityExtension from semantic_kernel.services.ai_service_selector import AIServiceSelector from semantic_kernel.services.kernel_services_extension import KernelServicesExtension from semantic_kernel.utils.naming import generate_random_ascii_name @@ -51,7 +52,7 @@ logger: logging.Logger = logging.getLogger(__name__) -class Kernel(KernelFilterExtension, KernelFunctionExtension, KernelServicesExtension): +class Kernel(KernelFilterExtension, KernelFunctionExtension, KernelServicesExtension, KernelReliabilityExtension): """The Kernel of Semantic Kernel. This is the main entry point for Semantic Kernel. It provides the ability to run diff --git a/python/semantic_kernel/reliability/__init__.py b/python/semantic_kernel/reliability/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/semantic_kernel/reliability/kernel_reliability_extension.py b/python/semantic_kernel/reliability/kernel_reliability_extension.py new file mode 100644 index 000000000000..9c89766c47db --- /dev/null +++ b/python/semantic_kernel/reliability/kernel_reliability_extension.py @@ -0,0 +1,22 @@ +# Copyright (c) Microsoft. All rights reserved. + +import logging +from abc import ABC + +from pydantic import Field +from typing_extensions import deprecated + +from semantic_kernel.kernel_pydantic import KernelBaseModel +from semantic_kernel.reliability.pass_through_without_retry import PassThroughWithoutRetry +from semantic_kernel.reliability.retry_mechanism_base import RetryMechanismBase + +logger: logging.Logger = logging.getLogger(__name__) + + +class KernelReliabilityExtension(KernelBaseModel, ABC): + """Kernel reliability extension.""" + + retry_mechanism: RetryMechanismBase = Field( + default_factory=PassThroughWithoutRetry, + deprecated=deprecated("retry_mechanism is deprecated; This property doesn't have any effect on the kernel."), + ) diff --git a/python/semantic_kernel/reliability/pass_through_without_retry.py b/python/semantic_kernel/reliability/pass_through_without_retry.py new file mode 100644 index 000000000000..2d675f72de2c --- /dev/null +++ b/python/semantic_kernel/reliability/pass_through_without_retry.py @@ -0,0 +1,31 @@ +# Copyright (c) Microsoft. All rights reserved. + +import logging +from collections.abc import Awaitable, Callable +from typing import TypeVar + +from semantic_kernel.kernel_pydantic import KernelBaseModel +from semantic_kernel.reliability.retry_mechanism_base import RetryMechanismBase + +T = TypeVar("T") + +logger: logging.Logger = logging.getLogger(__name__) + + +class PassThroughWithoutRetry(RetryMechanismBase, KernelBaseModel): + """A retry mechanism that does not retry.""" + + async def execute_with_retry(self, action: Callable[[], Awaitable[T]]) -> Awaitable[T]: + """Executes the given action with retry logic. + + Args: + action (Callable[[], Awaitable[T]]): The action to retry on exception. + + Returns: + Awaitable[T]: An awaitable that will return the result of the action. + """ + try: + return action() + except Exception as e: + logger.warning(e, "Error executing action, not retrying") + raise e diff --git a/python/semantic_kernel/reliability/retry_mechanism_base.py b/python/semantic_kernel/reliability/retry_mechanism_base.py new file mode 100644 index 000000000000..49fe20d91c81 --- /dev/null +++ b/python/semantic_kernel/reliability/retry_mechanism_base.py @@ -0,0 +1,26 @@ +# Copyright (c) Microsoft. All rights reserved. + +import logging +from abc import ABC, abstractmethod +from collections.abc import Awaitable, Callable +from typing import TypeVar + +T = TypeVar("T") + +logger: logging.Logger = logging.getLogger(__name__) + + +class RetryMechanismBase(ABC): + """Base class for retry mechanisms.""" + + @abstractmethod + async def execute_with_retry(self, action: Callable[[], Awaitable[T]]) -> Awaitable[T]: + """Executes the given action with retry logic. + + Args: + action (Callable[[], Awaitable[T]]): The action to retry on exception. + + Returns: + Awaitable[T]: An awaitable that will return the result of the action. + """ + pass diff --git a/python/tests/unit/kernel/test_kernel.py b/python/tests/unit/kernel/test_kernel.py index ea9821ae63e0..38b1608d150f 100644 --- a/python/tests/unit/kernel/test_kernel.py +++ b/python/tests/unit/kernel/test_kernel.py @@ -47,6 +47,7 @@ def test_init(): assert kernel.ai_service_selector is not None assert kernel.plugins is not None assert kernel.services is not None + assert kernel.retry_mechanism is not None assert kernel.function_invocation_filters is not None assert kernel.prompt_rendering_filters is not None