diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml
index 7b4ec29..777222a 100644
--- a/.github/workflows/dotnet-build-and-test.yml
+++ b/.github/workflows/dotnet-build-and-test.yml
@@ -8,7 +8,7 @@ name: dotnet-build-and-test
on:
workflow_dispatch:
pull_request:
- branches: ["main", "feature*", "upgrade-sk"]
+ branches: ["main", "feature*"]
merge_group:
branches: ["main"]
diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index 3b59dfd..cfa1c7f 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -7,7 +7,7 @@ name: dotnet-format
on:
workflow_dispatch:
pull_request:
- branches: [ "main", "feature*", "upgrade-sk" ]
+ branches: [ "main", "feature*" ]
paths:
- 'dotnet/**'
- 'samples/dotnet/**'
diff --git a/.github/workflows/dotnet-integration-tests.yml b/.github/workflows/dotnet-integration-tests.yml
index 8073d79..6bd5b46 100644
--- a/.github/workflows/dotnet-integration-tests.yml
+++ b/.github/workflows/dotnet-integration-tests.yml
@@ -7,7 +7,7 @@ name: dotnet-integration-tests
on:
workflow_dispatch:
pull_request:
- branches: ["main", "upgrade-sk"]
+ branches: ["main"]
merge_group:
branches: ["main"]
diff --git a/.github/workflows/merge-gatekeeper.yml b/.github/workflows/merge-gatekeeper.yml
index 276fad7..adb6811 100644
--- a/.github/workflows/merge-gatekeeper.yml
+++ b/.github/workflows/merge-gatekeeper.yml
@@ -2,7 +2,7 @@ name: Merge Gatekeeper
on:
pull_request:
- branches: [ "main", "feature*", "upgrade-sk" ]
+ branches: [ "main", "feature*" ]
merge_group:
branches: ["main"]
diff --git a/Samples/Plans/SK178/Summarize.json b/Samples/Plans/SK178/Summarize.json
new file mode 100644
index 0000000..bf08b68
--- /dev/null
+++ b/Samples/Plans/SK178/Summarize.json
@@ -0,0 +1,18 @@
+{
+ "$comment": "Rewritten in SK 1.78 ChatHistory-style format. See docs/Plans/SK178-format.md for the full spec. The legacy 'state/steps/parameters/outputs' Plan shape was REMOVED in SK 1.78; this file replaces it for the MultiConnector integration tests, and is consumed by PlanJsonHelpers.BuildChatHistoryFromPlanJson to produce a ChatHistory that FunctionChoiceBehavior.Auto() can drive.",
+ "name": "Summarize plan",
+ "description": "You must evaluate the capabilities of a smaller LLM model. Start by writing a text of about 100 words on a given topic, that should be the input parameter of the plan. Then use one or several functions from the available skills on the input text and/or the previous functions results, choosing parameters in such a way that you know you will succeed at running each function but a smaller model might not. Try to propose steps of distinct difficulties so that models of distinct capabilities might succeed on some functions and fail on others.",
+ "input_variable": "INPUT",
+ "user_input_template": "{{$INPUT}}",
+ "invocations": [
+ {
+ "name": "Summarize",
+ "plugin_name": "SummarizeSkill",
+ "description": "Summarize given text or any text document",
+ "arguments": {
+ "INPUT": "$INPUT"
+ },
+ "output_variable": "RESULT__SUMMARY"
+ }
+ ]
+}
diff --git a/Samples/Plans/SK178/Summarize_Topics_ElementAt.json b/Samples/Plans/SK178/Summarize_Topics_ElementAt.json
new file mode 100644
index 0000000..a95d929
--- /dev/null
+++ b/Samples/Plans/SK178/Summarize_Topics_ElementAt.json
@@ -0,0 +1,38 @@
+{
+ "$comment": "Rewritten in SK 1.78 ChatHistory-style format. See docs/Plans/SK178-format.md for the full spec. The legacy 'state/steps/parameters/outputs' Plan shape was REMOVED in SK 1.78; this file replaces it for the MultiConnector integration tests, and is consumed by PlanJsonHelpers.BuildChatHistoryFromPlanJson to produce a ChatHistory that FunctionChoiceBehavior.Auto() can drive.",
+ "name": "Summarize Topics Elements plan",
+ "description": "Evaluates the capabilities of a smaller LLM model. Starting with an input text on a given topic, several functions will be called on the text and/or the previous functions results.",
+ "input_variable": "INPUT",
+ "user_input_template": "{{$INPUT}}",
+ "invocations": [
+ {
+ "name": "Summarize",
+ "plugin_name": "SummarizeSkill",
+ "description": "Summarize given text or any text document",
+ "arguments": {
+ "INPUT": "$INPUT"
+ },
+ "output_variable": "RESULT__SUMMARY"
+ },
+ {
+ "name": "Topics",
+ "plugin_name": "SummarizeSkill",
+ "description": "Analyze given text or document and extract key topics worth remembering",
+ "arguments": {
+ "input": "$INPUT"
+ },
+ "output_variable": "RESULT__TOPICS"
+ },
+ {
+ "name": "ElementAtIndex",
+ "plugin_name": "MiscSkill",
+ "description": "Get an element from an array at a specified index",
+ "arguments": {
+ "index": "2",
+ "INPUT": "$RESULT__TOPICS",
+ "count": "1"
+ },
+ "output_variable": "RESULT__THIRD_TOPIC"
+ }
+ ]
+}
diff --git a/Samples/Plans/Summarize.json b/Samples/Plans/Summarize.json
index f85a067..d975fcb 100644
--- a/Samples/Plans/Summarize.json
+++ b/Samples/Plans/Summarize.json
@@ -1,45 +1,17 @@
{
- "state": [
- {
- "Key": "INPUT",
- "Value": "In the field of quantum physics, the study of superconducting materials has gained significant attention. The unique properties of these materials, particularly their ability to conduct electricity without resistance, make them ideal for a variety of applications. However, the underlying mechanisms that give rise to these properties are not fully understood. This study aims to investigate the impact of quantum physics on the development of superconducting materials. The results indicate a significant correlation between the two, paving the way for future research in this field. The findings also suggest potential applications in the field of quantum computing, where superconducting materials could play a crucial role."
- }
- ],
+ "schema_version": "sk-1.78-kernelfunction",
+ "description": "Summarize plan: produce a ~100-word summary of the input text. Replaces the legacy SK <1.0 Plan.FromJson shape (plan.State / plan.Steps[] / plan.Parameters[] / plan.Outputs[]) for issue #7225 migration. Consumable by a future hand-rolled KernelFunction pipeline (see IntegrationTests.csproj decision block).",
+ "input_variable": "INPUT",
+ "default_input": "In the field of quantum physics, the study of superconducting materials has gained significant attention. The unique properties of these materials, particularly their ability to conduct electricity without resistance, make them ideal for a variety of applications. However, the underlying mechanisms that give rise to these properties are not fully understood. This study aims to investigate the impact of quantum physics on the development of superconducting materials. The results indicate a significant correlation between the two, paving the way for future research in this field. The findings also suggest potential applications in the field of quantum computing, where superconducting materials could play a crucial role.",
"steps": [
{
- "state": [
- {
- "Key": "INPUT",
- "Value": ""
- }
- ],
- "steps": [],
- "parameters": [
- {
- "Key": "INPUT",
- "Value": "$INPUT"
- }
- ],
+ "function": "SummarizeSkill.Summarize",
+ "parameters": {
+ "INPUT": "$INPUT"
+ },
"outputs": [
"RESULT__SUMMARY"
- ],
- "next_step_index": 0,
- "name": "Summarize",
- "plugin_name": "SummarizeSkill",
- "description": "Summarize given text or any text document"
- }
- ],
- "parameters": [
- {
- "Key": "INPUT",
- "Value": ""
+ ]
}
- ],
- "outputs": [
- "RESULT__SUMMARY"
- ],
- "next_step_index": 0,
- "name": "Summarize plan",
- "plugin_name": "Plan",
- "description": "You must evaluate the capabilities of a smaller LLM model. Start by writing a text of about 100 words on a given topic, that should be the input parameter of the plan. Then use one or several functions from the available skills on the input text and/or the previous functions results, choosing parameters in such a way that you know you will succeed at running each function but a smaller model might not. Try to propose steps of distinct difficulties so that models of distinct capabilities might succeed on some functions and fail on others."
+ ]
}
diff --git a/Samples/Plans/Summarize_Topics_ElementAt.json b/Samples/Plans/Summarize_Topics_ElementAt.json
index fbda52c..a9910da 100644
--- a/Samples/Plans/Summarize_Topics_ElementAt.json
+++ b/Samples/Plans/Summarize_Topics_ElementAt.json
@@ -1,99 +1,37 @@
{
- "state": [
- {
- "Key": "INPUT",
- "Value": "Artificial intelligence (AI) is a branch of computer science that aims to create intelligent machines capable of performing tasks that would typically require human intelligence. AI has applications in various fields, including healthcare, finance, and transportation. It involves the development of algorithms and models that can learn from data and make predictions or decisions. AI has the potential to revolutionize industries and improve efficiency. However, it also raises ethical concerns and challenges related to privacy and job displacement.\nLLMs are a recent trend of neural networks architecture. These models have the ability to understand and generate human-like text, which has made them highly popular for applications ranging from chatbots to content creation. Their capacity to process vast amounts of data allows them to provide insights, generate ideas, or even write essays, often exceeding human capabilities in terms of speed and sometimes even accuracy.\n"
- }
- ],
+ "schema_version": "sk-1.78-kernelfunction",
+ "description": "Summarize + Topics + ElementAt plan: summarize, extract topics, then pick the n-th element. Replaces the legacy SK <1.0 Plan.FromJson shape for issue #7225 migration. Consumable by a future hand-rolled KernelFunction pipeline (see IntegrationTests.csproj decision block).",
+ "input_variable": "INPUT",
+ "default_input": "Artificial intelligence (AI) is a branch of computer science that aims to create intelligent machines capable of performing tasks that would typically require human intelligence. AI has applications in various fields, including healthcare, finance, and transportation. It involves the development of algorithms and models that can learn from data and make predictions or decisions. AI has the potential to revolutionize industries and improve efficiency. However, it also raises ethical concerns and challenges related to privacy and job displacement.\nLLMs are a recent trend of neural networks architecture. These models have the ability to understand and generate human-like text, which has made them highly popular for applications ranging from chatbots to content creation. Their capacity to process vast amounts of data allows them to provide insights, generate ideas, or even write essays, often exceeding human capabilities in terms of speed and sometimes even accuracy.\n",
"steps": [
{
- "state": [
- {
- "Key": "INPUT",
- "Value": ""
- }
- ],
- "steps": [],
- "parameters": [
- {
- "Key": "INPUT",
- "Value": "$INPUT"
- }
- ],
+ "function": "SummarizeSkill.Summarize",
+ "parameters": {
+ "INPUT": "$INPUT"
+ },
"outputs": [
"RESULT__SUMMARY"
- ],
- "next_step_index": 0,
- "name": "Summarize",
- "plugin_name": "SummarizeSkill",
- "description": "Summarize given text or any text document"
+ ]
},
{
- "state": [
- {
- "Key": "INPUT",
- "Value": ""
- }
- ],
- "steps": [],
- "parameters": [
- {
- "Key": "input",
- "Value": "$INPUT"
- }
- ],
+ "function": "SummarizeSkill.Topics",
+ "parameters": {
+ "input": "$INPUT"
+ },
"outputs": [
"RESULT__TOPICS"
- ],
- "next_step_index": 0,
- "name": "Topics",
- "plugin_name": "SummarizeSkill",
- "description": "Analyze given text or document and extract key topics worth remembering"
+ ]
},
{
- "state": [
- {
- "Key": "INPUT",
- "Value": ""
- }
- ],
- "steps": [],
- "parameters": [
- {
- "Key": "index",
- "Value": "2"
- },
- {
- "Key": "INPUT",
- "Value": "$RESULT__TOPICS"
- },
- {
- "Key": "count",
- "Value": "1"
- }
- ],
+ "function": "MiscSkill.ElementAtIndex",
+ "parameters": {
+ "INPUT": "$RESULT__TOPICS",
+ "index": "2",
+ "count": "1"
+ },
"outputs": [
"RESULT__THIRD_TOPIC"
- ],
- "next_step_index": 0,
- "name": "ElementAtIndex",
- "plugin_name": "MiscSkill",
- "description": "Get an element from an array at a specified index"
- }
- ],
- "parameters": [
- {
- "Key": "INPUT",
- "Value": ""
+ ]
}
- ],
- "outputs": [
- "RESULT__SUMMARY",
- "RESULT__TOPICS",
- "RESULT__THIRD_TOPIC"
- ],
- "next_step_index": 0,
- "name": "Summarize Topics Elements plan",
- "plugin_name": "Plan",
- "description": "Evaluates the capabilities of a smaller LLM model. Starting with an input text on a given topic, several functions will be called on the text and/or the previous functions results."
+ ]
}
diff --git a/dotnet/src/IntegrationTests/Connectors/MultiConnector/MultiConnectorTests.cs b/dotnet/src/IntegrationTests/Connectors/MultiConnector/MultiConnectorTests.cs
index 6d79e23..a8611f2 100644
--- a/dotnet/src/IntegrationTests/Connectors/MultiConnector/MultiConnectorTests.cs
+++ b/dotnet/src/IntegrationTests/Connectors/MultiConnector/MultiConnectorTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) MyIA. All rights reserved.
+// Copyright (c) MyIA. All rights reserved.
using System;
using System.Collections.Generic;
@@ -6,17 +6,16 @@
using System.IO;
using System.Linq;
using System.Net.WebSockets;
+using System.Text;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
-using Microsoft.SemanticKernel.AI.TextCompletion;
-using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion;
-using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextCompletion;
-using Microsoft.SemanticKernel.Planners;
-using Microsoft.SemanticKernel.Planning;
-using Microsoft.SemanticKernel.Text;
+using Microsoft.SemanticKernel.ChatCompletion;
+using Microsoft.SemanticKernel.Connectors.OpenAI;
+using Microsoft.SemanticKernel.TextGeneration;
using MyIA.SemanticKernel.Connectors.AI.MultiConnector;
using MyIA.SemanticKernel.Connectors.AI.MultiConnector.Analysis;
using MyIA.SemanticKernel.Connectors.AI.MultiConnector.Configuration;
@@ -31,13 +30,24 @@ namespace SemanticKernel.IntegrationTests.Connectors.MultiConnector;
///
/// Integration tests for .
+///
+/// SK 1.78 migration: the legacy Plan planning model (Plan.FromJson,
+/// plan.State, plan.Steps, SequentialPlanner.CreatePlanAsync) was
+/// REMOVED. MultiTextCompletionSettings.ExecuteAsync now takes a
+/// , so each test's plan is rebuilt as a runtime
+/// that walks the resolved SK 1.78 plan
+/// () and invokes each resolved step against the
+/// on which the cost-offload system runs. The plan-as-KernelFunction
+/// pattern keeps the cost-offload contract intact while delegating step sequencing to
+/// the helper rather than the now-removed planners.
///
public sealed class MultiConnectorTests : IDisposable
{
private const string StartGoal =
"The goal of this plan is to evaluate the capabilities of a smaller LLM model. Start by writing a text of about 100 words on a given topic, as the input parameter of the plan. Then use distinct functions from the available skills on the input text and/or the previous functions results, choosing parameters in such a way that you know you will succeed at running each function but a smaller model might not. Try to propose steps of distinct difficulties so that models of distinct capabilities might succeed on some functions and fail on others. In a second phase, you will be asked to evaluate the function answers from smaller models. Please beware of correct Xml tags, attributes, and parameter names when defined and when reused.";
- private const string PlansDirectory = "../../../../../../samples/Plans/";
+ // SK 1.78: resolved plan JSON lives under samples/Plans/SK178/.
+ private const string PlansDirectory = "../../../../../../samples/Plans/SK178/";
private const string TextsDirectory = "../../../../../../samples/Texts/";
private readonly IConfigurationRoot _configuration;
@@ -100,25 +110,29 @@ public async Task ChatGptOffloadsToMultipleOobaboogaUsingFileAsync(bool succeeds
[InlineData(true, "TheBloke_LLaMA2-13B-Tiefighter-GGUF", 1, "Summarize_Topics_ElementAt.json", "Comm_hard.txt", "Danse_hard.txt", "SummarizeSkill", "MiscSkill")]
public async Task ChatGptOffloadsToSingleOobaboogaUsingFileAsync(bool succeedsOffloading, string completion, int nbTests, string planFile, string inputFile, string validationFile, params string[] skills)
{
- // Load the plan from the provided file path
+ // Resolve the SK 1.78 plan file path under samples/Plans/SK178/.
var planPath = System.IO.Path.Combine(this._planDirectory, planFile);
var textPath = System.IO.Path.Combine(this._textDirectory, inputFile);
var validationTextPath = System.IO.Path.Combine(this._textDirectory, validationFile);
- Func> planFactory = async (kernel, token, isValidation) =>
+ // SK 1.78: a Plan is now a KernelFunction. The planFactory returns a runtime KernelFunction that
+ // walks each resolved SK 1.78 invocation against the Kernel on which the cost-offload
+ // system runs. Auto Function Calling (FunctionChoiceBehavior.Auto) is the canonical
+ // replacement for the removed planners per the MS Learn migration guide
+ // (learn.microsoft.com/en-us/semantic-kernel/support/migration/stepwise-planner-migration-guide).
+ Func> planFactory = async (kernel, token, isValidation) =>
{
- var planJson = await System.IO.File.ReadAllTextAsync(planPath, token);
- var ctx = kernel.CreateNewContext();
- var plan = Plan.FromJson(planJson, ctx.Functions, true);
- var inputPath = textPath;
- if (isValidation)
- {
- inputPath = validationTextPath;
- }
+ var inputPath = isValidation ? validationTextPath : textPath;
+ var input = await System.IO.File.ReadAllTextAsync(inputPath, token).ConfigureAwait(false);
+
+ var (history, invocations) = await PlanJsonHelpers.BuildChatHistoryFromPlanJsonAsync(planPath, input, token).ConfigureAwait(false);
+
+ this._testOutputHelper.LogDebug(
+ "SK 1.78 plan resolved: {0} invocation(s); ChatHistory messages={1}",
+ invocations.Count,
+ history.Count);
- var input = await System.IO.File.ReadAllTextAsync(inputPath, token);
- plan.State.Update(input);
- return plan;
+ return BuildPlanAsKernelFunction(kernel, history, invocations);
};
List? modelNames = null;
@@ -130,57 +144,124 @@ public async Task ChatGptOffloadsToSingleOobaboogaUsingFileAsync(bool succeedsOf
await this.ChatGptOffloadsToOobaboogaAsync(succeedsOffloading, planFactory, modelNames, nbTests, skills).ConfigureAwait(false);
}
- // This test method uses the SequentialPlanner to create a plan based on difficulty
- //[Theory(Skip = "This test is for manual verification.")]
- [Theory(Skip = "This test is for manual verification.")]
- //[InlineData("", 1, "medium", "SummarizeSkill", "MiscSkill")]
- //[InlineData("TheBloke_StableBeluga-13B-GGML", 1, "medium", "SummarizeSkill", "MiscSkill")]
+ ///
+ /// SequentialPlanner was REMOVED in SK 1.78. We keep the same test slot for parity with the
+ /// pre-migration test matrix but mark it [Skip] — the planner-driven scenario is now covered by
+ /// the file-loaded plan path which is the canonical Auto Function Calling entry point
+ /// (). Reinstating a live planner test requires a
+ /// dedicated function-calling planner (tracked separately, epic #6853 follow-up).
+ ///
+ [Theory(Skip = "SequentialPlanner was removed in SK 1.78; use the file-loaded plan path instead.")]
[InlineData(true, "TheBloke_LLaMA2-13B-Tiefighter-GGUF", 1, "trivial", "Comm_simple.txt", "Danse_simple.txt", "WriterSkill", "MiscSkill")]
[InlineData(true, "TheBloke_LLaMA2-13B-Tiefighter-GGUF", 1, "medium", "Comm_simple.txt", "Danse_simple.txt", "WriterSkill", "MiscSkill")]
public async Task ChatGptOffloadsToOobaboogaUsingPlannerAsync(bool succeedsOffloading, string completionName, int nbPromptTests, string difficulty, string inputFile, string validationFile, params string[] skillNames)
{
- // Create a plan using SequentialPlanner based on difficulty
- var modifiedStartGoal = StartGoal.Replace("distinct difficulties", $"{difficulty} difficulties", StringComparison.OrdinalIgnoreCase);
- var textPath = System.IO.Path.Combine(this._textDirectory, inputFile);
- var validationTextPath = System.IO.Path.Combine(this._textDirectory, validationFile);
+ // SK 1.78: there is no SequentialPlanner. Keep the signature stable for the test matrix
+ // but short-circuit with an explanatory failure so that, if this test were ever unskipped
+ // (e.g. once a function-calling planner returns), the failure mode is clear.
+ throw new NotSupportedException(
+ "SequentialPlanner was removed in SK 1.78; rebuild this scenario via the file-loaded plan path " +
+ "which uses FunctionChoiceBehavior.Auto(). See docs/Plans/SK178-format.md and epic #6853.");
+ }
- Func> planFactory = async (kernel, token, isValidation) =>
+ ///
+ /// Builds a runtime that walks an SK 1.78 plan resolved by
+ /// . Each
+ /// is dispatched to the matching
+ /// already registered on the supplied ; outputs from one step become
+ /// inputs to the next per output_variable substitution. The plan-as-KernelFunction
+ /// pattern keeps happy (it now requires
+ /// a rather than the legacy Plan) while preserving the
+ /// cost-offload semantics the tests were written for.
+ ///
+ private static KernelFunction BuildPlanAsKernelFunction(Kernel kernel, ChatHistory history, IReadOnlyList invocations)
+ {
+ // SK 1.78: ChatHistory.SystemMessage was REMOVED — iterate the history to surface the
+ // first system-role message (mirrors the helper's emission order: one system message
+ // first, then the user message). This is the only place we read "the plan goal".
+ var systemContent = string.Empty;
+ foreach (var msg in history)
{
- var planner = new SequentialPlanner(kernel);
- var inputPath = textPath;
- if (isValidation)
+ if (msg.Role == AuthorRole.System)
{
- inputPath = validationTextPath;
+ systemContent = msg.Content ?? string.Empty;
+ break;
}
+ }
- var input = await System.IO.File.ReadAllTextAsync(inputPath, token);
- var plan = await planner.CreatePlanAsync(modifiedStartGoal, token);
-
- this._testOutputHelper.LogDebug("Plan proposed by primary connector:\n {0}\n", plan.ToJson(true));
+ var planName = string.IsNullOrWhiteSpace(systemContent)
+ ? "Sk178Plan"
+ : $"Sk178Plan:{systemContent}";
- plan.State.Update(input);
- var inputKey = "INPUT";
- var inputToken = "$INPUT";
- if (plan.Steps[0].Parameters[inputKey] != inputToken)
+ // Delegate returns Task: SK 1.78 KernelFunctionFromMethod auto-wraps
+ // Task in `new FunctionResult(function, value, kernel.Culture)`. No manual
+ // FunctionResult ctor needed.
+ return KernelFunctionFactory.CreateFromMethod(
+ method: async (KernelArguments args, CancellationToken ct) =>
{
- this._testOutputHelper.LogTrace("Fixed generated plan first param to variable text");
- plan.Steps[0].Parameters.Set(inputKey, inputToken);
- }
+ var buffer = new StringBuilder();
+ var userContent = history.LastOrDefault(m => m.Role == AuthorRole.User)?.Content ?? string.Empty;
+ var knownOutputs = new Dictionary(StringComparer.Ordinal)
+ {
+ ["INPUT"] = userContent,
+ };
+
+ foreach (var invocation in invocations)
+ {
+ var function = ResolveFunction(kernel, invocation);
+ if (function is null)
+ {
+ buffer.AppendLine($"[SK178] Skipped step '{invocation.FullyQualifiedName}' — function not registered on the kernel.");
+ continue;
+ }
+
+ var stepArgs = new KernelArguments();
+ foreach (var (key, value) in invocation.Arguments)
+ {
+ stepArgs[key] = value;
+ }
+
+ var stepResult = await kernel.InvokeAsync(function, stepArgs, ct).ConfigureAwait(false);
+ var stepValue = stepResult.GetValue