Skip to content

Commit d3dcc27

Browse files
joowon-dm-snuawharrison-28lemillermicrosoft
authored andcommitted
Python: provide methods to register single native function to the kernel (microsoft#2390)
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> solve microsoft#2321 ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄 --------- Co-authored-by: Abby Harrison <[email protected]> Co-authored-by: Abby Harrison <[email protected]> Co-authored-by: Lee Miller <[email protected]>
1 parent abcaa35 commit d3dcc27

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

python/semantic_kernel/kernel.py

+33
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,39 @@ def register_semantic_function(
133133

134134
return function
135135

136+
def register_native_function(
137+
self,
138+
skill_name: Optional[str],
139+
sk_function: Callable,
140+
) -> SKFunctionBase:
141+
if not hasattr(sk_function, "__sk_function__"):
142+
raise KernelException(
143+
KernelException.ErrorCodes.InvalidFunctionType,
144+
"sk_function argument must be decorated with @sk_function",
145+
)
146+
function_name = sk_function.__sk_function_name__
147+
148+
if skill_name is None or skill_name == "":
149+
skill_name = SkillCollection.GLOBAL_SKILL
150+
assert skill_name is not None # for type checker
151+
152+
validate_skill_name(skill_name)
153+
validate_function_name(function_name)
154+
155+
function = SKFunction.from_native_method(sk_function, skill_name, self.logger)
156+
157+
if self.skills.has_function(skill_name, function_name):
158+
raise KernelException(
159+
KernelException.ErrorCodes.FunctionOverloadNotSupported,
160+
"Overloaded functions are not supported, "
161+
"please differentiate function names.",
162+
)
163+
164+
function.set_default_skill_collection(self.skills)
165+
self._skill_collection.add_native_function(function)
166+
167+
return function
168+
136169
async def run_stream_async(
137170
self,
138171
*functions: Any,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
4+
import pytest
5+
6+
from semantic_kernel import Kernel
7+
from semantic_kernel.kernel_exception import KernelException
8+
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
9+
from semantic_kernel.skill_definition.sk_function_decorator import sk_function
10+
from semantic_kernel.skill_definition.skill_collection import SkillCollection
11+
12+
13+
def not_decorated_native_function(arg1: str) -> str:
14+
return "test"
15+
16+
17+
@sk_function(name="getLightStatus")
18+
def decorated_native_function(arg1: str) -> str:
19+
return "test"
20+
21+
22+
def test_register_valid_native_function():
23+
kernel = Kernel()
24+
25+
registered_func = kernel.register_native_function(
26+
"TestSkill", decorated_native_function
27+
)
28+
29+
assert isinstance(registered_func, SKFunctionBase)
30+
assert (
31+
kernel.skills.get_native_function("TestSkill", "getLightStatus")
32+
== registered_func
33+
)
34+
assert registered_func.invoke("testtest").result == "test"
35+
36+
37+
def test_register_undecorated_native_function():
38+
kernel = Kernel()
39+
40+
with pytest.raises(KernelException):
41+
kernel.register_native_function("TestSkill", not_decorated_native_function)
42+
43+
44+
def test_register_with_none_skill_name():
45+
kernel = Kernel()
46+
47+
registered_func = kernel.register_native_function(None, decorated_native_function)
48+
assert registered_func.skill_name == SkillCollection.GLOBAL_SKILL
49+
50+
51+
def test_register_overloaded_native_function():
52+
kernel = Kernel()
53+
54+
kernel.register_native_function("TestSkill", decorated_native_function)
55+
56+
with pytest.raises(KernelException):
57+
kernel.register_native_function("TestSkill", decorated_native_function)
58+
59+
60+
if __name__ == "__main__":
61+
pytest.main([__file__])

0 commit comments

Comments
 (0)