11# Copyright (c) Microsoft. All rights reserved.
22
3+ import glob
4+ import importlib
35import inspect
6+ import os
47from logging import Logger
58from typing import Any , Callable , Dict , List , Optional , Type , TypeVar , Union
9+ from uuid import uuid4
610
711from semantic_kernel .connectors .ai .ai_exception import AIException
812from semantic_kernel .connectors .ai .chat_completion_client_base import (
1822from semantic_kernel .connectors .ai .text_completion_client_base import (
1923 TextCompletionClientBase ,
2024)
21- from semantic_kernel .kernel_base import KernelBase
2225from semantic_kernel .kernel_exception import KernelException
23- from semantic_kernel .kernel_extensions import KernelExtensions
2426from semantic_kernel .memory .memory_store_base import MemoryStoreBase
2527from semantic_kernel .memory .null_memory import NullMemory
28+ from semantic_kernel .memory .semantic_text_memory import SemanticTextMemory
2629from semantic_kernel .memory .semantic_text_memory_base import SemanticTextMemoryBase
2730from semantic_kernel .orchestration .context_variables import ContextVariables
2831from semantic_kernel .orchestration .sk_context import SKContext
3235 PassThroughWithoutRetry ,
3336)
3437from semantic_kernel .reliability .retry_mechanism import RetryMechanism
38+ from semantic_kernel .semantic_functions .prompt_template import PromptTemplate
39+ from semantic_kernel .semantic_functions .prompt_template_config import (
40+ PromptTemplateConfig ,
41+ )
3542from semantic_kernel .semantic_functions .semantic_function_config import (
3643 SemanticFunctionConfig ,
3744)
5057T = TypeVar ("T" )
5158
5259
53- class Kernel ( KernelBase , KernelExtensions ) :
60+ class Kernel :
5461 _log : Logger
5562 _skill_collection : SkillCollectionBase
5663 _prompt_template_engine : PromptTemplatingEngine
@@ -75,13 +82,13 @@ def __init__(
7582 self ._memory = memory if memory else NullMemory ()
7683
7784 self ._text_completion_services : Dict [
78- str , Callable [["KernelBase " ], TextCompletionClientBase ]
85+ str , Callable [["Kernel " ], TextCompletionClientBase ]
7986 ] = {}
8087 self ._chat_services : Dict [
81- str , Callable [["KernelBase " ], ChatCompletionClientBase ]
88+ str , Callable [["Kernel " ], ChatCompletionClientBase ]
8289 ] = {}
8390 self ._text_embedding_generation_services : Dict [
84- str , Callable [["KernelBase " ], EmbeddingGeneratorBase ]
91+ str , Callable [["Kernel " ], EmbeddingGeneratorBase ]
8592 ] = {}
8693
8794 self ._default_text_completion_service : Optional [str ] = None
@@ -90,9 +97,6 @@ def __init__(
9097
9198 self ._retry_mechanism : RetryMechanism = PassThroughWithoutRetry ()
9299
93- def kernel (self ) -> KernelBase :
94- return self
95-
96100 @property
97101 def logger (self ) -> Logger :
98102 return self ._log
@@ -212,6 +216,29 @@ def func(self, skill_name: str, function_name: str) -> SKFunctionBase:
212216
213217 return self .skills .get_semantic_function (skill_name , function_name )
214218
219+ def use_memory (
220+ self ,
221+ storage : MemoryStoreBase ,
222+ embeddings_generator : Optional [EmbeddingGeneratorBase ] = None ,
223+ ) -> None :
224+ if embeddings_generator is None :
225+ service_id = self .get_text_embedding_generation_service_id ()
226+ if not service_id :
227+ raise ValueError ("The embedding service id cannot be `None` or empty" )
228+
229+ embeddings_service = self .get_ai_service (EmbeddingGeneratorBase , service_id )
230+ if not embeddings_service :
231+ raise ValueError (f"AI configuration is missing for: { service_id } " )
232+
233+ embeddings_generator = embeddings_service (self )
234+
235+ if storage is None :
236+ raise ValueError ("The storage instance provided cannot be `None`" )
237+ if embeddings_generator is None :
238+ raise ValueError ("The embedding generator cannot be `None`" )
239+
240+ self .register_memory (SemanticTextMemory (storage , embeddings_generator ))
241+
215242 def register_memory (self , memory : SemanticTextMemoryBase ) -> None :
216243 self ._memory = memory
217244
@@ -267,7 +294,7 @@ def import_skill(
267294
268295 def get_ai_service (
269296 self , type : Type [T ], service_id : Optional [str ] = None
270- ) -> Callable [["KernelBase " ], T ]:
297+ ) -> Callable [["Kernel " ], T ]:
271298 matching_type = {}
272299 if type == TextCompletionClientBase :
273300 service_id = service_id or self ._default_text_completion_service
@@ -301,7 +328,7 @@ def add_text_completion_service(
301328 self ,
302329 service_id : str ,
303330 service : Union [
304- TextCompletionClientBase , Callable [["KernelBase " ], TextCompletionClientBase ]
331+ TextCompletionClientBase , Callable [["Kernel " ], TextCompletionClientBase ]
305332 ],
306333 overwrite : bool = True ,
307334 ) -> "Kernel" :
@@ -324,7 +351,7 @@ def add_chat_service(
324351 self ,
325352 service_id : str ,
326353 service : Union [
327- ChatCompletionClientBase , Callable [["KernelBase " ], ChatCompletionClientBase ]
354+ ChatCompletionClientBase , Callable [["Kernel " ], ChatCompletionClientBase ]
328355 ],
329356 overwrite : bool = True ,
330357 ) -> "Kernel" :
@@ -352,7 +379,7 @@ def add_text_embedding_generation_service(
352379 self ,
353380 service_id : str ,
354381 service : Union [
355- EmbeddingGeneratorBase , Callable [["KernelBase " ], EmbeddingGeneratorBase ]
382+ EmbeddingGeneratorBase , Callable [["Kernel " ], EmbeddingGeneratorBase ]
356383 ],
357384 overwrite : bool = False ,
358385 ) -> "Kernel" :
@@ -567,3 +594,137 @@ def _create_semantic_function(
567594 function .set_ai_service (lambda : service (self ))
568595
569596 return function
597+
598+ def import_native_skill_from_directory (
599+ self , parent_directory : str , skill_directory_name : str
600+ ) -> Dict [str , SKFunctionBase ]:
601+ MODULE_NAME = "native_function"
602+
603+ validate_skill_name (skill_directory_name )
604+
605+ skill_directory = os .path .abspath (
606+ os .path .join (parent_directory , skill_directory_name )
607+ )
608+ native_py_file_path = os .path .join (skill_directory , f"{ MODULE_NAME } .py" )
609+
610+ if not os .path .exists (native_py_file_path ):
611+ raise ValueError (
612+ f"Native Skill Python File does not exist: { native_py_file_path } "
613+ )
614+
615+ skill_name = os .path .basename (skill_directory )
616+ try :
617+ spec = importlib .util .spec_from_file_location (
618+ MODULE_NAME , native_py_file_path
619+ )
620+ module = importlib .util .module_from_spec (spec )
621+ spec .loader .exec_module (module )
622+
623+ class_name = next (
624+ (
625+ name
626+ for name , cls in inspect .getmembers (module , inspect .isclass )
627+ if cls .__module__ == MODULE_NAME
628+ ),
629+ None ,
630+ )
631+ if class_name :
632+ skill_obj = getattr (module , class_name )()
633+ return self .import_skill (skill_obj , skill_name )
634+ except Exception :
635+ pass
636+
637+ return {}
638+
639+ def import_semantic_skill_from_directory (
640+ self , parent_directory : str , skill_directory_name : str
641+ ) -> Dict [str , SKFunctionBase ]:
642+ CONFIG_FILE = "config.json"
643+ PROMPT_FILE = "skprompt.txt"
644+
645+ validate_skill_name (skill_directory_name )
646+
647+ skill_directory = os .path .join (parent_directory , skill_directory_name )
648+ skill_directory = os .path .abspath (skill_directory )
649+
650+ if not os .path .exists (skill_directory ):
651+ raise ValueError (f"Skill directory does not exist: { skill_directory_name } " )
652+
653+ skill = {}
654+
655+ directories = glob .glob (skill_directory + "/*/" )
656+ for directory in directories :
657+ dir_name = os .path .dirname (directory )
658+ function_name = os .path .basename (dir_name )
659+ prompt_path = os .path .join (directory , PROMPT_FILE )
660+
661+ # Continue only if the prompt template exists
662+ if not os .path .exists (prompt_path ):
663+ continue
664+
665+ config = PromptTemplateConfig ()
666+ config_path = os .path .join (directory , CONFIG_FILE )
667+ with open (config_path , "r" ) as config_file :
668+ config = config .from_json (config_file .read ())
669+
670+ # Load Prompt Template
671+ with open (prompt_path , "r" ) as prompt_file :
672+ template = PromptTemplate (
673+ prompt_file .read (), self .prompt_template_engine , config
674+ )
675+
676+ # Prepare lambda wrapping AI logic
677+ function_config = SemanticFunctionConfig (config , template )
678+
679+ skill [function_name ] = self .register_semantic_function (
680+ skill_directory_name , function_name , function_config
681+ )
682+
683+ return skill
684+
685+ def create_semantic_function (
686+ self ,
687+ prompt_template : str ,
688+ function_name : Optional [str ] = None ,
689+ skill_name : Optional [str ] = None ,
690+ description : Optional [str ] = None ,
691+ max_tokens : int = 256 ,
692+ temperature : float = 0.0 ,
693+ top_p : float = 1.0 ,
694+ presence_penalty : float = 0.0 ,
695+ frequency_penalty : float = 0.0 ,
696+ stop_sequences : Optional [List [str ]] = None ,
697+ ) -> "SKFunctionBase" :
698+ function_name = (
699+ function_name
700+ if function_name is not None
701+ else f"f_{ str (uuid4 ()).replace ('-' , '_' )} "
702+ )
703+
704+ config = PromptTemplateConfig (
705+ description = (
706+ description
707+ if description is not None
708+ else "Generic function, unknown purpose"
709+ ),
710+ type = "completion" ,
711+ completion = PromptTemplateConfig .CompletionConfig (
712+ temperature ,
713+ top_p ,
714+ presence_penalty ,
715+ frequency_penalty ,
716+ max_tokens ,
717+ stop_sequences if stop_sequences is not None else [],
718+ ),
719+ )
720+
721+ validate_function_name (function_name )
722+ if skill_name is not None :
723+ validate_skill_name (skill_name )
724+
725+ template = PromptTemplate (prompt_template , self .prompt_template_engine , config )
726+ function_config = SemanticFunctionConfig (config , template )
727+
728+ return self .register_semantic_function (
729+ skill_name , function_name , function_config
730+ )
0 commit comments