Skip to content

Commit 656d4c4

Browse files
mkarleshawncal
andauthored
Merge KernelConfig with Kernel (microsoft#731)
### Motivation and Context KernelConfig isn't needed as it's less pythonic and is not used independently from Kernel. This moves all its methods and members into Kernel. ### Description - Removed KernelConfig - Put KernelConfig's methods and variables into Kernel - Ran unit and end-to-end tests - Did not run notebooks because they are out of date with the current repo. --------- Co-authored-by: Shawn Callegari <[email protected]>
1 parent 73c1dd2 commit 656d4c4

39 files changed

+486
-347
lines changed

python/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ kernel = sk.Kernel()
3333

3434
# Prepare OpenAI service using credentials stored in the `.env` file
3535
api_key, org_id = sk.openai_settings_from_dot_env()
36-
kernel.config.add_text_completion_service("dv", OpenAITextCompletion("text-davinci-003", api_key, org_id))
36+
kernel.add_text_completion_service("dv", OpenAITextCompletion("text-davinci-003", api_key, org_id))
3737

3838
# Alternative using Azure:
3939
# deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
40-
# kernel.config.add_text_completion_service("dv", AzureTextCompletion(deployment, endpoint, api_key))
40+
# kernel.add_text_completion_service("dv", AzureTextCompletion(deployment, endpoint, api_key))
4141

4242
# Wrap your prompt in a function
4343
prompt = kernel.create_semantic_function("""

python/semantic_kernel/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from semantic_kernel import core_skills, memory
44
from semantic_kernel.kernel import Kernel
5-
from semantic_kernel.kernel_config import KernelConfig
65
from semantic_kernel.orchestration.context_variables import ContextVariables
76
from semantic_kernel.orchestration.sk_context import SKContext
87
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
@@ -22,7 +21,6 @@
2221

2322
__all__ = [
2423
"Kernel",
25-
"KernelConfig",
2624
"NullLogger",
2725
"openai_settings_from_dot_env",
2826
"azure_openai_settings_from_dot_env",

python/semantic_kernel/kernel.py

Lines changed: 256 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import inspect
44
from logging import Logger
5-
from typing import Any, Dict, Optional
5+
from typing import Any, Callable, Dict, List, Optional, Type, TypeVar, Union
66

77
from semantic_kernel.connectors.ai.ai_exception import AIException
88
from semantic_kernel.connectors.ai.chat_completion_client_base import (
@@ -12,11 +12,13 @@
1212
from semantic_kernel.connectors.ai.complete_request_settings import (
1313
CompleteRequestSettings,
1414
)
15+
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import (
16+
EmbeddingGeneratorBase,
17+
)
1518
from semantic_kernel.connectors.ai.text_completion_client_base import (
1619
TextCompletionClientBase,
1720
)
1821
from semantic_kernel.kernel_base import KernelBase
19-
from semantic_kernel.kernel_config import KernelConfig
2022
from semantic_kernel.kernel_exception import KernelException
2123
from semantic_kernel.kernel_extensions import KernelExtensions
2224
from semantic_kernel.memory.memory_store_base import MemoryStoreBase
@@ -26,6 +28,10 @@
2628
from semantic_kernel.orchestration.sk_context import SKContext
2729
from semantic_kernel.orchestration.sk_function import SKFunction
2830
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
31+
from semantic_kernel.reliability.pass_through_without_retry import (
32+
PassThroughWithoutRetry,
33+
)
34+
from semantic_kernel.reliability.retry_mechanism import RetryMechanism
2935
from semantic_kernel.semantic_functions.semantic_function_config import (
3036
SemanticFunctionConfig,
3137
)
@@ -41,10 +47,10 @@
4147
from semantic_kernel.utils.null_logger import NullLogger
4248
from semantic_kernel.utils.validation import validate_function_name, validate_skill_name
4349

50+
T = TypeVar("T")
4451

4552
class Kernel(KernelBase, KernelExtensions):
4653
_log: Logger
47-
_config: KernelConfig
4854
_skill_collection: SkillCollectionBase
4955
_prompt_template_engine: PromptTemplatingEngine
5056
_memory: SemanticTextMemoryBase
@@ -54,11 +60,9 @@ def __init__(
5460
skill_collection: Optional[SkillCollectionBase] = None,
5561
prompt_template_engine: Optional[PromptTemplatingEngine] = None,
5662
memory: Optional[SemanticTextMemoryBase] = None,
57-
config: Optional[KernelConfig] = None,
5863
log: Optional[Logger] = None,
5964
) -> None:
6065
self._log = log if log else NullLogger()
61-
self._config = config if config else KernelConfig()
6266
self._skill_collection = (
6367
skill_collection if skill_collection else SkillCollection(self._log)
6468
)
@@ -69,13 +73,25 @@ def __init__(
6973
)
7074
self._memory = memory if memory else NullMemory()
7175

76+
self._text_completion_services: Dict[
77+
str, Callable[["KernelBase"], TextCompletionClientBase]
78+
] = {}
79+
self._chat_services: Dict[
80+
str, Callable[["KernelBase"], ChatCompletionClientBase]
81+
] = {}
82+
self._text_embedding_generation_services: Dict[
83+
str, Callable[["KernelBase"], EmbeddingGeneratorBase]
84+
] = {}
85+
86+
self._default_text_completion_service: Optional[str] = None
87+
self._default_chat_service: Optional[str] = None
88+
self._default_text_embedding_generation_service: Optional[str] = None
89+
90+
self._retry_mechanism: RetryMechanism = PassThroughWithoutRetry()
91+
7292
def kernel(self) -> KernelBase:
7393
return self
7494

75-
@property
76-
def config(self) -> KernelConfig:
77-
return self._config
78-
7995
@property
8096
def logger(self) -> Logger:
8197
return self._log
@@ -248,6 +264,235 @@ def import_skill(
248264

249265
return skill
250266

267+
def get_ai_service(
268+
self, type: Type[T], service_id: Optional[str] = None
269+
) -> Callable[["KernelBase"], T]:
270+
matching_type = {}
271+
if type == TextCompletionClientBase:
272+
service_id = service_id or self._default_text_completion_service
273+
matching_type = self._text_completion_services
274+
elif type == ChatCompletionClientBase:
275+
service_id = service_id or self._default_chat_service
276+
matching_type = self._chat_services
277+
elif type == EmbeddingGeneratorBase:
278+
service_id = service_id or self._default_text_embedding_generation_service
279+
matching_type = self._text_embedding_generation_services
280+
else:
281+
raise ValueError(f"Unknown AI service type: {type.__name__}")
282+
283+
if service_id not in matching_type:
284+
raise ValueError(
285+
f"{type.__name__} service with service_id '{service_id}' not found"
286+
)
287+
288+
return matching_type[service_id]
289+
290+
def all_text_completion_services(self) -> List[str]:
291+
return list(self._text_completion_services.keys())
292+
293+
def all_chat_services(self) -> List[str]:
294+
return list(self._chat_services.keys())
295+
296+
def all_text_embedding_generation_services(self) -> List[str]:
297+
return list(self._text_embedding_generation_services.keys())
298+
299+
def add_text_completion_service(
300+
self,
301+
service_id: str,
302+
service: Union[
303+
TextCompletionClientBase, Callable[["KernelBase"], TextCompletionClientBase]
304+
],
305+
overwrite: bool = True,
306+
) -> "Kernel":
307+
if not service_id:
308+
raise ValueError("service_id must be a non-empty string")
309+
if not overwrite and service_id in self._text_completion_services:
310+
raise ValueError(
311+
f"Text service with service_id '{service_id}' already exists"
312+
)
313+
314+
self._text_completion_services[service_id] = (
315+
service if isinstance(service, Callable) else lambda _: service
316+
)
317+
if self._default_text_completion_service is None:
318+
self._default_text_completion_service = service_id
319+
320+
return self
321+
322+
def add_chat_service(
323+
self,
324+
service_id: str,
325+
service: Union[
326+
ChatCompletionClientBase, Callable[["KernelBase"], ChatCompletionClientBase]
327+
],
328+
overwrite: bool = True,
329+
) -> "Kernel":
330+
if not service_id:
331+
raise ValueError("service_id must be a non-empty string")
332+
if not overwrite and service_id in self._chat_services:
333+
raise ValueError(
334+
f"Chat service with service_id '{service_id}' already exists"
335+
)
336+
337+
self._chat_services[service_id] = (
338+
service if isinstance(service, Callable) else lambda _: service
339+
)
340+
if self._default_chat_service is None:
341+
self._default_chat_service = service_id
342+
343+
if isinstance(service, TextCompletionClientBase):
344+
self.add_text_completion_service(service_id, service)
345+
if self._default_text_completion_service is None:
346+
self._default_text_completion_service = service_id
347+
348+
return self
349+
350+
def add_text_embedding_generation_service(
351+
self,
352+
service_id: str,
353+
service: Union[
354+
EmbeddingGeneratorBase, Callable[["KernelBase"], EmbeddingGeneratorBase]
355+
],
356+
overwrite: bool = False,
357+
) -> "Kernel":
358+
if not service_id:
359+
raise ValueError("service_id must be a non-empty string")
360+
if not overwrite and service_id in self._text_embedding_generation_services:
361+
raise ValueError(
362+
f"Embedding service with service_id '{service_id}' already exists"
363+
)
364+
365+
self._text_embedding_generation_services[service_id] = (
366+
service if isinstance(service, Callable) else lambda _: service
367+
)
368+
if self._default_text_embedding_generation_service is None:
369+
self._default_text_embedding_generation_service = service_id
370+
371+
return self
372+
373+
def set_default_text_completion_service(self, service_id: str) -> "Kernel":
374+
if service_id not in self._text_completion_services:
375+
raise ValueError(
376+
f"AI service with service_id '{service_id}' does not exist"
377+
)
378+
379+
self._default_text_completion_service = service_id
380+
return self
381+
382+
def set_default_chat_service(self, service_id: str) -> "Kernel":
383+
if service_id not in self._chat_services:
384+
raise ValueError(
385+
f"AI service with service_id '{service_id}' does not exist"
386+
)
387+
388+
self._default_chat_service = service_id
389+
return self
390+
391+
def set_default_text_embedding_generation_service(
392+
self, service_id: str
393+
) -> "Kernel":
394+
if service_id not in self._text_embedding_generation_services:
395+
raise ValueError(
396+
f"AI service with service_id '{service_id}' does not exist"
397+
)
398+
399+
self._default_text_embedding_generation_service = service_id
400+
return self
401+
402+
def get_text_completion_service_service_id(
403+
self, service_id: Optional[str] = None
404+
) -> str:
405+
if service_id is None or service_id not in self._text_completion_services:
406+
if self._default_text_completion_service is None:
407+
raise ValueError("No default text service is set")
408+
return self._default_text_completion_service
409+
410+
return service_id
411+
412+
def get_chat_service_service_id(self, service_id: Optional[str] = None) -> str:
413+
if service_id is None or service_id not in self._chat_services:
414+
if self._default_chat_service is None:
415+
raise ValueError("No default chat service is set")
416+
return self._default_chat_service
417+
418+
return service_id
419+
420+
def get_text_embedding_generation_service_id(
421+
self, service_id: Optional[str] = None
422+
) -> str:
423+
if (
424+
service_id is None
425+
or service_id not in self._text_embedding_generation_services
426+
):
427+
if self._default_text_embedding_generation_service is None:
428+
raise ValueError("No default embedding service is set")
429+
return self._default_text_embedding_generation_service
430+
431+
return service_id
432+
433+
def remove_text_completion_service(self, service_id: str) -> "Kernel":
434+
if service_id not in self._text_completion_services:
435+
raise ValueError(
436+
f"AI service with service_id '{service_id}' does not exist"
437+
)
438+
439+
del self._text_completion_services[service_id]
440+
if self._default_text_completion_service == service_id:
441+
self._default_text_completion_service = next(
442+
iter(self._text_completion_services), None
443+
)
444+
return self
445+
446+
def remove_chat_service(self, service_id: str) -> "Kernel":
447+
if service_id not in self._chat_services:
448+
raise ValueError(
449+
f"AI service with service_id '{service_id}' does not exist"
450+
)
451+
452+
del self._chat_services[service_id]
453+
if self._default_chat_service == service_id:
454+
self._default_chat_service = next(iter(self._chat_services), None)
455+
return self
456+
457+
def remove_text_embedding_generation_service(self, service_id: str) -> "Kernel":
458+
if service_id not in self._text_embedding_generation_services:
459+
raise ValueError(
460+
f"AI service with service_id '{service_id}' does not exist"
461+
)
462+
463+
del self._text_embedding_generation_services[service_id]
464+
if self._default_text_embedding_generation_service == service_id:
465+
self._default_text_embedding_generation_service = next(
466+
iter(self._text_embedding_generation_services), None
467+
)
468+
return self
469+
470+
def clear_all_text_completion_services(self) -> "Kernel":
471+
self._text_completion_services = {}
472+
self._default_text_completion_service = None
473+
return self
474+
475+
def clear_all_chat_services(self) -> "Kernel":
476+
self._chat_services = {}
477+
self._default_chat_service = None
478+
return self
479+
480+
def clear_all_text_embedding_generation_services(self) -> "Kernel":
481+
self._text_embedding_generation_services = {}
482+
self._default_text_embedding_generation_service = None
483+
return self
484+
485+
def clear_all_services(self) -> "Kernel":
486+
self._text_completion_services = {}
487+
self._chat_services = {}
488+
self._text_embedding_generation_services = {}
489+
490+
self._default_text_completion_service = None
491+
self._default_chat_service = None
492+
self._default_text_embedding_generation_service = None
493+
494+
return self
495+
251496
def _create_semantic_function(
252497
self,
253498
skill_name: str,
@@ -274,7 +519,7 @@ def _create_semantic_function(
274519
function.set_default_skill_collection(self.skills)
275520

276521
if function_config.has_chat_prompt:
277-
service = self._config.get_ai_service(
522+
service = self.get_ai_service(
278523
ChatCompletionClientBase,
279524
function_config.prompt_template_config.default_services[0]
280525
if len(function_config.prompt_template_config.default_services) > 0
@@ -297,7 +542,7 @@ def _create_semantic_function(
297542

298543
function.set_chat_service(lambda: service(self))
299544
else:
300-
service = self._config.get_ai_service(
545+
service = self.get_ai_service(
301546
TextCompletionClientBase,
302547
function_config.prompt_template_config.default_services[0]
303548
if len(function_config.prompt_template_config.default_services) > 0

python/semantic_kernel/kernel_base.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from logging import Logger
55
from typing import Any, Dict, Optional
66

7-
from semantic_kernel.kernel_config import KernelConfig
87
from semantic_kernel.memory.semantic_text_memory_base import SemanticTextMemoryBase
98
from semantic_kernel.orchestration.context_variables import ContextVariables
109
from semantic_kernel.orchestration.sk_context import SKContext
@@ -21,11 +20,6 @@
2120

2221

2322
class KernelBase(ABC):
24-
@property
25-
@abstractmethod
26-
def config(self) -> KernelConfig:
27-
pass
28-
2923
@property
3024
@abstractmethod
3125
def logger(self) -> Logger:

0 commit comments

Comments
 (0)