Skip to content

Commit 8b4a149

Browse files
alliscodeBen Thomascrickman
authored
.Net Processes: Updated to process abstractions and core builders in preparation for Dapr runtime (#9285)
### Description This PR is the first in a series that will enable the Dapr runtime for Processes. This change makes adjustments to the abstractions and core builder that are mainly focused on supporting the DataContract serialization used for Dapr actor remoted function calling. ### 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 😄 --------- Co-authored-by: Ben Thomas <bentho@microsoft.com> Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
1 parent fb778b7 commit 8b4a149

File tree

13 files changed

+121
-43
lines changed

13 files changed

+121
-43
lines changed

.vscode/settings.json

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
{
22
"prettier.enable": true,
3-
"css.lint.validProperties": [
4-
"composes"
5-
],
3+
"css.lint.validProperties": ["composes"],
64
"editor.formatOnType": true,
75
"editor.formatOnSave": true,
86
"editor.formatOnPaste": true,
@@ -15,13 +13,9 @@
1513
"editor.bracketPairColorization.enabled": true,
1614
"editor.guides.bracketPairs": "active",
1715
"python.formatting.provider": "autopep8",
18-
"python.formatting.autopep8Args": [
19-
"--max-line-length=120"
20-
],
16+
"python.formatting.autopep8Args": ["--max-line-length=120"],
2117
"notebook.output.textLineLimit": 500,
22-
"python.analysis.extraPaths": [
23-
"./python/src"
24-
],
18+
"python.analysis.extraPaths": ["./python/src"],
2519
"javascript.updateImportsOnFileMove.enabled": "always",
2620
"search.exclude": {
2721
"**/node_modules": true,
@@ -71,31 +65,34 @@
7165
"**/Thumbs.db": true
7266
},
7367
"cSpell.words": [
68+
"Dapr",
7469
"Partitioner",
70+
"Pregel",
7571
"Prompty",
76-
"SKEXP"
72+
"SKEXP",
73+
"superstep",
74+
"Supersteps",
75+
"typeref"
7776
],
7877
"[java]": {
7978
"editor.formatOnSave": false,
8079
"editor.tabSize": 4,
8180
"editor.codeActionsOnSave": {
8281
"source.fixAll": "never"
83-
},
82+
}
8483
},
8584
"emeraldwalk.runonsave": {
8685
"commands": [
8786
{
8887
"match": "\\.java$",
8988
"cmd": "java -Xmx128m -jar ${workspaceFolder}/java/utilities/google-java-format-1.17.0-all-deps.jar --replace --aosp ${file}"
90-
},
91-
],
89+
}
90+
]
9291
},
9392
"java.debug.settings.onBuildFailureProceed": true,
9493
"java.compile.nullAnalysis.mode": "disabled",
9594
"dotnet.defaultSolution": "dotnet\\SK-dotnet.sln",
96-
"python.testing.pytestArgs": [
97-
"python/tests"
98-
],
95+
"python.testing.pytestArgs": ["python/tests"],
9996
"python.testing.unittestEnabled": false,
10097
"python.testing.pytestEnabled": true
101-
}
98+
}

dotnet/samples/GettingStartedWithProcesses/SharedSteps/ScriptedUserInputStep.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,9 @@ public virtual void PopulateUserInputs(UserInputState state)
4141
/// <returns>A <see cref="ValueTask"/></returns>
4242
public override ValueTask ActivateAsync(KernelProcessStepState<UserInputState> state)
4343
{
44-
state.State ??= new();
4544
_state = state.State;
4645

47-
PopulateUserInputs(_state);
46+
PopulateUserInputs(_state!);
4847

4948
return ValueTask.CompletedTask;
5049
}
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System.Runtime.Serialization;
4+
35
namespace Microsoft.SemanticKernel;
46

57
/// <summary>
68
/// A serializable representation of an edge between a source <see cref="KernelProcessStep"/> and a <see cref="KernelProcessFunctionTarget"/>.
79
/// </summary>
10+
[DataContract]
11+
[KnownType(typeof(KernelProcessFunctionTarget))]
812
public sealed class KernelProcessEdge
913
{
1014
/// <summary>
1115
/// The unique identifier of the source Step.
1216
/// </summary>
13-
public string SourceStepId { get; }
17+
[DataMember]
18+
public string SourceStepId { get; init; }
1419

1520
/// <summary>
1621
/// The collection of <see cref="KernelProcessFunctionTarget"/>s that are the output of the source Step.
1722
/// </summary>
18-
public KernelProcessFunctionTarget OutputTarget { get; }
23+
[DataMember]
24+
public KernelProcessFunctionTarget OutputTarget { get; init; }
1925

2026
/// <summary>
2127
/// Creates a new instance of the <see cref="KernelProcessEdge"/> class.
2228
/// </summary>
23-
public KernelProcessEdge(string sourceStepId, KernelProcessFunctionTarget outputTargets)
29+
public KernelProcessEdge(string sourceStepId, KernelProcessFunctionTarget outputTarget)
2430
{
2531
Verify.NotNullOrWhiteSpace(sourceStepId);
26-
Verify.NotNull(outputTargets);
32+
Verify.NotNull(outputTarget);
2733

2834
this.SourceStepId = sourceStepId;
29-
this.OutputTarget = outputTargets;
35+
this.OutputTarget = outputTarget;
3036
}
3137
}

