Skip to content

Commit ec89fe4

Browse files
authoredMar 20, 2025··
Python: Exclude certain fields from KernelFunction model dump (#11081)
### Motivation and Context When trying to do a model dump for `KernelFunction` models, they are failing with a message related to: ``` pydantic_core._pydantic_core.PydanticSerializationError: Unable to serialize unknown type: <class 'type'> ``` There are some class-level attributes that cannot be properly serialized. <!-- 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. --> ### Description Exclude dumping certain model attributes from the model dump, which allows the main KernelFunction metadata and its underlying parameters to be serialized. - Closes #11078 - Adds unit tests to ensure we have coverage and can dump Pydantic models. <!-- 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: --> - [X] The code builds clean without any errors or warnings - [X] 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 - [X] All unit tests pass, and I have added new tests where possible - [X] I didn't break anyone 😄
1 parent 103c1e6 commit ec89fe4

5 files changed

+66
-6
lines changed
 

‎python/semantic_kernel/functions/kernel_function.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ class KernelFunction(KernelBaseModel):
9292

9393
metadata: KernelFunctionMetadata
9494

95-
invocation_duration_histogram: metrics.Histogram = Field(default_factory=_create_function_duration_histogram)
95+
invocation_duration_histogram: metrics.Histogram = Field(
96+
default_factory=_create_function_duration_histogram, exclude=True
97+
)
9698
streaming_duration_histogram: metrics.Histogram = Field(
97-
default_factory=_create_function_streaming_duration_histogram
99+
default_factory=_create_function_streaming_duration_histogram, exclude=True
98100
)
99101

100102
@classmethod

‎python/semantic_kernel/functions/kernel_function_from_method.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from inspect import isasyncgen, isasyncgenfunction, isawaitable, iscoroutinefunction, isgenerator, isgeneratorfunction
77
from typing import Any
88

9-
from pydantic import ValidationError
9+
from pydantic import Field, ValidationError
1010

1111
from semantic_kernel.exceptions import FunctionExecutionException, FunctionInitializationError
1212
from semantic_kernel.filters.functions.function_invocation_context import FunctionInvocationContext
@@ -21,8 +21,8 @@
2121
class KernelFunctionFromMethod(KernelFunction):
2222
"""Semantic Kernel Function from a method."""
2323

24-
method: Callable[..., Any]
25-
stream_method: Callable[..., Any] | None = None
24+
method: Callable[..., Any] = Field(exclude=True)
25+
stream_method: Callable[..., Any] | None = Field(default=None, exclude=True)
2626

2727
def __init__(
2828
self,

‎python/semantic_kernel/functions/kernel_parameter_metadata.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class KernelParameterMetadata(KernelBaseModel):
1717
default_value: Any | None = None
1818
type_: str | None = Field(default="str", alias="type")
1919
is_required: bool | None = False
20-
type_object: Any | None = None
20+
type_object: Any | None = Field(default=None, exclude=True)
2121
schema_data: dict[str, Any] | None = None
2222
include_in_function_choices: bool = True
2323

‎python/tests/unit/functions/test_kernel_function_from_method.py

+22
Original file line numberDiff line numberDiff line change
@@ -544,3 +544,25 @@ def test_gather_function_parameters_exception_handling(get_custom_type_function_
544544

545545
with pytest.raises(FunctionExecutionException, match=r"Parameter param is expected to be parsed to .* but is not."):
546546
func.gather_function_parameters(context)
547+
548+
549+
@pytest.mark.parametrize(
550+
("mode"),
551+
[
552+
("python"),
553+
("json"),
554+
],
555+
)
556+
def test_function_model_dump(get_custom_type_function_pydantic, mode):
557+
func: KernelFunctionFromMethod = get_custom_type_function_pydantic
558+
model_dump = func.model_dump(mode=mode)
559+
assert isinstance(model_dump, dict)
560+
assert "metadata" in model_dump
561+
assert len(model_dump["metadata"]["parameters"]) == 1
562+
563+
564+
def test_function_model_dump_json(get_custom_type_function_pydantic):
565+
func = get_custom_type_function_pydantic
566+
model_dump = func.model_dump_json()
567+
assert isinstance(model_dump, str)
568+
assert "metadata" in model_dump

‎python/tests/unit/functions/test_kernel_function_from_prompt.py

+36
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,39 @@ async def prompt_rendering_filter(context: PromptRenderContext, next):
382382
context = FunctionInvocationContext(function=function, kernel=kernel, arguments=KernelArguments())
383383
prompt_render_result = await function._render_prompt(context)
384384
assert prompt_render_result.rendered_prompt == "preface test"
385+
386+
387+
@pytest.mark.parametrize(
388+
("mode"),
389+
[
390+
("python"),
391+
("json"),
392+
],
393+
)
394+
def test_function_model_dump(mode: str):
395+
function = KernelFunctionFromPrompt(
396+
function_name="test",
397+
plugin_name="test",
398+
prompt="test",
399+
template_format="semantic-kernel",
400+
prompt_template_config=PromptTemplateConfig(
401+
template="test",
402+
input_variables=[InputVariable(name="input", type="str", default="test", is_required=False)],
403+
),
404+
)
405+
model_dump = function.model_dump(mode=mode)
406+
assert isinstance(model_dump, dict)
407+
assert "metadata" in model_dump
408+
assert len(model_dump["metadata"]["parameters"]) == 1
409+
410+
411+
def test_function_model_dump_json():
412+
function = KernelFunctionFromPrompt(
413+
function_name="test",
414+
plugin_name="test",
415+
prompt="test",
416+
template_format="semantic-kernel",
417+
)
418+
model_dump_json = function.model_dump_json()
419+
assert isinstance(model_dump_json, str)
420+
assert "test" in model_dump_json

0 commit comments

Comments
 (0)
Please sign in to comment.