Remove kernel builder pattern for Python: sk.kernel_create() -> sk.Kernel() (#454)

### Motivation and Context

This PR makes creating a kernel instance more pythonic. The builder
pattern is removed and the kernel is constructed using Kernel(...).

Work originally started with #137

### Description

- Removed kernel_builder.py.
- Instance creation pattern `kernel = sk.create_kernel() -> kernel =
sk.Kernel()`
- For notebook 6, there is no need to create a separate kernel for the
GitHub files. Changed to use a separate collection.


Co-authored-by: Devis Lucato <[email protected]>
This commit is contained in:
Abby Harrison
2023-04-17 11:07:46 -07:00
committed by GitHub
co-authored by Devis Lucato
parent f06a8453ff
commit d9323b573f
23 changed files with 86 additions and 222 deletions
+1 -1
View File
@@ -98,7 +98,7 @@ You should be able to run the example under the [tests](tests) folder.
## Pipeline checks
To run the same checks that run during the GitHub Action build, you can use
this command, from the `python` folder:
this command, from the [python](../python) folder:
poetry run pre-commit run -c .conf/.pre-commit-config.yaml -a
+1 -1
View File
@@ -29,7 +29,7 @@ AZURE_OPENAI_API_KEY=""
import semantic_kernel as sk
from semantic_kernel.ai.open_ai import OpenAITextCompletion, AzureTextCompletion
kernel = sk.create_kernel()
kernel = sk.Kernel()
# Prepare OpenAI backend using credentials stored in the `.env` file
api_key, org_id = sk.openai_settings_from_dot_env()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "semantic-kernel"
version = "0.2.2.dev"
version = "0.2.3.dev"
description = ""
authors = ["Microsoft <[email protected]>"]
readme = "README.md"
+3 -12
View File
@@ -2,9 +2,7 @@
from semantic_kernel import core_skills, memory
from semantic_kernel.kernel import Kernel
from semantic_kernel.kernel_builder import KernelBuilder
from semantic_kernel.kernel_config import KernelConfig
from semantic_kernel.memory.null_memory import NullMemory
from semantic_kernel.orchestration.context_variables import ContextVariables
from semantic_kernel.orchestration.sk_context import SKContext
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
@@ -22,17 +20,10 @@ from semantic_kernel.utils.settings import (
openai_settings_from_dot_env,
)
def create_kernel() -> Kernel:
return KernelBuilder.create_kernel()
def kernel_builder() -> KernelBuilder:
return KernelBuilder(KernelConfig(), NullMemory(), NullLogger())
__all__ = [
"create_kernel",
"Kernel",
"KernelConfig",
"NullLogger",
"openai_settings_from_dot_env",
"azure_openai_settings_from_dot_env",
"PromptTemplateConfig",
+23 -10
View File
@@ -13,6 +13,8 @@ from semantic_kernel.kernel_base import KernelBase
from semantic_kernel.kernel_config import KernelConfig
from semantic_kernel.kernel_exception import KernelException
from semantic_kernel.kernel_extensions import KernelExtensions
from semantic_kernel.memory.memory_store_base import MemoryStoreBase
from semantic_kernel.memory.null_memory import NullMemory
from semantic_kernel.memory.semantic_text_memory_base import SemanticTextMemoryBase
from semantic_kernel.orchestration.context_variables import ContextVariables
from semantic_kernel.orchestration.sk_context import SKContext
@@ -26,9 +28,11 @@ from semantic_kernel.skill_definition.read_only_skill_collection_base import (
)
from semantic_kernel.skill_definition.skill_collection import SkillCollection
from semantic_kernel.skill_definition.skill_collection_base import SkillCollectionBase
from semantic_kernel.template_engine.prompt_template_engine import PromptTemplateEngine
from semantic_kernel.template_engine.protocols.prompt_templating_engine import (
PromptTemplatingEngine,
)
from semantic_kernel.utils.null_logger import NullLogger
from semantic_kernel.utils.validation import validate_function_name, validate_skill_name
@@ -41,17 +45,23 @@ class Kernel(KernelBase, KernelExtensions):
def __init__(
self,
skill_collection: SkillCollectionBase,
prompt_template_engine: PromptTemplatingEngine,
memory: SemanticTextMemoryBase,
config: KernelConfig,
log: Logger,
skill_collection: Optional[SkillCollectionBase] = None,
prompt_template_engine: Optional[PromptTemplatingEngine] = None,
memory: Optional[SemanticTextMemoryBase] = None,
config: Optional[KernelConfig] = None,
log: Optional[Logger] = None,
) -> None:
self._log = log
self._config = config
self._skill_collection = skill_collection
self._prompt_template_engine = prompt_template_engine
self._memory = memory
self._log = log if log else NullLogger()
self._config = config if config else KernelConfig()
self._skill_collection = (
skill_collection if skill_collection else SkillCollection(self._log)
)
self._prompt_template_engine = (
prompt_template_engine
if prompt_template_engine
else PromptTemplateEngine(self._log)
)
self._memory = memory if memory else NullMemory()
def kernel(self) -> KernelBase:
return self
@@ -158,6 +168,9 @@ class Kernel(KernelBase, KernelExtensions):
def register_memory(self, memory: SemanticTextMemoryBase) -> None:
self._memory = memory
def register_memory_store(self, memory_store: MemoryStoreBase) -> None:
self.use_memory(memory_store)
def create_new_context(self) -> SKContext:
return SKContext(
ContextVariables(),
-92
View File
@@ -1,92 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from logging import Logger
from typing import Callable, Optional
from semantic_kernel.kernel import Kernel
from semantic_kernel.kernel_config import KernelConfig
from semantic_kernel.kernel_extensions import KernelExtensions
from semantic_kernel.memory.memory_store_base import MemoryStoreBase
from semantic_kernel.memory.null_memory import NullMemory
from semantic_kernel.memory.semantic_text_memory_base import SemanticTextMemoryBase
from semantic_kernel.skill_definition.skill_collection import SkillCollection
from semantic_kernel.template_engine.prompt_template_engine import PromptTemplateEngine
from semantic_kernel.utils.null_logger import NullLogger
class KernelBuilder:
_config: KernelConfig
_memory: SemanticTextMemoryBase
_memory_storage: Optional[MemoryStoreBase]
_log: Logger
def __init__(
self, config: KernelConfig, memory: SemanticTextMemoryBase, log: Logger
) -> None:
self._config = config
self._memory = memory
self._memory_storage = None
self._log = log
def with_configuration(self, config: KernelConfig) -> "KernelBuilder":
if config is None:
raise ValueError("The configuration instance cannot be `None`")
self._config = config
return self
def with_memory(self, memory: SemanticTextMemoryBase) -> "KernelBuilder":
if memory is None:
raise ValueError("The memory instance cannot be `None`")
self._memory = memory
return self
def with_memory_storage(self, storage: MemoryStoreBase) -> "KernelBuilder":
if storage is None:
raise ValueError("The memory storage instance cannot be `None`")
self._memory_storage = storage
return self
def with_logger(self, log: Logger) -> "KernelBuilder":
if log is None:
raise ValueError("The logger instance cannot be `None`")
self._log = log
return self
def configure(
self, config_func: Callable[[KernelConfig], KernelConfig]
) -> "KernelBuilder":
self._config = config_func(self._config)
return self
def build(self) -> Kernel:
instance = Kernel(
SkillCollection(self._log),
PromptTemplateEngine(self._log),
self._memory,
self._config,
self._log,
)
if self._memory_storage is not None:
KernelExtensions.use_memory(instance, self._memory_storage)
return instance
@staticmethod
def create_kernel(
config: Optional[KernelConfig] = None,
log: Optional[Logger] = None,
memory: Optional[SemanticTextMemoryBase] = None,
) -> Kernel:
builder = KernelBuilder(KernelConfig(), NullMemory(), NullLogger())
if config is not None:
builder = builder.with_configuration(config)
if log is not None:
builder = builder.with_logger(log)
if memory is not None:
builder = builder.with_memory(memory)
return builder.build()
+1 -1
View File
@@ -5,7 +5,7 @@ import asyncio
import semantic_kernel as sk
import semantic_kernel.ai.open_ai as sk_oai
kernel = sk.create_kernel()
kernel = sk.Kernel()
# Load credentials from .env file
api_key, org_id = sk.openai_settings_from_dot_env()
+1 -1
View File
@@ -15,7 +15,7 @@ User:> {{$user_input}}
ChatBot:>
"""
kernel = sk.create_kernel()
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.config.add_text_backend(
+1 -1
View File
@@ -14,7 +14,7 @@ effectively, but you tend to answer with long
flowery prose.
"""
kernel = sk.create_kernel()
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.config.add_chat_backend(
+12 -28
View File
@@ -7,33 +7,6 @@ import semantic_kernel as sk
import semantic_kernel.ai.open_ai as sk_oai
def build_kernel() -> sk.Kernel:
# Setup kernel with OpenAI completion and embedding backends
api_key, org_id = sk.openai_settings_from_dot_env()
kernel = (
sk.kernel_builder()
.configure(
lambda c: c.add_text_backend(
"davinci-003",
sk_oai.OpenAITextCompletion("text-davinci-003", api_key, org_id),
)
)
.configure(
lambda c: c.add_embedding_backend(
"ada-002",
sk_oai.OpenAITextEmbedding("text-embedding-ada-002", api_key, org_id),
)
)
.with_memory_storage(sk.memory.VolatileMemoryStore())
.build()
)
kernel.import_skill(sk.core_skills.TextMemorySkill())
return kernel
async def populate_memory(kernel: sk.Kernel) -> None:
# Add some documents to the semantic memory
await kernel.memory.save_information_async(
@@ -132,7 +105,18 @@ async def chat(
async def main() -> None:
kernel = build_kernel()
kernel = sk.Kernel()
api_key, org_id = sk.openai_settings_from_dot_env()
kernel.config.add_text_backend(
"dv", sk_oai.OpenAITextCompletion("text-davinci-003", api_key, org_id)
)
kernel.config.add_embedding_backend(
"ada", sk_oai.OpenAITextEmbedding("text-embedding-ada-002", api_key, org_id)
)
kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())
kernel.import_skill(sk.core_skills.TextMemorySkill())
print("Populating memory...")
await populate_memory(kernel)
+1 -1
View File
@@ -6,7 +6,7 @@ import os
import semantic_kernel as sk
import semantic_kernel.ai.open_ai as sk_oai
kernel = sk.create_kernel()
kernel = sk.Kernel()
useAzureOpenAI = False
model = "text-davinci-002"
@@ -5,7 +5,7 @@ from typing import List, Tuple
from pytest import mark, raises
import semantic_kernel as sk
from semantic_kernel import Kernel
from semantic_kernel.skill_definition import sk_function
from semantic_kernel.template_engine.prompt_template_engine import PromptTemplateEngine
@@ -54,7 +54,7 @@ class TestPromptTemplateEngine:
winner = "SK"
template = "And the winner\n of {{$input}} \nis: {{ $winner }}!"
kernel = sk.create_kernel()
kernel = Kernel()
context = kernel.create_new_context()
context["input"] = input
context["winner"] = winner
@@ -74,7 +74,7 @@ class TestPromptTemplateEngine:
template = "And the winner\n of {{'template\ntests'}} \nis: {{ \"SK\" }}!"
expected = "And the winner\n of template\ntests \nis: SK!"
kernel = sk.create_kernel()
kernel = Kernel()
context = kernel.create_new_context()
# Act
@@ -87,7 +87,7 @@ class TestPromptTemplateEngine:
async def test_it_allows_to_pass_variables_to_functions_async(self):
# Arrange
template = "== {{my.check123 $call}} =="
kernel = sk.create_kernel()
kernel = Kernel()
kernel.import_skill(MySkill(), "my")
context = kernel.create_new_context()
context["call"] = "123"
@@ -102,7 +102,7 @@ class TestPromptTemplateEngine:
async def test_it_allows_to_pass_values_to_functions_async(self):
# Arrange
template = "== {{my.check123 '234'}} =="
kernel = sk.create_kernel()
kernel = Kernel()
kernel.import_skill(MySkill(), "my")
context = kernel.create_new_context()
@@ -116,7 +116,7 @@ class TestPromptTemplateEngine:
async def test_it_allows_to_pass_escaped_values1_to_functions_async(self):
# Arrange
template = "== {{my.check123 'a\\'b'}} =="
kernel = sk.create_kernel()
kernel = Kernel()
kernel.import_skill(MySkill(), "my")
context = kernel.create_new_context()
@@ -130,7 +130,7 @@ class TestPromptTemplateEngine:
async def test_it_allows_to_pass_escaped_values2_to_functions_async(self):
# Arrange
template = '== {{my.check123 "a\\"b"}} =='
kernel = sk.create_kernel()
kernel = Kernel()
kernel.import_skill(MySkill(), "my")
context = kernel.create_new_context()
@@ -148,7 +148,7 @@ class TestPromptTemplateEngine:
self, template: str, expected_result: str
):
# Arrange
kernel = sk.create_kernel()
kernel = Kernel()
kernel.import_skill(MySkill())
context = kernel.create_new_context()
@@ -3,7 +3,7 @@ import tempfile
import pytest
import semantic_kernel as sk
from semantic_kernel import Kernel
from semantic_kernel.core_skills.file_io_skill import FileIOSkill
from semantic_kernel.orchestration.context_variables import ContextVariables
from semantic_kernel.orchestration.sk_context import SKContext
@@ -15,7 +15,7 @@ def test_can_be_instantiated():
def test_can_be_imported():
kernel = sk.create_kernel()
kernel = Kernel()
assert kernel.import_skill(FileIOSkill(), "file")
assert kernel.skills.has_native_function("file", "readAsync")
@@ -8,13 +8,13 @@ def test_can_be_instantiated():
def test_can_be_imported():
kernel = sk.create_kernel()
kernel = sk.Kernel()
assert kernel.import_skill(TextSkill())
assert kernel.skills.has_native_function(SkillCollection.GLOBAL_SKILL, "trim")
def test_can_be_imported_with_name():
kernel = sk.create_kernel()
kernel = sk.Kernel()
assert kernel.import_skill(TextSkill(), "text")
assert kernel.skills.has_native_function("text", "trim")
@@ -12,7 +12,7 @@ def test_can_be_instantiated():
def test_can_be_imported():
kernel = sk.create_kernel()
kernel = sk.Kernel()
assert kernel.import_skill(TimeSkill(), "time")
assert kernel.skills.has_native_function("time", "now")
@@ -8,7 +8,7 @@ import semantic_kernel.ai.open_ai as sk_oai
def test_skill_can_be_imported():
# create a kernel
kernel = sk.create_kernel()
kernel = sk.Kernel()
api_key = "test-api-key"
org_id = "test-org-id"
kernel.config.add_text_backend(
@@ -23,7 +23,7 @@
"\n",
"import semantic_kernel as sk\n",
"\n",
"kernel = sk.create_kernel()"
"kernel = sk.Kernel()"
]
},
{
@@ -126,7 +126,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
"version": "3.10.10"
},
"orig_nbformat": 4
},
@@ -51,7 +51,7 @@
"outputs": [],
"source": [
"# Simple instance\n",
"kernel_1 = sk.create_kernel()"
"kernel_1 = sk.Kernel()"
]
},
{
@@ -62,11 +62,7 @@
"source": [
"# Instance with a custom logger\n",
"my_logger = sk.NullLogger()\n",
"kernel_2 = (\n",
" sk.kernel_builder()\n",
" .with_logger(my_logger)\n",
" .build()\n",
")"
"kernel_2 = sk.Kernel(log=my_logger)"
]
},
{
@@ -77,11 +73,7 @@
"source": [
"# Instance with a custom configuration\n",
"my_config = sk.KernelConfig()\n",
"kernel_2 = (\n",
" sk.kernel_builder()\n",
" .with_configuration(my_config)\n",
" .build()\n",
")"
"kernel_2 = sk.Kernel(config=my_config)"
]
},
{
@@ -101,7 +93,7 @@
"metadata": {},
"outputs": [],
"source": [
"kernel = sk.create_kernel()\n",
"kernel = sk.Kernel()\n",
"\n",
"kernel.config.add_text_backend( # We are adding a text backend\n",
" \"Azure_curie\", # The alias we can use in prompt templates' config.json\n",
@@ -169,7 +161,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
"version": "3.10.10"
},
"polyglot_notebook": {
"kernelInfo": {
@@ -97,7 +97,7 @@
"import semantic_kernel as sk\n",
"from semantic_kernel.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n",
"\n",
"kernel = sk.create_kernel()\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"\n",
@@ -182,7 +182,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
"version": "3.10.10"
}
},
"nbformat": 4,
@@ -59,7 +59,7 @@
"import semantic_kernel as sk\n",
"from semantic_kernel.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n",
"\n",
"kernel = sk.create_kernel()\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"\n",
@@ -204,7 +204,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
"version": "3.10.10"
}
},
"nbformat": 4,
@@ -38,7 +38,7 @@
"import semantic_kernel as sk\n",
"from semantic_kernel.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n",
"\n",
"kernel = sk.create_kernel()\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"\n",
@@ -238,7 +238,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
"version": "3.10.10"
}
},
"nbformat": 4,
@@ -34,7 +34,7 @@
"import semantic_kernel as sk\n",
"from semantic_kernel.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n",
"\n",
"kernel = sk.create_kernel()\n",
"kernel = sk.Kernel()\n",
"\n",
"useAzureOpenAI = False\n",
"\n",
@@ -62,21 +62,16 @@
"metadata": {},
"outputs": [],
"source": [
"def build_kernel() -> sk.Kernel:\n",
" # Setup kernel with OpenAI completion and embedding backends\n",
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
"from semantic_kernel.kernel_config import KernelConfig\n",
"\n",
" kernel = (\n",
" sk.kernel_builder()\n",
" .configure(lambda c: c.add_text_backend(\"completion\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id)))\n",
" .configure(lambda c: c.add_embedding_backend(\"embeddings\", OpenAITextEmbedding(\"text-embedding-ada-002\", api_key, org_id)))\n",
" .with_memory_storage(sk.memory.VolatileMemoryStore())\n",
" .build()\n",
" )\n",
"kernel = sk.Kernel()\n",
"\n",
" kernel.import_skill(sk.core_skills.TextMemorySkill())\n",
"api_key, org_id = sk.openai_settings_from_dot_env()\n",
"kernel.config.add_text_backend(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))\n",
"kernel.config.add_embedding_backend(\"ada\", OpenAITextEmbedding(\"text-embedding-ada-002\", api_key, org_id))\n",
"\n",
" return kernel"
"kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())\n",
"kernel.import_skill(sk.core_skills.TextMemorySkill())"
]
},
{
@@ -274,8 +269,6 @@
"metadata": {},
"outputs": [],
"source": [
"kernel = build_kernel()\n",
"\n",
"print(\"Populating memory...\")\n",
"await populate_memory(kernel)\n",
"\n",
@@ -311,10 +304,9 @@
"metadata": {},
"outputs": [],
"source": [
"memory_collection_name = \"SKGitHub\"\n",
"\n",
"github_files ={}\n",
"github_files[\"https://github.com/microsoft/semantic-kernel/blob/main/README.md\"] = \"README: Installation, getting started, and how to contribute\"\n",
"github_files[\"https://github.com/microsoft/semantic-kernel/blob/main/README.md\"] = \\\n",
" \"README: Installation, getting started, and how to contribute\"\n",
"github_files[\"https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/02-running-prompts-from-file.ipynb\"] = \\\n",
" \"Jupyter notebook describing how to pass prompts from a file to a semantic skill or function\"\n",
"github_files[\"https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/00-getting-started.ipynb\"] = \\\n",
@@ -330,29 +322,12 @@
]
},
{
"cell_type": "markdown",
"id": "a4aa0e5d",
"metadata": {},
"source": [
"Let's build a new Kernel."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f6518515",
"metadata": {},
"outputs": [],
"source": [
"kernel = build_kernel()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "75f3ea5e",
"metadata": {},
"source": [
"Now let's add these files to our VolatileMemoryStore using `SaveReferenceAsync`."
"Now let's add these files to our VolatileMemoryStore using `SaveReferenceAsync`. We'll separate these memories from the chat memories by putting them in a different collection."
]
},
{
@@ -362,6 +337,7 @@
"metadata": {},
"outputs": [],
"source": [
"memory_collection_name = \"SKGitHub\"\n",
"print(\"Adding some GitHub file URLs and their descriptions to a volatile Semantic Memory.\");\n",
"i = 0\n",
"for entry, value in github_files.items():\n",
@@ -424,7 +400,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
"version": "3.10.10"
}
},
"nbformat": 4,