diff --git a/dotnet/samples/GettingStartedWithProcesses/Step06/Step06_FoundryAgentProcess.cs b/dotnet/samples/GettingStartedWithProcesses/Step06/Step06_FoundryAgentProcess.cs
deleted file mode 100644
index 7465fe5610c9..000000000000
--- a/dotnet/samples/GettingStartedWithProcesses/Step06/Step06_FoundryAgentProcess.cs
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.ClientModel;
-using System.Text;
-using Azure.AI.Agents.Persistent;
-using Azure.Identity;
-using Microsoft.SemanticKernel;
-using Microsoft.SemanticKernel.Agents.AzureAI;
-using Microsoft.SemanticKernel.Agents.OpenAI;
-using OpenAI;
-
-namespace Step06;
-public class Step06_FoundryAgentProcess : BaseTest
-{
- public Step06_FoundryAgentProcess(ITestOutputHelper output) : base(output, redirectSystemConsoleOutput: true)
- {
- this.Client =
- this.UseOpenAIConfig ?
- OpenAIAssistantAgent.CreateOpenAIClient(new ApiKeyCredential(this.ApiKey ?? throw new ConfigurationNotFoundException("OpenAI:ApiKey"))) :
- !string.IsNullOrWhiteSpace(this.ApiKey) ?
- OpenAIAssistantAgent.CreateAzureOpenAIClient(new ApiKeyCredential(this.ApiKey), new Uri(this.Endpoint!)) :
- OpenAIAssistantAgent.CreateAzureOpenAIClient(new AzureCliCredential(), new Uri(this.Endpoint!));
- }
-
- protected OpenAIClient Client { get; init; }
-
- // Target Open AI Services
- protected override bool ForceOpenAI => true;
-
- ///
- /// This example demonstrates how to create a process with two agents that can chat with each other. A student agent and a teacher agent are created.
- /// The process will keep track of the interaction count between the two agents.
- ///
- [Fact]
- public async Task ProcessWithTwoAgentMathChat()
- {
- var endpoint = TestConfiguration.AzureAI.Endpoint;
- PersistentAgentsClient client = new(endpoint.TrimEnd('/'), new DefaultAzureCredential(), new PersistentAgentsAdministrationClientOptions().WithPolicy(endpoint, "2025-05-15-preview"));
-
- Azure.Response? studentAgent = null;
- Azure.Response? teacherAgent = null;
-
- try
- {
- // Create the single agents
- studentAgent = await client.Administration.CreateAgentAsync(
- model: "gpt-4o",
- name: "Student",
- instructions: "You are a student that answer question from teacher, when teacher gives you question you answer them."
- );
-
- teacherAgent = await client.Administration.CreateAgentAsync(
- model: "gpt-4o",
- name: "Teacher",
- instructions: "You are a teacher that create pre-school math question for student and check answer.\nIf the answer is correct, you stop the conversation by saying [COMPLETE].\nIf the answer is wrong, you ask student to fix it."
- );
-
- // Define the process with a state type
- var processBuilder = new FoundryProcessBuilder("two_agent_math_chat");
-
- // Create a thread for the student
- processBuilder.AddThread("Student", KernelProcessThreadLifetime.Scoped);
- processBuilder.AddThread("Teacher", KernelProcessThreadLifetime.Scoped);
-
- // Add the student
- var student = processBuilder.AddStepFromAgent(studentAgent);
-
- // Add the teacher
- var teacher = processBuilder.AddStepFromAgent(teacherAgent);
-
- /**************************** Orchestrate ***************************/
-
- // When the process starts, activate the student agent
- processBuilder.OnProcessEnter().SendEventTo(
- student,
- thread: "_variables_.Student",
- messagesIn: ["_variables_.TeacherMessages"],
- inputs: new Dictionary { });
-
- // When the student agent exits, update the process state to save the student's messages and update interaction counts
- processBuilder.OnStepExit(student)
- .UpdateProcessState(path: "StudentMessages", operation: StateUpdateOperations.Set, value: "_agent_.messages_out");
-
- // When the student agent is finished, send the messages to the teacher agent
- processBuilder.OnEvent(student, "_default_")
- .SendEventTo(teacher, messagesIn: ["_variables_.StudentMessages"], thread: "Teacher");
-
- // When the teacher agent exits with a message containing '[COMPLETE]', update the process state to save the teacher's messages and update interaction counts and emit the `correct_answer` event
- processBuilder.OnStepExit(teacher, condition: "jmespath(contains(to_string(_agent_.messages_out), '[COMPLETE]'))")
- .EmitEvent(
- eventName: "correct_answer",
- payload: new Dictionary
- {
- { "Question", "_variables_.TeacherMessages" },
- { "Answer", "_variables_.StudentMessages" }
- })
- .UpdateProcessState(path: "_variables_.TeacherMessages", operation: StateUpdateOperations.Set, value: "_agent_.messages_out");
-
- // When the teacher agent exits with a message not containing '[COMPLETE]', update the process state to save the teacher's messages and update interaction counts
- processBuilder.OnStepExit(teacher, condition: "_default_")
- .UpdateProcessState(path: "_variables_.TeacherMessages", operation: StateUpdateOperations.Set, value: "_agent_.messages_out");
-
- // When the teacher agent is finished, send the messages to the student agent
- processBuilder.OnEvent(teacher, "_default_", condition: "_default_")
- .SendEventTo(student, messagesIn: ["_variables_.TeacherMessages"], thread: "Student");
-
- // When the teacher agent emits the `correct_answer` event, stop the process
- processBuilder.OnEvent(teacher, "correct_answer")
- .StopProcess();
-
- // Verify that the process can be built and serialized to json
- var processJson = await processBuilder.ToJsonAsync();
- Assert.NotEmpty(processJson);
-
- var content = await RunWorkflowAsync(client, processBuilder, [new(MessageRole.User, "Go")]);
- Assert.NotEmpty(content);
- }
- finally
- {
- // Clean up the agents
- await client.Administration.DeleteAgentAsync(studentAgent?.Value.Id);
- await client.Administration.DeleteAgentAsync(teacherAgent?.Value.Id);
- }
- }
-
- private async Task RunWorkflowAsync(PersistentAgentsClient client, FoundryProcessBuilder processBuilder, List? initialMessages = null) where T : class, new()
- {
- Workflow? workflow = null;
- StringBuilder output = new();
-
- try
- {
- // publish the workflow
- workflow = await client.Administration.Pipeline.PublishWorkflowAsync(processBuilder);
-
- // threadId is used to store the thread ID
- PersistentAgentThread thread = await client.Threads.CreateThreadAsync(messages: initialMessages ?? []);
-
- // create run
- await foreach (var run in client.Runs.CreateRunStreamingAsync(thread.Id, workflow.Id))
- {
- if (run is Azure.AI.Agents.Persistent.MessageContentUpdate contentUpdate)
- {
- output.Append(contentUpdate.Text);
- Console.Write(contentUpdate.Text);
- }
- else if (run is Azure.AI.Agents.Persistent.RunUpdate runUpdate)
- {
- if (runUpdate.UpdateKind == Azure.AI.Agents.Persistent.StreamingUpdateReason.RunInProgress && !runUpdate.Value.Id.StartsWith("wf_run", StringComparison.OrdinalIgnoreCase))
- {
- Console.WriteLine();
- Console.Write($"{runUpdate.Value.Metadata["x-agent-name"]}> ");
- }
- }
- }
-
- // delete thread, so we can start over
- Console.WriteLine($"\nDeleting thread {thread?.Id}...");
- await client.Threads.DeleteThreadAsync(thread?.Id);
- return output.ToString();
- }
- finally
- {
- // // delete workflow
- Console.WriteLine($"Deleting workflow {workflow?.Id}...");
- await client.Administration.Pipeline.DeleteWorkflowAsync(workflow!);
- }
- }
-
- ///
- /// Represents the state of the two-agent math chat process.
- ///
- public class TwoAgentMathState
- {
- public List StudentMessages { get; set; }
-
- public List TeacherMessages { get; set; }
-
- public StudentState StudentState { get; set; } = new();
-
- public int InteractionCount { get; set; }
- }
-
- ///
- /// Represents the state of the student agent.
- ///
- public class StudentState
- {
- public int InteractionCount { get; set; }
-
- public string Name { get; set; }
- }
-}
diff --git a/dotnet/src/Agents/AzureAI/Extensions/FoundryWorkflowExtensions.cs b/dotnet/src/Agents/AzureAI/Extensions/FoundryWorkflowExtensions.cs
deleted file mode 100644
index 4c02567c5a32..000000000000
--- a/dotnet/src/Agents/AzureAI/Extensions/FoundryWorkflowExtensions.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using Azure.AI.Agents.Persistent;
-using Azure.Core;
-
-namespace Microsoft.SemanticKernel.Agents.AzureAI;
-
-///
-/// Extensions for configuring the PersistentAgentsAdministrationClientOptions with a routing policy for Foundry Workflows.
-///
-public static class FoundryWorkflowExtensions
-{
- ///
- /// Adds a routing policy to the PersistentAgentsAdministrationClientOptions for Foundry Workflows.
- ///
- ///
- ///
- ///
- ///
- ///
- public static PersistentAgentsAdministrationClientOptions WithPolicy(this PersistentAgentsAdministrationClientOptions options, string endpoint, string apiVersion)
- {
- if (!Uri.TryCreate(endpoint, UriKind.Absolute, out var _endpoint))
- {
- throw new ArgumentException("The endpoint must be an absolute URI.", nameof(endpoint));
- }
-
- options.AddPolicy(new HttpPipelineRoutingPolicy(_endpoint, apiVersion), HttpPipelinePosition.PerCall);
-
- return options;
- }
-}
diff --git a/dotnet/src/Experimental/Process.Core/FoundryListenForBuilder.cs b/dotnet/src/Experimental/Process.Core/FoundryListenForBuilder.cs
deleted file mode 100644
index 3fe9bd25ed7c..000000000000
--- a/dotnet/src/Experimental/Process.Core/FoundryListenForBuilder.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Diagnostics.CodeAnalysis;
-using System.Threading.Tasks;
-using Microsoft.SemanticKernel.Process.Internal;
-
-namespace Microsoft.SemanticKernel;
-
-///
-/// Builder class for defining Processes that can be exported to Foundry.
-///
-[Experimental("SKEXP0081")]
-public class FoundryListenForBuilder
-{
- private readonly ProcessBuilder _processBuilder;
- private readonly ListenForBuilder _listenForBuilder;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The process builder.
- public FoundryListenForBuilder(ProcessBuilder processBuilder)
- {
- this._processBuilder = processBuilder;
- this._listenForBuilder = new ListenForBuilder(processBuilder);
- }
-
- ///
- /// Listens for an input event.
- ///
- ///
- ///
- ///
- public FoundryListenForTargetBuilder InputEvent(string eventName, KernelProcessEdgeCondition? condition = null)
- {
- return new(this._listenForBuilder.InputEvent(eventName, condition));
- }
-
- ///
- /// Defines a message to listen for from a specific process step.
- ///
- ///
- ///
- public FoundryListenForTargetBuilder ProcessStart(KernelProcessEdgeCondition? condition = null)
- {
- return this.InputEvent(ProcessConstants.Declarative.OnEnterEvent, condition);
- }
-
- ///
- /// Defines a message to listen for from a specific process step.
- ///
- /// The type of the message.
- /// The process step from which the message originates.
- /// Condition that must be met for the message to be processed
- /// A builder for defining the target of the message.
- public FoundryListenForTargetBuilder Message(string messageType, ProcessStepBuilder from, string? condition = null)
- {
- KernelProcessEdgeCondition? edgeCondition = null;
- if (!string.IsNullOrWhiteSpace(condition))
- {
- edgeCondition = new KernelProcessEdgeCondition(
- (e, s) =>
- {
- var wrapper = new DeclarativeConditionContentWrapper
- {
- State = s,
- Event = e.Data
- };
-
- var result = JMESPathConditionEvaluator.EvaluateCondition(wrapper, condition);
- return Task.FromResult(result);
- }, condition);
- }
-
- return new(this._listenForBuilder.Message(messageType, from, edgeCondition));
- }
-
- ///
- /// Defines a message to listen for from a specific process step.
- ///
- /// The process step from which the message originates.
- /// Condition that must be met for the message to be processed
- /// A builder for defining the target of the message.
- public FoundryListenForTargetBuilder ResultFrom(ProcessStepBuilder from, string? condition = null)
- {
- KernelProcessEdgeCondition? edgeCondition = null;
- if (!string.IsNullOrWhiteSpace(condition))
- {
- edgeCondition = new KernelProcessEdgeCondition(
- (e, s) =>
- {
- var wrapper = new DeclarativeConditionContentWrapper
- {
- State = s,
- Event = e.Data
- };
-
- var result = JMESPathConditionEvaluator.EvaluateCondition(wrapper, condition);
- return Task.FromResult(result);
- }, condition);
- }
-
- return new(this._listenForBuilder.OnResult(from, edgeCondition));
- }
-
- ///
- /// Listen for the OnEnter event from a specific process step.
- ///
- /// The process step from which the message originates.
- /// Condition that must be met for the message to be processed
- /// A builder for defining the target of the message.
- public FoundryListenForTargetBuilder OnEnter(ProcessStepBuilder from, string? condition = null)
- {
- return this.Message(ProcessConstants.Declarative.OnEnterEvent, from, condition);
- }
-
- ///
- /// Listen for the OnEnter event from a specific process step.
- ///
- /// The process step from which the message originates.
- /// Condition that must be met for the message to be processed
- /// A builder for defining the target of the message.
- public FoundryListenForTargetBuilder OnExit(ProcessStepBuilder from, string? condition = null)
- {
- return this.Message(ProcessConstants.Declarative.OnExitEvent, from, condition);
- }
-}
diff --git a/dotnet/src/Experimental/Process.Core/FoundryListenForTargetBuilder.cs b/dotnet/src/Experimental/Process.Core/FoundryListenForTargetBuilder.cs
deleted file mode 100644
index 3f5642e1df6b..000000000000
--- a/dotnet/src/Experimental/Process.Core/FoundryListenForTargetBuilder.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Microsoft.SemanticKernel;
-
-///
-/// Builder class for defining targets to listen for in a process.
-///
-[Experimental("SKEXP0081")]
-public class FoundryListenForTargetBuilder
-{
- private readonly ListenForTargetBuilder _listenForTargetBuilder;
-
- internal FoundryListenForTargetBuilder(ListenForTargetBuilder listenForTargetBuilder)
- {
- this._listenForTargetBuilder = listenForTargetBuilder;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The list of message sources.
- /// The process builder.
- /// The group ID for the message sources.
- public FoundryListenForTargetBuilder(List messageSources, ProcessBuilder processBuilder, KernelProcessEdgeGroupBuilder? edgeGroup = null)
- {
- this._listenForTargetBuilder = new ListenForTargetBuilder(messageSources, processBuilder, edgeGroup);
- }
-
- ///
- /// Signals that the output of the source step should be sent to the specified target when the associated event fires.
- ///
- /// The output target.
- /// The thread to send the event to.
- /// The inputs to the target.
- /// The messages to be sent to the target.
- /// A fresh builder instance for fluid definition
- public ProcessStepEdgeBuilder SendEventTo(ProcessAgentBuilder target, string? thread = null, Dictionary? inputs = null, List? messagesIn = null) where TProcessState : class, new()
- {
- return this._listenForTargetBuilder.SendEventTo_Internal(new ProcessAgentInvokeTargetBuilder(target, thread, messagesIn ?? [], inputs ?? []));
- }
-
- ///
- /// Signals that the specified event should be emitted.
- ///
- ///
- ///
- ///
- public FoundryListenForTargetBuilder EmitEvent(string eventName, Dictionary? payload = null)
- {
- return new(this._listenForTargetBuilder.EmitEvent(eventName, payload));
- }
-
- ///
- /// Signals that the specified state variable should be updated in the process state.
- ///
- ///
- ///
- ///
- ///
- public FoundryListenForTargetBuilder UpdateProcessState(string path, StateUpdateOperations operation, object? value)
- {
- return new(this._listenForTargetBuilder.UpdateProcessState(path, operation, value));
- }
-
- ///
- /// Signals that the process should be stopped.
- ///
- public void StopProcess(string? thread = null, Dictionary? inputs = null, List? messagesIn = null)
- {
- var target = new ProcessAgentInvokeTargetBuilder(EndStep.Instance, thread, messagesIn ?? [], inputs ?? []);
- this._listenForTargetBuilder.SendEventTo_Internal(target);
- }
-}
diff --git a/dotnet/src/Experimental/Process.Core/FoundryMessageSourceBuilder.cs b/dotnet/src/Experimental/Process.Core/FoundryMessageSourceBuilder.cs
deleted file mode 100644
index 25cf713711ae..000000000000
--- a/dotnet/src/Experimental/Process.Core/FoundryMessageSourceBuilder.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Diagnostics.CodeAnalysis;
-using System.Threading.Tasks;
-using Microsoft.SemanticKernel.Process.Internal;
-
-namespace Microsoft.SemanticKernel;
-
-///
-/// Builder class for defining message sources in a Foundry process.
-///
-[Experimental("SKEXP0081")]
-public class FoundryMessageSourceBuilder
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The meassage type
- /// The source step builder
- /// Condition that must be met for the message to be processed
- public FoundryMessageSourceBuilder(string messageType, ProcessStepBuilder source, string? condition)
- {
- this.MessageType = messageType;
- this.Source = source;
- this.Condition = condition;
- }
-
- ///
- /// The message type
- ///
- public string MessageType { get; }
-
- ///
- /// The source step builder.
- ///
- public ProcessStepBuilder Source { get; }
-
- ///
- /// The condition that must be met for the message to be processed.
- ///
- public string? Condition { get; }
-
- ///
- /// Builds the message source.
- ///
- ///
- internal MessageSourceBuilder Build()
- {
- KernelProcessEdgeCondition? edgeCondition = null;
- if (!string.IsNullOrWhiteSpace(this.Condition))
- {
- edgeCondition = new KernelProcessEdgeCondition(
- (e, s) =>
- {
- var wrapper = new DeclarativeConditionContentWrapper
- {
- State = s,
- Event = e.Data
- };
-
- var result = JMESPathConditionEvaluator.EvaluateCondition(wrapper, this.Condition);
- return Task.FromResult(result);
- });
- }
- return new MessageSourceBuilder(this.MessageType, this.Source, edgeCondition);
- }
-}
diff --git a/dotnet/src/Experimental/Process.Core/FoundryProcessBuilder.cs b/dotnet/src/Experimental/Process.Core/FoundryProcessBuilder.cs
deleted file mode 100644
index ea80d4d060b6..000000000000
--- a/dotnet/src/Experimental/Process.Core/FoundryProcessBuilder.cs
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Net.Http;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Azure.AI.Agents.Persistent;
-using Azure.Core;
-using Azure.Identity;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel.Agents.AzureAI;
-using Microsoft.SemanticKernel.Process.Models;
-
-namespace Microsoft.SemanticKernel;
-
-///
-/// A builder for creating a process that can be deployed to Azure Foundry.
-///
-[Experimental("SKEXP0081")]
-public class FoundryProcessBuilder where TProcessState : class, new()
-{
- private readonly ProcessBuilder _processBuilder;
- private static readonly string[] s_scopes = ["https://management.azure.com/"];
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The name of the process. This is required.
- /// The description of the Process.
- public FoundryProcessBuilder(string id, string? description = null)
- {
- this._processBuilder = new ProcessBuilder(id, description, processBuilder: null, typeof(TProcessState));
- }
-
- ///
- /// Adds an to the process.
- ///
- /// The name of the thread.
- /// The policy that determines the lifetime of the
- ///
- public ProcessBuilder AddThread(string threadName, KernelProcessThreadLifetime threadPolicy = KernelProcessThreadLifetime.Scoped)
- {
- return this._processBuilder.AddThread(threadName, threadPolicy);
- }
-
- ///
- /// Adds a step to the process from a declarative agent.
- ///
- /// The
- /// The unique Id of the step. If not provided, the name of the step Type will be used.
- /// Aliases that have been used by previous versions of the step, used for supporting backward compatibility when reading old version Process States
- /// Specifies the thread reference to be used by the agent. If not provided, the agent will create a new thread for each invocation.
- /// Specifies the human-in-the-loop mode for the agent. If not provided, the default is .
- public ProcessAgentBuilder AddStepFromAgent(AgentDefinition agentDefinition, string? stepId = null, IReadOnlyList? aliases = null, string? defaultThread = null, HITLMode humanInLoopMode = HITLMode.Never)
- {
- Verify.NotNull(agentDefinition);
- if (agentDefinition.Type != AzureAIAgentFactory.AzureAIAgentType)
- {
- throw new ArgumentException($"The agent type '{agentDefinition.Type}' is not supported. Only '{AzureAIAgentFactory.AzureAIAgentType}' is supported.");
- }
-
- return this._processBuilder.AddStepFromAgent(agentDefinition, stepId, aliases, defaultThread, humanInLoopMode);
- }
-
- ///
- /// Adds a step to the process from a .
- ///
- /// The
- /// The unique Id of the step. If not provided, the name of the step Type will be used.
- /// Aliases that have been used by previous versions of the step, used for supporting backward compatibility when reading old version Process States
- /// Specifies the thread reference to be used by the agent. If not provided, the agent will create a new thread for each invocation.
- /// Specifies the human-in-the-loop mode for the agent. If not provided, the default is .
- public ProcessAgentBuilder AddStepFromAgent(PersistentAgent persistentAgent, string? stepId = null, IReadOnlyList? aliases = null, string? defaultThread = null, HITLMode humanInLoopMode = HITLMode.Never)
- {
- Verify.NotNull(persistentAgent);
-
- var agentDefinition = new AgentDefinition
- {
- Id = persistentAgent.Id,
- Type = AzureAIAgentFactory.AzureAIAgentType,
- Name = persistentAgent.Name,
- Description = persistentAgent.Description
- };
-
- return this._processBuilder.AddStepFromAgent(agentDefinition, stepId, aliases, defaultThread, humanInLoopMode);
- }
-
- ///
- /// Adds a step to the process from a declarative agent.
- ///
- /// Id of the step. If not provided, the Id will come from the agent Id.
- /// The
- /// Specifies the thread reference to be used by the agent. If not provided, the agent will create a new thread for each invocation.
- /// Specifies the human-in-the-loop mode for the agent. If not provided, the default is .
- ///
- ///
- ///
- public ProcessAgentBuilder AddStepFromAgentProxy(string stepId, AgentDefinition agentDefinition, string? threadName = null, HITLMode humanInLoopMode = HITLMode.Never, IReadOnlyList? aliases = null) // TODO: Is there a better way to model this?
- {
- Verify.NotNullOrWhiteSpace(stepId);
- Verify.NotNull(agentDefinition);
- if (agentDefinition.Type != AzureAIAgentFactory.AzureAIAgentType)
- {
- throw new ArgumentException($"The agent type '{agentDefinition.Type}' is not supported. Only '{AzureAIAgentFactory.AzureAIAgentType}' is supported.");
- }
-
- return this._processBuilder.AddStepFromAgentProxy(agentDefinition, threadName, stepId, humanInLoopMode, aliases);
- }
-
- ///
- /// Provides an instance of for defining an input edge to a process.
- ///
- /// The Id of the external event.
- /// An instance of
- internal ProcessEdgeBuilder OnInputEvent(string eventId)
- {
- return this._processBuilder.OnInputEvent(eventId);
- }
-
- ///
- /// Creates a instance to define a listener for incoming messages.
- ///
- /// The process step from which the message originates.
- /// The name of the event to listen for.
- /// An optional condition using JMESPath syntax.
- ///
- public FoundryListenForTargetBuilder OnEvent(ProcessStepBuilder step, string eventName, string? condition = null)
- {
- Verify.NotNull(step);
- Verify.NotNullOrWhiteSpace(eventName);
- return new FoundryListenForBuilder(this._processBuilder).Message(eventName, step, condition);
- }
-
- ///
- /// Creates a instance to define a listener for when the process step is entered.
- ///
- ///
- ///
- ///
- public FoundryListenForTargetBuilder OnStepEnter(ProcessStepBuilder step, string? condition = null)
- {
- Verify.NotNull(step);
- return new FoundryListenForBuilder(this._processBuilder).OnEnter(step, condition);
- }
-
- ///
- /// Creates a instance to define a listener for when the process step is exited.
- ///
- ///
- ///
- ///
- public FoundryListenForTargetBuilder OnStepExit(ProcessStepBuilder step, string? condition = null)
- {
- Verify.NotNull(step);
- return new FoundryListenForBuilder(this._processBuilder).OnExit(step, condition);
- }
-
- ///
- /// Creates a instance to define a listener for when the process starts.
- ///
- ///
- public FoundryListenForTargetBuilder OnProcessEnter()
- {
- return new FoundryListenForBuilder(this._processBuilder).ProcessStart();
- }
-
- ///
- /// Builds the process.
- ///
- /// An instance of
- ///
- public KernelProcess Build(KernelProcessStateMetadata? stateMetadata = null)
- {
- return this._processBuilder.Build(stateMetadata);
- }
-
- ///
- /// Deploys the process to Azure Foundry.
- ///
- /// Th workflow endpoint to deploy to.
- /// The credential to use.
- ///
- ///
- public async Task DeployToFoundryAsync(string endpoint, TokenCredential? credential = null, CancellationToken cancellationToken = default)
- {
- // Build the process
- var process = this.Build();
-
- // Serialize and deploy
- using var httpClient = new HttpClient();
- if (credential != null)
- {
- var token = await credential.GetTokenAsync(new TokenRequestContext(s_scopes), cancellationToken).ConfigureAwait(false);
- httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.Token}");
- }
- else
- {
- var token = await new DefaultAzureCredential().GetTokenAsync(new TokenRequestContext(s_scopes), cancellationToken).ConfigureAwait(false);
- httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.Token}");
- }
-
- var workflow = await WorkflowBuilder.BuildWorkflow(process).ConfigureAwait(false);
- string json = WorkflowSerializer.SerializeToJson(workflow);
- using var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
- var response = await httpClient.PostAsync(new Uri($"{endpoint}/agents?api-version=2025-05-01-preview"), content, cancellationToken).ConfigureAwait(false);
-
- if (!response.IsSuccessStatusCode)
- {
- var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- throw new KernelException($"Failed to deploy process. Response: {errorContent}");
- }
-
- var responseContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- var foundryWorkflow = JsonSerializer.Deserialize(responseContent);
- return foundryWorkflow?.Id ?? throw new KernelException("Failed to parse the response from Foundry.");
- }
-
- ///
- /// Serializes the process to JSON.
- ///
- public async Task ToJsonAsync()
- {
- var process = this.Build();
- var workflow = await WorkflowBuilder.BuildWorkflow(process).ConfigureAwait(false);
- return WorkflowSerializer.SerializeToJson(workflow);
- }
-
- ///
- /// Serializes the process to YAML.
- ///
- public async Task ToYamlAsync()
- {
- var process = this.Build();
- var workflow = await WorkflowBuilder.BuildWorkflow(process).ConfigureAwait(false);
- return WorkflowSerializer.SerializeToYaml(workflow);
- }
-
- private class FoundryWorkflow
- {
- [JsonPropertyName("id")]
- public string? Id { get; set; }
- }
-}
-
-///
-/// A builder for creating a process that can be deployed to Azure Foundry.
-///
-public class FoundryProcessBuilder : FoundryProcessBuilder
-{
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- public FoundryProcessBuilder(string id) : base(id)
- {
- }
-}
-
-///
-/// A default process state for the .
-///
-public class FoundryProcessDefaultState
-{
-}
diff --git a/dotnet/src/Experimental/Process.Core/FoundryWorkflowExtensions.cs b/dotnet/src/Experimental/Process.Core/FoundryWorkflowExtensions.cs
deleted file mode 100644
index 15ca43c0c83a..000000000000
--- a/dotnet/src/Experimental/Process.Core/FoundryWorkflowExtensions.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.ClientModel.Primitives;
-using System.IO;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Azure.Core;
-using Azure.Core.Pipeline;
-
-namespace Microsoft.SemanticKernel.Agents.AzureAI;
-
-///
-/// Extensions for managing Foundry Workflows
-///
-public static class FoundryWorkflowExtensions
-{
- ///
- /// Publishes a workflow using a and a .
- ///
- /// The process state type.
- /// The client pipeline.
- /// The process builder.
- /// The published .
- public static async Task PublishWorkflowAsync(this ClientPipeline pipeline, FoundryProcessBuilder process) where T : class, new()
- {
- // Send the request
- using var message = pipeline.CreateMessage();
- var payload = await process.ToJsonAsync().ConfigureAwait(false);
- message.Request.Method = "POST";
- message.Request.Uri = new Uri("https://localhost/agents");
- message.Request.Content = System.ClientModel.BinaryContent.Create(new MemoryStream(Encoding.UTF8.GetBytes(payload)));
- message.Request.Headers.Add("Content-Type", "application/json");
-
- await pipeline.SendAsync(message).ConfigureAwait(false);
-
- if (message.Response?.Status < 200 || message.Response?.Status >= 300)
- {
- var errorContent = await message.Response.Content.AsJsonAsync().ConfigureAwait(false);
-
- throw new KernelException($"Error publishing workflow: {errorContent}");
- }
-
- var responseJson = await message.Response!.Content.AsJsonAsync().ConfigureAwait(false) ?? string.Empty;
-
- using var doc = JsonDocument.Parse(responseJson);
- var workflowId = doc.RootElement.GetProperty("id").GetString() ?? string.Empty;
-
- return new Workflow() { Id = workflowId };
- }
-
- ///
- /// Publishes a workflow using an and a .
- ///
- /// The process state type.
- /// The HTTP pipeline.
- /// The process builder.
- /// The published .
- public static async Task PublishWorkflowAsync(this HttpPipeline pipeline, FoundryProcessBuilder process) where T : class, new()
- {
- // Send the request
- using var message = pipeline.CreateMessage();
- message.Request.Method = RequestMethod.Post;
- message.Request.Uri.Reset(new Uri("https://localhost/agents"));
- message.Request.Content = RequestContent.Create(new MemoryStream(Encoding.UTF8.GetBytes(await process.ToJsonAsync().ConfigureAwait(false))));
- message.Request.Headers.Add("Content-Type", "application/json");
-
- await pipeline.SendAsync(message, default).ConfigureAwait(false);
-
- if (message.Response?.Status < 200 || message.Response?.Status >= 300)
- {
- var errorContent = await message.Response.Content.AsJsonAsync().ConfigureAwait(false);
-
- throw new KernelException($"Error publishing workflow: {errorContent}");
- }
-
- var responseJson = await message.Response!.Content.AsJsonAsync().ConfigureAwait(false) ?? string.Empty;
-
- using var doc = JsonDocument.Parse(responseJson);
- var workflowId = doc.RootElement.GetProperty("id").GetString() ?? string.Empty;
-
- Console.WriteLine($"Creating workflow {workflowId}...");
-
- return new Workflow() { Id = workflowId };
- }
-
- ///
- /// Deletes a workflow using a .
- ///
- /// The client pipeline.
- /// The workflow to delete.
- public static async Task DeleteWorkflowAsync(this ClientPipeline pipeline, Workflow workflow)
- {
- // Send the request
- using var message = pipeline.CreateMessage();
- message.Request.Method = "DELETE";
- message.Request.Uri = new Uri($"https://localhost/agents/{workflow.Id}");
-
- await pipeline.SendAsync(message).ConfigureAwait(false);
-
- if (message.Response?.Status < 200 || message.Response?.Status >= 300)
- {
- throw new KernelException($"Failed to delete workflow: {message.Response?.Status} {message.Response?.ReasonPhrase}");
- }
- }
-
- ///
- /// Deletes a workflow using an .
- ///
- /// The HTTP pipeline.
- /// The workflow to delete.
- public static async Task DeleteWorkflowAsync(this HttpPipeline pipeline, Workflow workflow)
- {
- // Send the request
- using var message = pipeline.CreateMessage();
- message.Request.Method = RequestMethod.Delete;
- message.Request.Uri.Reset(new Uri($"https://localhost/agents/{workflow.Id}"));
-
- await pipeline.SendAsync(message, default).ConfigureAwait(false);
-
- if (message.Response?.Status < 200 || message.Response?.Status >= 300)
- {
- throw new KernelException($"Failed to delete workflow: {message.Response?.Status} {message.Response?.ReasonPhrase}");
- }
- }
-
- ///
- /// Reads the as a JSON string asynchronously.
- ///
- /// The binary data.
- /// The JSON string.
- public static async Task AsJsonAsync(this BinaryData data)
- {
- if (data == null || data.Length == 0)
- {
- return string.Empty;
- }
-
- using var reader = new StreamReader(data.ToStream(), Encoding.UTF8);
-
- return await reader.ReadToEndAsync().ConfigureAwait(false);
- }
-}
diff --git a/dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs b/dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs
index 91e295cdff08..3a4b39f82751 100644
--- a/dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs
+++ b/dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs
@@ -240,7 +240,7 @@ internal ProcessFunctionTargetBuilder GetInvokeAgentFunctionTargetBuilder()
///
/// Builder for a process step that represents an agent.
///
-public class ProcessAgentBuilder : ProcessAgentBuilder
+public class ProcessAgentBuilder : ProcessAgentBuilder
{
///
/// Creates a new instance of the class.
diff --git a/dotnet/src/Experimental/Process.Core/ProcessExporter.cs b/dotnet/src/Experimental/Process.Core/ProcessExporter.cs
deleted file mode 100644
index bd95513a5923..000000000000
--- a/dotnet/src/Experimental/Process.Core/ProcessExporter.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Linq;
-using System.Text.Json;
-using YamlDotNet.Serialization;
-using YamlDotNet.Serialization.NamingConventions;
-
-namespace Microsoft.SemanticKernel.Process;
-
-///
-/// Export a process to a string representation.
-///
-public sealed class ProcessExporter
-{
- ///
- /// Export a process to a string representation.
- ///
- ///
- ///
- public static string ExportProcess(KernelProcess process)
- {
- Verify.NotNull(process);
-
- Workflow workflow = new()
- {
- Name = process.State.Name,
- Description = process.Description,
- FormatVersion = "1.0",
- WorkflowVersion = process.State.Version,
- Nodes = [.. process.Steps.Select(step => GetNodeFromStep(step))],
- // Orchestration
- // Suggested Inputs
- // Variables
- // Schema
- // Error handling
- };
-
- return "";
- }
-
- private static Node GetNodeFromStep(KernelProcessStepInfo stepInfo)
- {
- Verify.NotNull(stepInfo);
-
- if (stepInfo is KernelProcess)
- {
- throw new KernelException("Processes that contain a subprocess are not currently exportable.");
- }
- else if (stepInfo is KernelProcessAgentStep agentStep)
- {
- var agentNode = new Node()
- {
- Id = agentStep.State.Id ?? throw new KernelException("All steps must have an Id."),
- Description = agentStep.Description,
- Type = "agent",
- Inputs = agentStep.Inputs.ToDictionary((kvp) => kvp.Key, (kvp) =>
- {
- var value = kvp.Value;
- var schema = KernelJsonSchemaBuilder.Build(value);
- var schemaJson = JsonSerializer.Serialize(schema.RootElement);
-
- var deserializer = new DeserializerBuilder()
- .WithNamingConvention(UnderscoredNamingConvention.Instance)
- .IgnoreUnmatchedProperties()
- .Build();
-
- var yamlSchema = deserializer.Deserialize(schemaJson);
- if (yamlSchema is null)
- {
- throw new KernelException("Failed to deserialize schema.");
- }
-
- return yamlSchema;
- }),
- OnComplete = null, // TODO: OnComplete,
- OnError = null // TODO: OnError
- };
- }
- else if (stepInfo is KernelProcessMap mapStep)
- {
- throw new KernelException("Processes that contain a map step are not currently exportable.");
- }
- else if (stepInfo is KernelProcessProxy proxyStep)
- {
- throw new KernelException("Processes that contain a proxy step are not currently exportable.");
- }
- else
- {
- throw new KernelException("Processes that contain non Foundry-Agent step are not currently exportable.");
- }
-
- return new Node();
- }
-}
diff --git a/dotnet/src/Experimental/Process.UnitTests/Process.UnitTests.csproj b/dotnet/src/Experimental/Process.UnitTests/Process.UnitTests.csproj
index 2d53676bcbb6..566e3f5559aa 100644
--- a/dotnet/src/Experimental/Process.UnitTests/Process.UnitTests.csproj
+++ b/dotnet/src/Experimental/Process.UnitTests/Process.UnitTests.csproj
@@ -11,28 +11,6 @@
$(NoWarn);CA2007,CA1812,CA1861,CA1063,VSTHRD111,SKEXP0001,SKEXP0050,SKEXP0080,SKEXP0110;OPENAI001,CA1024
-
-
-
-
-
-
-
-
-
- Always
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
diff --git a/dotnet/src/Experimental/Process.UnitTests/ProcessSerializationTests.cs b/dotnet/src/Experimental/Process.UnitTests/ProcessSerializationTests.cs
deleted file mode 100644
index a073c47b2a13..000000000000
--- a/dotnet/src/Experimental/Process.UnitTests/ProcessSerializationTests.cs
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
-using Microsoft.SemanticKernel.Process.UnitTests.Steps;
-using Xunit;
-
-namespace Microsoft.SemanticKernel.Process.UnitTests;
-
-///
-/// Unit testing of .
-///
-public class ProcessSerializationTests
-{
- ///
- /// Verify initialization of .
- ///
- [Fact(Skip = "More work left to do.")]
- public async Task KernelProcessFromYamlWorksAsync()
- {
- // Arrange
- var yaml = this.ReadResource("workflow1.yaml");
-
- // Act
- var process = await ProcessBuilder.LoadFromYamlAsync(yaml);
-
- // Assert
- Assert.NotNull(process);
- }
-
- ///
- /// Verify initialization of from a YAML file that contains only a .NET workflow.
- ///
- ///
- [Fact]
- public async Task KernelProcessFromDotnetOnlyWorkflow1YamlAsync()
- {
- // Arrange
- var yaml = this.ReadResource("dotnetOnlyWorkflow1.yaml");
-
- // Act
- var process = await ProcessBuilder.LoadFromYamlAsync(yaml);
-
- // Assert
- Assert.NotNull(process);
-
- var stepKickoff = process.Steps.FirstOrDefault(s => s.State.Id == "kickoff");
- var stepA = process.Steps.FirstOrDefault(s => s.State.Id == "a_step");
- var stepB = process.Steps.FirstOrDefault(s => s.State.Id == "b_step");
- var stepC = process.Steps.FirstOrDefault(s => s.State.Id == "c_step");
-
- Assert.NotNull(stepKickoff);
- Assert.NotNull(stepA);
- Assert.NotNull(stepB);
- Assert.NotNull(stepC);
-
- // kickoff step has outgoing edge to aStep and bStep on event startAStep
- Assert.Single(stepKickoff.Edges);
- var kickoffStartEdges = stepKickoff.Edges["kickoff.StartARequested"];
- Assert.Equal(2, kickoffStartEdges.Count);
- Assert.Contains(kickoffStartEdges, e => (e.OutputTarget as KernelProcessFunctionTarget)!.StepId == "a_step");
- Assert.Contains(kickoffStartEdges, e => (e.OutputTarget as KernelProcessFunctionTarget)!.StepId == "b_step");
-
- // aStep and bStep have grouped outgoing edges to cStep on event aStepDone and bStepDone
- Assert.Single(stepA.Edges);
- var aStepDoneEdges = stepA.Edges["a_step.AStepDone"];
- Assert.Single(aStepDoneEdges);
- var aStepDoneEdge = aStepDoneEdges.First();
- Assert.Equal("c_step", (aStepDoneEdge.OutputTarget as KernelProcessFunctionTarget)!.StepId);
- Assert.NotEmpty(aStepDoneEdge.GroupId ?? "");
-
- Assert.Single(stepB.Edges);
- var bStepDoneEdges = stepB.Edges["b_step.BStepDone"];
- Assert.Single(bStepDoneEdges);
- var bStepDoneEdge = bStepDoneEdges.First();
- Assert.Equal("c_step", (bStepDoneEdge.OutputTarget as KernelProcessFunctionTarget)!.StepId);
- Assert.NotEmpty(bStepDoneEdge.GroupId ?? "");
-
- // cStep has outgoing edge to kickoff step on event cStepDone and one to end the process on event exitRequested
- Assert.Equal(2, stepC.Edges.Count);
- var cStepDoneEdges = stepC.Edges["c_step.CStepDone"];
- Assert.Single(cStepDoneEdges);
- var cStepDoneEdge = cStepDoneEdges.First();
- Assert.Equal("kickoff", (cStepDoneEdge.OutputTarget as KernelProcessFunctionTarget)!.StepId);
- Assert.Null(cStepDoneEdge.GroupId);
-
- var exitRequestedEdges = stepC.Edges["Microsoft.SemanticKernel.Process.EndStep"];
- Assert.Single(exitRequestedEdges);
- var exitRequestedEdge = exitRequestedEdges.First();
- Assert.Equal("Microsoft.SemanticKernel.Process.EndStep", (exitRequestedEdge.OutputTarget as KernelProcessFunctionTarget)!.StepId);
-
- // edges to cStep are in the same group
- Assert.Equal(aStepDoneEdge.GroupId, bStepDoneEdge.GroupId);
- }
-
- ///
- /// Verify initialization of from a YAML file that contains foundry_agents
- ///
- ///
- [Fact]
- public async Task KernelProcessFromScenario1YamlAsync()
- {
- // Arrange
- var yaml = this.ReadResource("scenario1.yaml");
- // Act
- var process = await ProcessBuilder.LoadFromYamlAsync(yaml);
- // Assert
- Assert.NotNull(process);
- }
-
- ///
- /// Verify that the process can be serialized to YAML and deserialized back to a workflow.
- ///
- ///
- [Fact]
- public async Task ProcessToWorkflowWorksAsync()
- {
- var process = this.GetProcess();
- var workflow = await WorkflowBuilder.BuildWorkflow(process);
- string yaml = WorkflowSerializer.SerializeToYaml(workflow);
-
- Assert.NotNull(workflow);
- }
-
- ///
- /// Verify initialization of from a YAML file that contains references to C# class and chat completion agent.
- ///
- [Fact]
- public async Task KernelProcessFromCombinedWorkflowYamlAsync()
- {
- // Arrange
- var yaml = this.ReadResource("combined-workflow.yaml");
-
- // Act
- var process = await ProcessBuilder.LoadFromYamlAsync(yaml);
-
- // Assert
- Assert.NotNull(process);
- Assert.Contains(process.Steps, step => step.State.Id == "GetProductInfo");
- Assert.Contains(process.Steps, step => step.State.Id == "Summarize");
- }
-
- private KernelProcess GetProcess()
- {
- // Create the process builder.
- ProcessBuilder processBuilder = new("ProcessWithDapr");
-
- // Add some steps to the process.
- var kickoffStep = processBuilder.AddStepFromType();
- var myAStep = processBuilder.AddStepFromType();
- var myBStep = processBuilder.AddStepFromType();
-
- // ########## Configuring initial state on steps in a process ###########
- // For demonstration purposes, we add the CStep and configure its initial state with a CurrentCycle of 1.
- // Initializing state in a step can be useful for when you need a step to start out with a predetermines
- // configuration that is not easily accomplished with dependency injection.
- var myCStep = processBuilder.AddStepFromType(initialState: new() { CurrentCycle = 1 });
-
- // Setup the input event that can trigger the process to run and specify which step and function it should be routed to.
- processBuilder
- .OnInputEvent(CommonEvents.StartProcess)
- .SendEventTo(new ProcessFunctionTargetBuilder(kickoffStep));
-
- // When the kickoff step is finished, trigger both AStep and BStep.
- kickoffStep
- .OnEvent(CommonEvents.StartARequested)
- .SendEventTo(new ProcessFunctionTargetBuilder(myAStep))
- .SendEventTo(new ProcessFunctionTargetBuilder(myBStep));
-
- processBuilder
- .ListenFor()
- .AllOf(new()
- {
- new(messageType: CommonEvents.AStepDone, source: myAStep),
- new(messageType: CommonEvents.BStepDone, source: myBStep)
- })
- .SendEventTo(new ProcessStepTargetBuilder(myCStep, inputMapping: (inputEvents) =>
- {
- // Map the input events to the CStep's input parameters.
- // In this case, we are mapping the output of AStep to the first input parameter of CStep
- // and the output of BStep to the second input parameter of CStep.
- return new()
- {
- { "astepdata", inputEvents[$"aStep.{CommonEvents.AStepDone}"] },
- { "bstepdata", inputEvents[$"bStep.{CommonEvents.BStepDone}"] }
- };
- }));
-
- // When CStep has finished without requesting an exit, activate the Kickoff step to start again.
- myCStep
- .OnEvent(CommonEvents.CStepDone)
- .SendEventTo(new ProcessFunctionTargetBuilder(kickoffStep));
-
- // When the CStep has finished by requesting an exit, stop the process.
- myCStep
- .OnEvent(CommonEvents.ExitRequested)
- .StopProcess();
-
- var process = processBuilder.Build();
- return process;
- }
-
- private string ReadResource(string name)
- {
- // Get the current assembly
- Assembly assembly = Assembly.GetExecutingAssembly();
-
- // Specify the resource name
- string resourceName = $"SemanticKernel.Process.UnitTests.Resources.{name}";
-
- // Get the resource stream
- using (Stream? resourceStream = assembly.GetManifestResourceStream(resourceName))
- {
- if (resourceStream != null)
- {
- using (StreamReader reader = new(resourceStream))
- {
- string content = reader.ReadToEnd();
- return content;
- }
- }
- else
- {
- throw new InvalidOperationException($"Resource {resourceName} not found in assembly {assembly.FullName}");
- }
- }
- }
-}
diff --git a/dotnet/src/Experimental/Process.UnitTests/Resources/combined-workflow.yaml b/dotnet/src/Experimental/Process.UnitTests/Resources/combined-workflow.yaml
deleted file mode 100644
index 5934688bab80..000000000000
--- a/dotnet/src/Experimental/Process.UnitTests/Resources/combined-workflow.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-workflow:
- id: combined_workflow
- name: ProductSummarization
- inputs:
- events:
- cloud_events:
- - type: input_message_received
- data_schema:
- type: string
- nodes:
- - id: GetProductInfo
- type: dotnet
- description: Gets product information
- agent:
- type: SemanticKernel.Process.UnitTests.Steps.ProductInfoProvider, SemanticKernel.Process.UnitTests
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: GetProductInfo.OnResult
- - id: Summarize
- type: declarative
- description: Summarizes the information
- agent:
- type: chat_completion_agent
- name: SummarizationAgent
- description: Summarizes the information
- instructions: Summarize the provided information in 3 sentences
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: ProcessCompleted
- orchestration:
- - listen_for:
- event: input_message_received
- from: _workflow_
- then:
- - node: GetProductInfo
- - listen_for:
- from: GetProductInfo
- event: GetProductInfo.OnResult
- then:
- - node: Summarize
diff --git a/dotnet/src/Experimental/Process.UnitTests/Resources/dotnetOnlyWorkflow1.yaml b/dotnet/src/Experimental/Process.UnitTests/Resources/dotnetOnlyWorkflow1.yaml
deleted file mode 100644
index 4b7466b80038..000000000000
--- a/dotnet/src/Experimental/Process.UnitTests/Resources/dotnetOnlyWorkflow1.yaml
+++ /dev/null
@@ -1,205 +0,0 @@
-id: dotnetOnlyWorkflow1
-format_version: "1.0" # The version of the declarative spec being used to define this workflow.
-workflow_version: "1.5" # The version of the workflow itself.
-name: report_generation_pipeline
-description: "A workflow that generates and publishes a report on a given topic."
-suggested_inputs:
- events:
- - type: "research_requested"
- payload:
- topic: "Create a report on AI agents at Microsoft."
-
-# Input that the workflow supports.
-# The way the events get sent to the workflow may differ depending on the platform. Some platforms may support sending events directly to the workflow,
-# while others may require using a chat completion interface similar to how local tool calls work.
-inputs: # The structured inputs supported by the workflow.
- events:
- cloud_events:
- - type: "StartRequested"
- data_schema:
- type: string
- - type: "StartARequested"
- data_schema:
- type: string
-
-# Schemas for the data types used in the workflow. These can be defined inline or referenced from an external schema.
-schemas:
- research_data:
- type: object
- properties:
- summary: { type: string }
- articles: { type: array, items: { type: string } }
- required: [summary, articles]
-
- draft:
- type: object
- properties:
- content: { type: string }
- word_count: { type: integer }
- required: [content, word_count]
-
- report_feedback:
- type: object
- properties:
- passed: { type: boolean }
- content: { type: string }
- feedback: { type: string }
- required: [passed, content, feedback]
-
- report:
- type: object
- properties:
- content: { type: string }
- approval_reason: { type: string }
- required: [content, approval_reason]
-
-# The nodes that make up the workflow. A node is a wrapper around something that can be invoked such as code, an agent, a tool, etc.
-nodes:
- - id: kickoff
- type: dotnet # dotnet | python
- version: "1.0"
- description: "Kickoff the workflow"
- agent:
- type: "Microsoft.SemanticKernel.Process.UnitTests.Steps.KickoffStep, SemanticKernel.Process.UnitTests"
- id: kickoff_agent
- inputs:
- input:
- type: string
- agent_input_mapping:
- topic: "inputs.input"
- on_complete:
- - on_condition:
- type: Eval
- expression: "results.articles.length > '0'"
- emits:
- - event_type: data_fetched
- schema:
- $ref: "#/workflow/schemas/research_data"
- payload: "$agent.outputs.results"
- - on_condition:
- type: default
- emits:
- - event_type: data_fetch_no_results
-
- - id: a_step
- type: dotnet
- version: "1.0"
- description: "A step"
- inputs:
- research_data:
- schema:
- $ref: "#/workflow/schemas/research_data"
- last_feedback:
- type: string
- agent:
- type: "Microsoft.SemanticKernel.Process.UnitTests.Steps.AStep, SemanticKernel.Process.UnitTests"
- id: a_step_agent
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: draft_created
- schema:
- $ref: "#/workflow/schemas/draft"
- payload: "$agent.outputs.draft"
-
- - id: b_step
- type: dotnet
- version: "1.0"
- description: "B Step"
- agent:
- type: "Microsoft.SemanticKernel.Process.UnitTests.Steps.BStep, SemanticKernel.Process.UnitTests"
- id: b_step_agent
- on_complete:
- - on_condition:
- type: eval
- expression: "report_feedback.passed == 'true'"
- emits:
- - event_type: report_approved
- schema:
- $ref: "#/workflow/schemas/report"
- payload:
- object:
- content: "$agent.outputs.report_feedback.content"
- approval_reason: "$agent.outputs.report_feedback.feedback"
- - on_condition:
- type: default
- emits:
- - event_type: report_rejected
- schema:
- $ref: "#/workflow/schemas/report_feedback"
- payload: "$agent.outputs.report_feedback"
- updates:
- - variable: revision_count
- operation: increment
- value: 1
- - variable: last_feedback
- operation: set
- value: "$agent.outputs.report_feedback.feedback"
-
- - id: c_step
- type: dotnet
- version: "1.0"
- description: "C Step"
- agent:
- type: "Microsoft.SemanticKernel.Process.UnitTests.Steps.CStep, SemanticKernel.Process.UnitTests"
- id: c_step_agent
- # inputs:
- # type: string
- agent_input_mapping:
- event_payload: "$.inputs.report"
- event_type: "human_approval_request"
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: human_approved
- schema:
- $ref: "#/workflow/schemas/report"
- updates:
- - variable: approved_report
- operation: set
- value: "$agent.outputs.report"
- - variable: $workflow.thread
- operation: set
- value: "$agent.outputs.report"
-
-# The orchestration of the workflow. This defines the sequence of events and actions that make up the workflow.
-orchestration:
-
- - listen_for:
- event: "StartRequested"
- from: _workflow_
- then:
- - node: kickoff
-
- - listen_for:
- event: "StartARequested"
- from: kickoff
- then:
- - node: a_step
- - node: b_step
-
- - listen_for:
- all_of:
- - event: "AStepDone"
- from: a_step
- - event: "BStepDone"
- from: b_step
- then:
- - node: c_step
- inputs:
- aStepData: a_step.AStepDone
- bStepData: b_step.BStepDone
-
- - listen_for:
- event: "CStepDone"
- from: c_step
- then:
- - node: kickoff
-
- - listen_for:
- event: "ExitRequested"
- from: c_step
- then:
- - node: End
diff --git a/dotnet/src/Experimental/Process.UnitTests/Resources/scenario1.yaml b/dotnet/src/Experimental/Process.UnitTests/Resources/scenario1.yaml
deleted file mode 100644
index 509ea58c479f..000000000000
--- a/dotnet/src/Experimental/Process.UnitTests/Resources/scenario1.yaml
+++ /dev/null
@@ -1,88 +0,0 @@
-id: two_agent_math_chat
-format_version: "1.0"
-name: student_teacher_chat
-description:
- A workflow that has student and teacher that does question answering
- about math
-inputs:
- messages:
- events:
- cloud_events:
- - type: "input_message_received"
- data_schema:
- type: string
-variables: {}
-schemas: {}
-nodes:
- - id: Student
- type: declarative
- version: "1.0"
- description: Solves problem
- agent:
- type: foundry_agent
- id: "{{student.id}}"
- name: "{{student.name}}"
- human_in_loop_mode: onNoMessage
- stream_output: true
- inputs:
- Question:
- type: messages
- on_invoke:
- on_error:
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: Answer
- schema:
- type: messages
- - id: Teacher
- type: declarative
- version: "1.0"
- description: Giving the problem
- agent:
- type: foundry_agent
- id: "{{teacher.id}}"
- name: "{{teacher.name}}"
- human_in_loop_mode: never
- stream_output: true
- inputs:
- Answer:
- type: messages
- on_invoke:
- on_error:
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: Question
- schema:
- type: messages
- - id: End
- type: declarative
- version: "1.0"
- description: Terminal State
-
-orchestration:
- - listen_for:
- event: input_message_received
- from: _workflow_
- then:
- - node: Student
- - listen_for:
- event: Answer
- from: Student
- then:
- - node: Teacher
- - listen_for:
- event: Question
- from: Teacher
- condition: Question.NotContains('[COMPLETE]')
- then:
- - node: Student
- - listen_for:
- event: Question
- from: Teacher
- condition: Question.Contains('[COMPLETE]')
- then:
- - node: End
diff --git a/dotnet/src/Experimental/Process.UnitTests/Resources/workflow1.yaml b/dotnet/src/Experimental/Process.UnitTests/Resources/workflow1.yaml
deleted file mode 100644
index 86591dbb46e3..000000000000
--- a/dotnet/src/Experimental/Process.UnitTests/Resources/workflow1.yaml
+++ /dev/null
@@ -1,396 +0,0 @@
-workflow:
- format_version: "1.0" # The version of the declarative spec being used to define this workflow.
- workflow_version: "1.5" # The version of the workflow itself.
- name: report_generation_pipeline
- description: "A workflow that generates and publishes a report on a given topic."
- suggested_inputs:
- events:
- - type: "research_requested"
- payload:
- topic: "Create a report on AI agents at Microsoft."
-
- # Input that the workflow supports.
- # The way the events get sent to the workflow may differ depending on the platform. Some platforms may support sending events directly to the workflow,
- # while others may require using a chat completion interface similar to how local tool calls work.
- inputs: # The structured inputs supported by the workflow.
- events:
- cloud_events:
- - type: "research_requested"
- data_schema:
- type: string
- filters: # optional filters on cloud event attributes
- - filter: "$.source == 'my_input_source'"
-
- # Variables used by the agents in the workflow. Variables can be defined as read-only or mutable.
- # Read-only variables are initialized with a default value and cannot be modified during the workflow execution.
- variables:
- max_retries:
- type: integer # defaults to mutable: false
- default: 3
- scope: "workflow"
- report_length_threshold:
- type: integer
- default: 500
- research_history:
- type: "chat_history" # defaults to scope: "run", should it be thread?
- is_mutable: true
- acls:
- - node: "researcher"
- access: "read"
- drafting_history:
- type: "chat_history"
- is_mutable: true
- research_memory:
- type: "memory"
- is_mutable: true
- drafting_memory:
- type: "memory"
- is_mutable: true
- drafting_whiteboard:
- type: "whiteboard"
- is_mutable: true
- revision_count:
- type: integer
- default: 0
- is_mutable: true
- last_feedback:
- type: string
- default: ""
- is_mutable: true
- approved_report:
- type: "string"
- is_mutable: true
-
- # Schemas for the data types used in the workflow. These can be defined inline or referenced from an external schema.
- schemas:
- research_data:
- type: object
- properties:
- summary: { type: string }
- articles: { type: array, items: { type: string } }
- required: [summary, articles]
-
- draft:
- type: object
- properties:
- content: { type: string }
- word_count: { type: integer }
- required: [content, word_count]
-
- report_feedback:
- type: object
- properties:
- passed: { type: boolean }
- content: { type: string }
- feedback: { type: string }
- required: [passed, content, feedback]
-
- report:
- type: object
- properties:
- content: { type: string }
- approval_reason: { type: string }
- required: [content, approval_reason]
-
- # The nodes that make up the workflow. A node is a wrapper around something that can be invoked such as code, an agent, a tool, etc.
- nodes:
- - id: fetch_data
- type: declarative
- version: "1.0"
- description: "Fetches relevant research data on the given topic."
- agent:
- type: foundry_agent
- id: research_agent
- # name: research_agent
- # description: "Find the most relevant articles and summarize key points ${{topic}}."
- # inputs:
- # topic:
- # type: string
- # outputs:
- # results:
- # $ref: "#/workflow/schemas/research_data"
- inputs:
- input:
- type: string
- agent_input_mapping:
- topic: "inputs.input"
- on_invoke: # mvp?
- emits:
- updates:
- on_error: # mvp?
- emits:
- updates:
- on_complete:
- - on_condition:
- type: state
- expression: "$agent.outputs.results.articles.length > 0" # json path or something standard, look at Azure pipelines, GH, etc.
- emits:
- - event_type: data_fetched
- schema:
- $ref: "#/workflow/schemas/research_data"
- payload: "$agent.outputs.results"
- - on_condition:
- type: default
- emits:
- - event_type: data_fetch_no_results
-
- - id: draft_report
- type: declarative
- version: "1.0"
- description: "Generates a draft report based on the research data."
- inputs:
- research_data:
- schema:
- $ref: "#/workflow/schemas/research_data"
- last_feedback:
- type: string
- agent:
- type: foundry_agent
- id: report_drafter
- # name: generate_draft
- # prompt: "Create a well-structured draft based on the given research data."
- # inputs:
- # research_data:
- # type: object
- # $ref: "#/workflow/schemas/research_data"
- # last_feedback:
- # type: string
- # outputs:
- # draft:
- # $ref: "#/workflow/schemas/draft"
- on_invoke: # mvp?
- emits:
- updates:
- on_error: # mvp?
- emits:
- updates:
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: draft_created
- schema:
- $ref: "#/workflow/schemas/draft"
- payload: "$agent.outputs.draft"
-
- - id: proofread_report
- type: declarative
- version: "1.0"
- description: "Proofreads the draft report for grammar, clarity, and factual accuracy."
- agent:
- type: foundry_agent
- id: proofreader
- # The agent is already deployed to Foundry and is only referenced here by Id.
- # The definition looks like this:
- # name: proofreader
- # prompt: "Review the draft for grammar, clarity, and factual accuracy."
- # inputs:
- # draft:
- # $ref: "#/workflow/schemas/draft"
- # output:
- # report_feedback:
- # $ref: "#/workflow/schemas/report_feedback"
- # tools:
- # ...
- inputs:
- draft:
- schema:
- $ref: "#/workflow/schemas/draft"
- agent_input_mapping:
- draft: "$.inputs.draft" # Should only be needed when mapping is not 1:1.
-
- on_invoke: # mvp?
- emits:
- updates:
- on_error: # mvp?
- emits:
- updates:
- on_complete:
- - on_condition:
- type: state # need to support structured and unstructured evaluation
- expression: "$agent.outputs.report_feedback.passed == true" # json path or something standard
- emits:
- - event_type: report_approved
- schema:
- $ref: "#/workflow/schemas/report"
- payload:
- object:
- content: "$agent.outputs.report_feedback.content"
- approval_reason: "$agent.outputs.report_feedback.feedback"
- - on_condition:
- type: default
- emits:
- - event_type: report_rejected
- schema:
- $ref: "#/workflow/schemas/report_feedback"
- payload: "$agent.outputs.report_feedback"
- updates: # discuss more with AK
- - variable: $.variables.revision_count
- operation: increment
- value: 1
- - variable: last_feedback
- operation: set
- value: "$agent.outputs.report_feedback.feedback"
-
- - id: human_review
- type: declarative
- version: "1.0"
- description: "Human reviewer for the final report."
- # Could be a pre-built agent template from the Foundry Catalog. The behavior is to
- # yield an event to the workflow and wait for a response before resuming.
- agent:
- type: foundry_agent
- id: built_in/yield_event
- # name: yield_event
- # inputs:
- # event_type:
- # type: string
- # event_payload:
- # type: object
- # output:
- # event_response:
- # type: object
- inputs:
- report:
- schema:
- $ref: "#/workflow/schemas/report"
- agent_input_mapping:
- event_payload: "$.inputs.report"
- event_type: "human_approval_request"
- on_invoke: # mvp?
- emits:
- updates:
- on_error: # mvp?
- emits:
- updates:
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: human_approved
- schema:
- $ref: "#/workflow/schemas/report"
- updates:
- - variable: approved_report
- operation: set
- value: "$agent.outputs.report"
- - variable: $workflow.thread
- operation: append
- value: "$agent.outputs.report"
-
- - id: error_handler
- type: declarative
- version: "1.0"
- description: "Handles errors that occur during the workflow."
- agent:
- type: foundry_agent
- id: built_in/yield_event
- # name: yield_event
- # inputs:
- # event_type:
- # type: string
- # event_payload:
- # type: object
- # output:
- # event_response:
- # type: object
- # schema:
- # $ref: "#/workflow/schemas/human_approval_response"
- inputs:
- error_details:
- type: object
- error_type:
- type: string
- agent_input_mapping:
- event_payload: "$.inputs.error"
- event_type: "$.inputs.error_type"
- on_invoke: # mvp?
- emits:
- updates:
- on_error: # mvp?
- emits:
- updates:
- on_complete:
- - on_condition:
- type: default
- emits:
- - event_type: human_approved
- schema:
- $ref: "#/workflow/schemas/report"
- updates: # append to the main thread to "output" the answer
- - variable: approved_report
- operation: set
- value: "$agent.outputs.report"
-
- # The orchestration of the workflow. This defines the sequence of events and actions that make up the workflow.
- orchestration:
-
- - listen_for:
- event: "research_requested"
- from: $.workflow
- then:
- - node: fetch_data
- inputs:
- input: $.event.payload
- last_feedback: ""
-
- - listen_for:
- event: "data_fetched"
- from: fetch_data
- then:
- - node: draft_report
- inputs:
- research_data: $.event.payload
-
- - listen_for:
- event: "draft_created"
- from: draft_report
- then:
- - node: proofread_report
- inputs:
- draft: $.event.payload
-
- - listen_for:
- event: "report_approved"
- from: proofread_report
- then:
- - node: human_review
- inputs: # input mapping for different entry points
- report: $.event.payload
-
- - listen_for:
- all_of: # Want to also support any_of - AK needs to figure out implementation
- - event: "report_approved"
- from: proofread_report
- - event: "human_approved"
- from: human_review
- then:
- - node: publish_report
- inputs:
- report: $.event.payload
-
- # The compatibility matrix for the workflow. This defines the compatibility of the workflow with different versions of itself.
- upgrade:
- - from_versions:
- min_version: "0.1"
- max_version_exclusive: "1.0"
- strategy: "not_compatible"
- - from_versions:
- min_version: "1.0"
- max_version_exclusive: "*"
- strategy: "backward_compatible"
-
- # The error handling for the workflow. This defines how errors are handled at different levels of the workflow.
- error_handling:
- on_error:
- - listen_for:
- event: "*_failed"
- then:
- - node: error_handler
- inputs:
- error_details: $.event.payload
- error_type: "unknown_error"
- default:
- - node: logging_service
- inputs:
- error_details: $.event.payload