Skip to content

Commit ed1fb08

Browse files
authored
Python: Add type hint for ProcessStepBuilder in send_event_to. Improve schema building. (#9608)
### Motivation and Context The `ProcessStepEdgeBuilder` `send_event_to` method allows one to pass in a target that isn't of type `ProcessFunctionTargetBuilder`. If it is of the other type `ProcessStepBuilder` we build the expected type `ProcessFunctionTargetBuilder`. The `send_event_to` method was missing the type hint, which can cause issues for type checkers. Secondly, during json schema building, we get the type hints using the builder's globals and locals. This is an issue because it doesn't allow us to find types defined outside of this module. Improve this by getting type hints for user defined modules, as well. <!-- 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 This PR adds the proper type hint. - Closes #9605 - Closes #9606 - Renames a `test_postgres.py` file that existed both in unit tests and integration tests and caused a load conflict. <!-- 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 1db12c4 commit ed1fb08

File tree

5 files changed

+85
-4
lines changed

5 files changed

+85
-4
lines changed

python/semantic_kernel/processes/process_step_edge_builder.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,26 @@ def __init__(self, source: "ProcessStepBuilder", event_id: str):
2525
self.source = source
2626
self.event_id = event_id
2727

28-
def send_event_to(self, target: ProcessFunctionTargetBuilder, **kwargs) -> "ProcessStepEdgeBuilder":
29-
"""Sends the event to the target."""
28+
def send_event_to(
29+
self, target: "ProcessFunctionTargetBuilder | ProcessStepBuilder", **kwargs
30+
) -> "ProcessStepEdgeBuilder":
31+
"""Sends the event to the target.
32+
33+
Args:
34+
target: The target to send the event to.
35+
**kwargs: Additional keyword arguments.
36+
37+
Returns:
38+
ProcessStepEdgeBuilder: The ProcessStepEdgeBuilder instance.
39+
"""
40+
from semantic_kernel.processes.process_step_builder import ProcessStepBuilder
41+
3042
if self.target is not None:
3143
raise ProcessInvalidConfigurationException(
3244
"An output target has already been set part of the ProcessStepEdgeBuilder."
3345
)
3446

35-
if not isinstance(target, ProcessFunctionTargetBuilder):
47+
if isinstance(target, ProcessStepBuilder):
3648
target = ProcessFunctionTargetBuilder(step=target, parameter_name=kwargs.get("parameter_name"))
3749

3850
self.target = target

python/semantic_kernel/schema/kernel_json_schema_builder.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright (c) Microsoft. All rights reserved.
22

3+
import sys
34
import types
45
from enum import Enum
56
from typing import Any, Union, get_args, get_origin, get_type_hints
@@ -80,7 +81,9 @@ def build_model_schema(
8081
# https://github.com/microsoft/semantic-kernel/issues/6464
8182
properties = {}
8283
required = []
83-
hints = get_type_hints(model, globals(), locals())
84+
85+
model_module_globals = vars(sys.modules[model.__module__])
86+
hints = get_type_hints(model, globalns=model_module_globals, localns={})
8487

8588
for field_name, field_type in hints.items():
8689
field_description = None

python/tests/unit/processes/test_process_step_edge_builder.py

+22
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ def test_send_event_to():
4242
source.link_to.assert_called_once_with(event_id, edge_builder)
4343

4444

45+
def test_send_event_to_step_builder_input():
46+
# Arrange
47+
source = MagicMock(spec=ProcessStepBuilder)
48+
source.link_to = MagicMock()
49+
50+
target = MagicMock(spec=ProcessStepBuilder)
51+
target.resolve_function_target = MagicMock(
52+
return_value=MagicMock(function_name="mock_function_name", parameter_name="provided_parameter_name")
53+
)
54+
55+
event_id = "event_003"
56+
edge_builder = ProcessStepEdgeBuilder(source=source, event_id=event_id)
57+
58+
# Act
59+
edge_builder.send_event_to(target, parameter_name="provided_parameter_name")
60+
61+
# Assert
62+
assert edge_builder.target.step == target
63+
assert edge_builder.target.parameter_name == "provided_parameter_name"
64+
source.link_to.assert_called_once_with(event_id, edge_builder)
65+
66+
4567
def test_send_event_to_creates_target():
4668
# Arrange
4769
source = MagicMock(spec=ProcessStepBuilder)

python/tests/unit/services/test_service_utils.py

+44
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright (c) Microsoft. All rights reserved.
22

3+
import datetime
34
from enum import Enum
45
from typing import Annotated
56

@@ -16,6 +17,16 @@
1617
# region Test helpers
1718

1819

20+
class DateTimePlugin:
21+
class Data(KernelBaseModel):
22+
timestamp: datetime.datetime
23+
24+
@kernel_function(name="GetData", description="Get a Data class.")
25+
def get_data(data: Annotated[Data, "The data."]) -> Annotated[Data, "The data."]:
26+
"""Get the data."""
27+
return data
28+
29+
1930
class BooleanPlugin:
2031
@kernel_function(name="GetBoolean", description="Get a boolean value.")
2132
def get_boolean(self, value: Annotated[bool, "The boolean value."]) -> Annotated[bool, "The boolean value."]:
@@ -117,6 +128,7 @@ def setup_kernel():
117128
"UnionPlugin": UnionTypePlugin(),
118129
"UnionPluginLegacy": UnionTypePluginLegacySyntax(),
119130
"EnumPlugin": EnumPlugin(),
131+
"DateTimePlugin": DateTimePlugin(),
120132
})
121133
return kernel
122134

@@ -396,3 +408,35 @@ def test_enum_plugin(setup_kernel):
396408
}
397409

398410
assert complex_schema == expected_schema
411+
412+
413+
def test_datetime_parameter(setup_kernel):
414+
kernel = setup_kernel
415+
416+
complex_func_metadata = kernel.get_list_of_function_metadata_filters(
417+
filters={"included_plugins": ["DateTimePlugin"]}
418+
)
419+
420+
complex_schema = kernel_function_metadata_to_function_call_format(complex_func_metadata[0])
421+
422+
expected_schema = {
423+
"type": "function",
424+
"function": {
425+
"name": "DateTimePlugin-GetData",
426+
"description": "Get a Data class.",
427+
"parameters": {
428+
"type": "object",
429+
"properties": {
430+
"data": {
431+
"type": "object",
432+
"properties": {"timestamp": {"type": "object"}},
433+
"required": ["timestamp"],
434+
"description": "The data.",
435+
}
436+
},
437+
"required": ["data"],
438+
},
439+
},
440+
}
441+
442+
assert complex_schema == expected_schema

0 commit comments

Comments
 (0)