dotnet/src/Experimental/Process.Abstractions/KernelProcessFunctionTarget.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System.Runtime.Serialization;
4+
35
namespace Microsoft.SemanticKernel;
46

57
/// <summary>
68
/// A serializable representation of a specific parameter of a specific function of a specific Step.
79
/// </summary>
10+
[DataContract]
811
public record KernelProcessFunctionTarget
912
{
1013
/// <summary>
@@ -24,20 +27,24 @@ public KernelProcessFunctionTarget(string stepId, string functionName, string? p
2427
/// <summary>
2528
/// The unique identifier of the Step being targeted.
2629
/// </summary>
30+
[DataMember]
2731
public string StepId { get; init; }
2832

2933
/// <summary>
3034
/// The name if the Kernel Function to target.
3135
/// </summary>
36+
[DataMember]
3237
public string FunctionName { get; init; }
3338

3439
/// <summary>
3540
/// The name of the parameter to target. This may be null if the function has no parameters.
3641
/// </summary>
42+
[DataMember]
3743
public string? ParameterName { get; init; }
3844

3945
/// <summary>
4046
/// The unique identifier for the event to target. This may be null if the target is not a sub-process.
4147
/// </summary>
48+
[DataMember]
4249
public string? TargetEventId { get; init; }
4350
}

dotnet/src/Experimental/Process.Abstractions/KernelProcessState.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System.Runtime.Serialization;
4+
35
namespace Microsoft.SemanticKernel;
46

57
/// <summary>
68
/// Represents the state of a process.
79
/// </summary>
10+
[DataContract]
811
public sealed record KernelProcessState : KernelProcessStepState
912
{
1013
/// <summary>
11-
/// Initializes a new instance of the <see cref="KernelProcessStepState"/> class.
14+
/// Initializes a new instance of the <see cref="KernelProcessState"/> class.
1215
/// </summary>
1316
/// <param name="name">The name of the associated <see cref="KernelProcessStep"/></param>
1417
/// <param name="id">The Id of the associated <see cref="KernelProcessStep"/></param>

dotnet/src/Experimental/Process.Abstractions/KernelProcessStepInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,8 @@ public KernelProcessStepInfo(Type innerStepType, KernelProcessStepState state, D
5454
this.InnerStepType = innerStepType;
5555
this._outputEdges = edges;
5656
this._state = state;
57+
58+
// Register the state as a know type for the DataContractSerialization used by Dapr.
59+
KernelProcessState.RegisterDerivedType(state.GetType());
5760
}
5861
}

dotnet/src/Experimental/Process.Abstractions/KernelProcessStepState.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,51 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System;
4+
using System.Collections.Generic;
5+
using System.Runtime.Serialization;
6+
37
namespace Microsoft.SemanticKernel;
48

59
/// <summary>
610
/// Represents the state of an individual step in a process.
711
/// </summary>
12+
[DataContract]
13+
[KnownType(nameof(GetKnownTypes))]
814
public record KernelProcessStepState
915
{
16+
/// <summary>
17+
/// A set of known types that may be used in serialization.
18+
/// </summary>
19+
private readonly static HashSet<Type> s_knownTypes = [];
20+
21+
/// <summary>
22+
/// Used to dynamically provide the set of known types for serialization.
23+
/// </summary>
24+
/// <returns></returns>
25+
private static HashSet<Type> GetKnownTypes() => s_knownTypes;
26+
27+
/// <summary>
28+
/// Registers a derived type for serialization. Types registered here are used by the KnownType attribute
29+
/// to support DataContractSerialization of derived types as required to support Dapr.
30+
/// </summary>
31+
/// <param name="derivedType">A Type that derives from <typeref name="KernelProcessStepState"/></param>
32+
internal static void RegisterDerivedType(Type derivedType)
33+
{
34+
s_knownTypes.Add(derivedType);
35+
}
36+
1037
/// <summary>
1138
/// The identifier of the Step which is required to be unique within an instance of a Process.
1239
/// This may be null until a process containing this step has been invoked.
1340
/// </summary>
41+
[DataMember]
1442
public string? Id { get; init; }
1543

1644
/// <summary>
17-
/// The name of the Step. This is itended to be human readable and is not required to be unique. If
45+
/// The name of the Step. This is intended to be human readable and is not required to be unique. If
1846
/// not provided, the name will be derived from the steps .NET type.
1947
/// </summary>
48+
[DataMember]
2049
public string Name { get; init; }
2150

2251
/// <summary>
@@ -37,12 +66,14 @@ public KernelProcessStepState(string name, string? id = null)
3766
/// Represents the state of an individual step in a process that includes a user-defined state object.
3867
/// </summary>
3968
/// <typeparam name="TState">The type of the user-defined state.</typeparam>
69+
[DataContract]
4070
public sealed record KernelProcessStepState<TState> : KernelProcessStepState where TState : class, new()
4171
{
4272
/// <summary>
4373
/// The user-defined state object associated with the Step.
4474
/// </summary>
45-
public TState? State { get; set; }
75+
[DataMember]
76+
public TState? State { get; init; }
4677

4778
/// <summary>
4879
/// Initializes a new instance of the <see cref="KernelProcessStepState"/> class.

dotnet/src/Experimental/Process.Core/Internal/EndStep.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Microsoft.SemanticKernel;
99
/// </summary>
1010
internal sealed class EndStep : ProcessStepBuilder
1111
{
12-
private const string EndStepValue = "END";
12+
private const string EndStepValue = "Microsoft.SemanticKernel.Process.EndStep";
1313

1414
/// <summary>
1515
/// The name of the end step.

dotnet/src/Experimental/Process.Core/ProcessBuilder.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,22 @@ public ProcessStepBuilder AddStepFromType<TStep>(string? name = null) where TSte
115115
return stepBuilder;
116116
}
117117

118+
/// <summary>
119+
/// Adds a step to the process and define it's initial user-defined state.
120+
/// </summary>
121+
/// <typeparam name="TStep">The step Type.</typeparam>
122+
/// <typeparam name="TState">The state Type.</typeparam>
123+
/// <param name="initialState">The initial state of the step.</param>
124+
/// <param name="name">The name of the step. This parameter is optional.</param>
125+
/// <returns>An instance of <see cref="ProcessStepBuilder"/></returns>
126+
public ProcessStepBuilder AddStepFromType<TStep, TState>(TState initialState, string? name = null) where TStep : KernelProcessStep<TState> where TState : class, new()
127+
{
128+
var stepBuilder = new ProcessStepBuilder<TStep>(name, initialState);
129+
this._steps.Add(stepBuilder);
130+
131+
return stepBuilder;
132+
}
133+
118134
/// <summary>
119135
/// Adds a sub process to the process.
120136
/// </summary>

dotnet/src/Experimental/Process.Core/ProcessStepBuilder.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,21 @@ protected ProcessStepBuilder(string name)
194194
/// </summary>
195195
public sealed class ProcessStepBuilder<TStep> : ProcessStepBuilder where TStep : KernelProcessStep
196196
{
197+
/// <summary>
198+
/// The initial state of the step. This may be null if the step does not have any state.
199+
/// </summary>
200+
private readonly object? _initialState;
201+
197202
/// <summary>
198203
/// Creates a new instance of the <see cref="ProcessStepBuilder"/> class. If a name is not provided, the name will be derived from the type of the step.
199204
/// </summary>
200-
public ProcessStepBuilder(string? name = null)
205+
/// <param name="name">Optional: The name of the step.</param>
206+
/// <param name="initialState">Optional: The initial state of the step.</param>
207+
internal ProcessStepBuilder(string? name = null, object? initialState = default)
201208
: base(name ?? typeof(TStep).Name)
202209
{
203210
this.FunctionsDict = this.GetFunctionMetadataMap();
211+
this._initialState = initialState;
204212
}
205213

206214
/// <summary>
@@ -221,7 +229,14 @@ internal override KernelProcessStepInfo BuildStep()
221229
var stateType = typeof(KernelProcessStepState<>).MakeGenericType(userStateType);
222230
Verify.NotNull(stateType);
223231

232+
// If the step has a user-defined state then we need to validate that the initial state is of the correct type.
233+
if (this._initialState is not null && this._initialState.GetType() != userStateType)
234+
{
235+
throw new KernelException($"The initial state provided for step {this.Name} is not of the correct type. The expected type is {userStateType.Name}.");
236+
}
237+
224238
stateObject = (KernelProcessStepState?)Activator.CreateInstance(stateType, this.Name, this.Id);
239+
stateType.GetProperty(nameof(KernelProcessStepState<object>.State))?.SetValue(stateObject, this._initialState);
225240
}
226241
else
227242
{

0 commit comments

Comments
 (0)