diff --git a/Conductor/Api/IIntegrationResourceApi.cs b/Conductor/Api/IIntegrationResourceApi.cs index 6900cbb1..81316374 100644 --- a/Conductor/Api/IIntegrationResourceApi.cs +++ b/Conductor/Api/IIntegrationResourceApi.cs @@ -386,6 +386,16 @@ public interface IIntegrationResourceApi : IApiAccessor /// Task of Integration ThreadTask.Task GetIntegrationProviderAsync(string name); + /// + /// Get Integration provider definitions + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Task of List + ThreadTask.Task> GetIntegrationProviderDefsAsync(); + /// /// Get all Integrations Providers /// diff --git a/Conductor/Api/IntegrationResourceApi.cs b/Conductor/Api/IntegrationResourceApi.cs index 86a78e92..c946818f 100644 --- a/Conductor/Api/IntegrationResourceApi.cs +++ b/Conductor/Api/IntegrationResourceApi.cs @@ -1406,6 +1406,18 @@ public async System.Threading.Tasks.Task> GetIntegratio localVarResponse.Headers.ToDictionary(x => x.Name, x => string.Join(",", x.Value)), (Integration)this.Configuration.ApiClient.Deserialize(localVarResponse, typeof(Integration))); } + + /// + /// Get Integration provider definitions + /// + /// Thrown when fails to make API call + /// List + public List GetIntegrationProviderDefs() + { + ApiResponse> localVarResponse = GetIntegrationProviderDefsWithHttpInfo(); + return localVarResponse.Data; + } + /// /// Get Integration provider definitions /// @@ -1456,6 +1468,18 @@ public ApiResponse> GetIntegrationProviderDefsWithHttpInfo( (List)this.Configuration.ApiClient.Deserialize(localVarResponse, typeof(List))); } + /// + /// Get Integration provider definitions + /// + /// Thrown when fails to make API call + /// Task of List + public async System.Threading.Tasks.Task> GetIntegrationProviderDefsAsync() + { + ApiResponse> localVarResponse = await GetIntegrationProviderDefsAsyncWithHttpInfo(); + return localVarResponse.Data; + + } + /// /// Get Integration provider definitions /// diff --git a/Conductor/Client/Ai/Orchestrator.cs b/Conductor/Client/Ai/Orchestrator.cs index 56e2d78e..94d5dbc0 100644 --- a/Conductor/Client/Ai/Orchestrator.cs +++ b/Conductor/Client/Ai/Orchestrator.cs @@ -127,9 +127,10 @@ public string TestPromptTemplate(PromptTemplateTestRequest promptTemplateTestReq /// public void AddAIIntegration(string aiIntegrationName, LLMProviderEnum provider, List models, string description, IntegrationConfig config, bool overwrite = false) { + IntegrationUpdate details = null; try { - var details = new IntegrationUpdate(); + details = new IntegrationUpdate(); details.Configuration = config.ToDictionary(); details.Type = provider.ToString(); details.Category = IntegrationUpdate.CategoryEnum.AIMODEL; @@ -138,20 +139,20 @@ public void AddAIIntegration(string aiIntegrationName, LLMProviderEnum provider, var existingIntegration = _integrationResourceApi.GetIntegrationProvider(aiIntegrationName); if (existingIntegration == null || overwrite) _integrationResourceApi.SaveIntegrationProvider(details, aiIntegrationName); - foreach (var model in models) - { - var apiDetails = new IntegrationApiUpdate(); - apiDetails.Enabled = true; - apiDetails.Description = description; - var existingIntegrationApi = _integrationResourceApi.GetIntegrationApi(aiIntegrationName, model); - if (existingIntegrationApi == null || overwrite) - _integrationResourceApi.SaveIntegrationApi(apiDetails, model, aiIntegrationName); - } + SaveIntegrationApis(aiIntegrationName, models, description, overwrite); } catch (Exception ex) { - string errorMessage = string.Format(Constants.ADD_AI_INTEGRATION_ERROR_MESSAGE, ex.Message); - throw new Exception(errorMessage, ex); + if (ex.Message.Contains("404") && details != null) + { + _integrationResourceApi.SaveIntegrationProvider(details, aiIntegrationName); + SaveIntegrationApis(aiIntegrationName, models, description, overwrite); + } + else + { + string errorMessage = string.Format(Constants.ADD_AI_INTEGRATION_ERROR_MESSAGE, ex.Message); + throw new Exception(errorMessage, ex); + } } } @@ -233,5 +234,31 @@ public Dictionary GetTokenUsed(string aiIntegration) throw new Exception(errorMessage, ex); } } + + /// + /// Method to save IntegrationApi's + /// + /// + /// + /// + /// + private void SaveIntegrationApis(string aiIntegrationName, List models, string description, bool overwrite) + { + foreach (var model in models) + { + var apiDetails = new IntegrationApiUpdate + { + Enabled = true, + Description = description + }; + + var existingIntegrationApi = _integrationResourceApi.GetIntegrationApi(aiIntegrationName, model); + + if (existingIntegrationApi == null || overwrite) + { + _integrationResourceApi.SaveIntegrationApi(apiDetails, model, aiIntegrationName); + } + } + } } } \ No newline at end of file diff --git a/Conductor/Client/ApiClient.cs b/Conductor/Client/ApiClient.cs index e4875ec1..e6bbe06a 100644 --- a/Conductor/Client/ApiClient.cs +++ b/Conductor/Client/ApiClient.cs @@ -1,5 +1,3 @@ -using Conductor.Client.Models; -using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RestSharp; @@ -8,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net.Mime; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -118,6 +115,7 @@ private RestRequest PrepareRequest( String contentType) { var request = new RestRequest(path, method); + request.AddHeader("Accept-Encoding", "gzip"); // add path parameter, if any foreach (var param in pathParams) diff --git a/Conductor/Client/Models/StateChangeConfig.cs b/Conductor/Client/Models/StateChangeConfig.cs new file mode 100644 index 00000000..a44a94b1 --- /dev/null +++ b/Conductor/Client/Models/StateChangeConfig.cs @@ -0,0 +1,93 @@ +using Newtonsoft.Json.Converters; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace Conductor.Client.Models +{ + public class StateChangeConfig + { + /// + /// Gets or sets Type + /// + public List Type { get; set; } + + /// + /// Gets or sets Events + /// + public List Events { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public StateChangeConfig(List eventType = null, List events = null) + { + Type = eventType; + Events = events; + } + } +} + +/// +/// Defines StateChangeEventType +/// + +[JsonConverter(typeof(StringEnumConverter))] +public enum StateChangeEventType +{ + /// + /// Enum OnScheduled for value: onScheduled + /// + [EnumMember(Value = "onScheduled")] + OnScheduled = 0, + + /// + /// Enum OnStart for value: onStart + /// + [EnumMember(Value = "onStart")] + OnStart = 1, + + /// + /// Enum OnFailed for value: onFailed + /// + [EnumMember(Value = "onFailed")] + OnFailed = 2, + + /// + /// Enum OnSuccess for value: onSuccess + /// + [EnumMember(Value = "onSuccess")] + OnSuccess = 3, + + /// + /// Enum OnCancelled for value: onCancelled + /// + [EnumMember(Value = "onCancelled")] + OnCancelled = 4 +} + +public class StateChangeEvent +{ + /// + /// Gets or sets Type + /// + public string Type { get; set; } + + /// + /// Gets or sets Payload + /// + public Dictionary Payload { get; set; } + + /// + /// Initializes a new instance of the class + /// + /// + /// + public StateChangeEvent(string type, Dictionary payload) + { + Type = type; + Payload = payload; + } +} \ No newline at end of file diff --git a/Conductor/Client/Models/Workflow.cs b/Conductor/Client/Models/Workflow.cs index bafe8119..ba889a67 100644 --- a/Conductor/Client/Models/Workflow.cs +++ b/Conductor/Client/Models/Workflow.cs @@ -1,12 +1,12 @@ -using System.Linq; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; -using System.Text; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; namespace Conductor.Client.Models { @@ -292,34 +292,34 @@ public override string ToString() { var sb = new StringBuilder(); sb.Append("class Workflow {\n"); - sb.Append(" CorrelationId: ").Append(CorrelationId).Append("\n"); - sb.Append(" CreateTime: ").Append(CreateTime).Append("\n"); - sb.Append(" CreatedBy: ").Append(CreatedBy).Append("\n"); - sb.Append(" EndTime: ").Append(EndTime).Append("\n"); - sb.Append(" _Event: ").Append(_Event).Append("\n"); - sb.Append(" ExternalInputPayloadStoragePath: ").Append(ExternalInputPayloadStoragePath).Append("\n"); - sb.Append(" ExternalOutputPayloadStoragePath: ").Append(ExternalOutputPayloadStoragePath).Append("\n"); - sb.Append(" FailedReferenceTaskNames: ").Append(FailedReferenceTaskNames).Append("\n"); - sb.Append(" Input: ").Append(Input).Append("\n"); - sb.Append(" LastRetriedTime: ").Append(LastRetriedTime).Append("\n"); - sb.Append(" Output: ").Append(Output).Append("\n"); - sb.Append(" OwnerApp: ").Append(OwnerApp).Append("\n"); - sb.Append(" ParentWorkflowId: ").Append(ParentWorkflowId).Append("\n"); - sb.Append(" ParentWorkflowTaskId: ").Append(ParentWorkflowTaskId).Append("\n"); - sb.Append(" Priority: ").Append(Priority).Append("\n"); - sb.Append(" ReRunFromWorkflowId: ").Append(ReRunFromWorkflowId).Append("\n"); - sb.Append(" ReasonForIncompletion: ").Append(ReasonForIncompletion).Append("\n"); - sb.Append(" StartTime: ").Append(StartTime).Append("\n"); - sb.Append(" Status: ").Append(Status).Append("\n"); - sb.Append(" TaskToDomain: ").Append(TaskToDomain).Append("\n"); - sb.Append(" Tasks: ").Append(Tasks).Append("\n"); - sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n"); - sb.Append(" UpdatedBy: ").Append(UpdatedBy).Append("\n"); - sb.Append(" Variables: ").Append(Variables).Append("\n"); - sb.Append(" WorkflowDefinition: ").Append(WorkflowDefinition).Append("\n"); - sb.Append(" WorkflowId: ").Append(WorkflowId).Append("\n"); - sb.Append(" WorkflowName: ").Append(WorkflowName).Append("\n"); - sb.Append(" WorkflowVersion: ").Append(WorkflowVersion).Append("\n"); + sb.Append(" CorrelationId: ").Append(CorrelationId).Append("\n"); + sb.Append(" CreateTime: ").Append(CreateTime).Append("\n"); + sb.Append(" CreatedBy: ").Append(CreatedBy).Append("\n"); + sb.Append(" EndTime: ").Append(EndTime).Append("\n"); + sb.Append(" _Event: ").Append(_Event).Append("\n"); + sb.Append(" ExternalInputPayloadStoragePath: ").Append(ExternalInputPayloadStoragePath).Append("\n"); + sb.Append(" ExternalOutputPayloadStoragePath: ").Append(ExternalOutputPayloadStoragePath).Append("\n"); + sb.Append(" FailedReferenceTaskNames: ").Append(FailedReferenceTaskNames).Append("\n"); + sb.Append(" Input: ").Append(Input).Append("\n"); + sb.Append(" LastRetriedTime: ").Append(LastRetriedTime).Append("\n"); + sb.Append(" Output: ").Append(Output).Append("\n"); + sb.Append(" OwnerApp: ").Append(OwnerApp).Append("\n"); + sb.Append(" ParentWorkflowId: ").Append(ParentWorkflowId).Append("\n"); + sb.Append(" ParentWorkflowTaskId: ").Append(ParentWorkflowTaskId).Append("\n"); + sb.Append(" Priority: ").Append(Priority).Append("\n"); + sb.Append(" ReRunFromWorkflowId: ").Append(ReRunFromWorkflowId).Append("\n"); + sb.Append(" ReasonForIncompletion: ").Append(ReasonForIncompletion).Append("\n"); + sb.Append(" StartTime: ").Append(StartTime).Append("\n"); + sb.Append(" Status: ").Append(Status).Append("\n"); + sb.Append(" TaskToDomain: ").Append(TaskToDomain).Append("\n"); + sb.Append(" Tasks: ").Append(Tasks).Append("\n"); + sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n"); + sb.Append(" UpdatedBy: ").Append(UpdatedBy).Append("\n"); + sb.Append(" Variables: ").Append(Variables).Append("\n"); + sb.Append(" WorkflowDefinition: ").Append(WorkflowDefinition).Append("\n"); + sb.Append(" WorkflowId: ").Append(WorkflowId).Append("\n"); + sb.Append(" WorkflowName: ").Append(WorkflowName).Append("\n"); + sb.Append(" WorkflowVersion: ").Append(WorkflowVersion).Append("\n"); sb.Append("}\n"); return sb.ToString(); } @@ -354,152 +354,152 @@ public bool Equals(Workflow input) return false; return - ( - this.CorrelationId == input.CorrelationId || - (this.CorrelationId != null && - this.CorrelationId.Equals(input.CorrelationId)) - ) && - ( - this.CreateTime == input.CreateTime || - (this.CreateTime != null && - this.CreateTime.Equals(input.CreateTime)) - ) && - ( - this.CreatedBy == input.CreatedBy || - (this.CreatedBy != null && - this.CreatedBy.Equals(input.CreatedBy)) - ) && - ( - this.EndTime == input.EndTime || - (this.EndTime != null && - this.EndTime.Equals(input.EndTime)) - ) && - ( - this._Event == input._Event || - (this._Event != null && - this._Event.Equals(input._Event)) - ) && - ( - this.ExternalInputPayloadStoragePath == input.ExternalInputPayloadStoragePath || - (this.ExternalInputPayloadStoragePath != null && - this.ExternalInputPayloadStoragePath.Equals(input.ExternalInputPayloadStoragePath)) - ) && - ( - this.ExternalOutputPayloadStoragePath == input.ExternalOutputPayloadStoragePath || - (this.ExternalOutputPayloadStoragePath != null && - this.ExternalOutputPayloadStoragePath.Equals(input.ExternalOutputPayloadStoragePath)) - ) && - ( - this.FailedReferenceTaskNames == input.FailedReferenceTaskNames || - this.FailedReferenceTaskNames != null && - input.FailedReferenceTaskNames != null && - this.FailedReferenceTaskNames.SequenceEqual(input.FailedReferenceTaskNames) - ) && - ( - this.Input == input.Input || - this.Input != null && - input.Input != null && - this.Input.SequenceEqual(input.Input) - ) && - ( - this.LastRetriedTime == input.LastRetriedTime || - (this.LastRetriedTime != null && - this.LastRetriedTime.Equals(input.LastRetriedTime)) - ) && - ( - this.Output == input.Output || - this.Output != null && - input.Output != null && - this.Output.SequenceEqual(input.Output) - ) && - ( - this.OwnerApp == input.OwnerApp || - (this.OwnerApp != null && - this.OwnerApp.Equals(input.OwnerApp)) - ) && - ( - this.ParentWorkflowId == input.ParentWorkflowId || - (this.ParentWorkflowId != null && - this.ParentWorkflowId.Equals(input.ParentWorkflowId)) - ) && - ( - this.ParentWorkflowTaskId == input.ParentWorkflowTaskId || - (this.ParentWorkflowTaskId != null && - this.ParentWorkflowTaskId.Equals(input.ParentWorkflowTaskId)) - ) && - ( - this.Priority == input.Priority || - (this.Priority != null && - this.Priority.Equals(input.Priority)) - ) && - ( - this.ReRunFromWorkflowId == input.ReRunFromWorkflowId || - (this.ReRunFromWorkflowId != null && - this.ReRunFromWorkflowId.Equals(input.ReRunFromWorkflowId)) - ) && - ( - this.ReasonForIncompletion == input.ReasonForIncompletion || - (this.ReasonForIncompletion != null && - this.ReasonForIncompletion.Equals(input.ReasonForIncompletion)) - ) && - ( - this.StartTime == input.StartTime || - (this.StartTime != null && - this.StartTime.Equals(input.StartTime)) - ) && - ( - this.Status == input.Status || - (this.Status != null && - this.Status.Equals(input.Status)) - ) && - ( - this.TaskToDomain == input.TaskToDomain || - this.TaskToDomain != null && - input.TaskToDomain != null && - this.TaskToDomain.SequenceEqual(input.TaskToDomain) - ) && - ( - this.Tasks == input.Tasks || - this.Tasks != null && - input.Tasks != null && - this.Tasks.SequenceEqual(input.Tasks) - ) && - ( - this.UpdateTime == input.UpdateTime || - (this.UpdateTime != null && - this.UpdateTime.Equals(input.UpdateTime)) - ) && - ( - this.UpdatedBy == input.UpdatedBy || - (this.UpdatedBy != null && - this.UpdatedBy.Equals(input.UpdatedBy)) - ) && - ( - this.Variables == input.Variables || - this.Variables != null && - input.Variables != null && - this.Variables.SequenceEqual(input.Variables) - ) && - ( - this.WorkflowDefinition == input.WorkflowDefinition || - (this.WorkflowDefinition != null && - this.WorkflowDefinition.Equals(input.WorkflowDefinition)) - ) && - ( - this.WorkflowId == input.WorkflowId || - (this.WorkflowId != null && - this.WorkflowId.Equals(input.WorkflowId)) - ) && - ( - this.WorkflowName == input.WorkflowName || - (this.WorkflowName != null && - this.WorkflowName.Equals(input.WorkflowName)) - ) && - ( - this.WorkflowVersion == input.WorkflowVersion || - (this.WorkflowVersion != null && - this.WorkflowVersion.Equals(input.WorkflowVersion)) - ); + ( + this.CorrelationId == input.CorrelationId || + (this.CorrelationId != null && + this.CorrelationId.Equals(input.CorrelationId)) + ) && + ( + this.CreateTime == input.CreateTime || + (this.CreateTime != null && + this.CreateTime.Equals(input.CreateTime)) + ) && + ( + this.CreatedBy == input.CreatedBy || + (this.CreatedBy != null && + this.CreatedBy.Equals(input.CreatedBy)) + ) && + ( + this.EndTime == input.EndTime || + (this.EndTime != null && + this.EndTime.Equals(input.EndTime)) + ) && + ( + this._Event == input._Event || + (this._Event != null && + this._Event.Equals(input._Event)) + ) && + ( + this.ExternalInputPayloadStoragePath == input.ExternalInputPayloadStoragePath || + (this.ExternalInputPayloadStoragePath != null && + this.ExternalInputPayloadStoragePath.Equals(input.ExternalInputPayloadStoragePath)) + ) && + ( + this.ExternalOutputPayloadStoragePath == input.ExternalOutputPayloadStoragePath || + (this.ExternalOutputPayloadStoragePath != null && + this.ExternalOutputPayloadStoragePath.Equals(input.ExternalOutputPayloadStoragePath)) + ) && + ( + this.FailedReferenceTaskNames == input.FailedReferenceTaskNames || + this.FailedReferenceTaskNames != null && + input.FailedReferenceTaskNames != null && + this.FailedReferenceTaskNames.SequenceEqual(input.FailedReferenceTaskNames) + ) && + ( + this.Input == input.Input || + this.Input != null && + input.Input != null && + this.Input.SequenceEqual(input.Input) + ) && + ( + this.LastRetriedTime == input.LastRetriedTime || + (this.LastRetriedTime != null && + this.LastRetriedTime.Equals(input.LastRetriedTime)) + ) && + ( + this.Output == input.Output || + this.Output != null && + input.Output != null && + this.Output.SequenceEqual(input.Output) + ) && + ( + this.OwnerApp == input.OwnerApp || + (this.OwnerApp != null && + this.OwnerApp.Equals(input.OwnerApp)) + ) && + ( + this.ParentWorkflowId == input.ParentWorkflowId || + (this.ParentWorkflowId != null && + this.ParentWorkflowId.Equals(input.ParentWorkflowId)) + ) && + ( + this.ParentWorkflowTaskId == input.ParentWorkflowTaskId || + (this.ParentWorkflowTaskId != null && + this.ParentWorkflowTaskId.Equals(input.ParentWorkflowTaskId)) + ) && + ( + this.Priority == input.Priority || + (this.Priority != null && + this.Priority.Equals(input.Priority)) + ) && + ( + this.ReRunFromWorkflowId == input.ReRunFromWorkflowId || + (this.ReRunFromWorkflowId != null && + this.ReRunFromWorkflowId.Equals(input.ReRunFromWorkflowId)) + ) && + ( + this.ReasonForIncompletion == input.ReasonForIncompletion || + (this.ReasonForIncompletion != null && + this.ReasonForIncompletion.Equals(input.ReasonForIncompletion)) + ) && + ( + this.StartTime == input.StartTime || + (this.StartTime != null && + this.StartTime.Equals(input.StartTime)) + ) && + ( + this.Status == input.Status || + (this.Status != null && + this.Status.Equals(input.Status)) + ) && + ( + this.TaskToDomain == input.TaskToDomain || + this.TaskToDomain != null && + input.TaskToDomain != null && + this.TaskToDomain.SequenceEqual(input.TaskToDomain) + ) && + ( + this.Tasks == input.Tasks || + this.Tasks != null && + input.Tasks != null && + this.Tasks.SequenceEqual(input.Tasks) + ) && + ( + this.UpdateTime == input.UpdateTime || + (this.UpdateTime != null && + this.UpdateTime.Equals(input.UpdateTime)) + ) && + ( + this.UpdatedBy == input.UpdatedBy || + (this.UpdatedBy != null && + this.UpdatedBy.Equals(input.UpdatedBy)) + ) && + ( + this.Variables == input.Variables || + this.Variables != null && + input.Variables != null && + this.Variables.SequenceEqual(input.Variables) + ) && + ( + this.WorkflowDefinition == input.WorkflowDefinition || + (this.WorkflowDefinition != null && + this.WorkflowDefinition.Equals(input.WorkflowDefinition)) + ) && + ( + this.WorkflowId == input.WorkflowId || + (this.WorkflowId != null && + this.WorkflowId.Equals(input.WorkflowId)) + ) && + ( + this.WorkflowName == input.WorkflowName || + (this.WorkflowName != null && + this.WorkflowName.Equals(input.WorkflowName)) + ) && + ( + this.WorkflowVersion == input.WorkflowVersion || + (this.WorkflowVersion != null && + this.WorkflowVersion.Equals(input.WorkflowVersion)) + ); } /// @@ -580,5 +580,35 @@ public override int GetHashCode() { yield break; } + + /// + /// Returns the task + /// + /// + /// + /// + /// + public Task GetTask(string name = null, string taskReferenceName = null) + { + if (name == null && taskReferenceName == null) + { + throw new Exception("ONLY one of name or taskReferenceName MUST be provided. None were provided"); + } + if (name != null && taskReferenceName != null) + { + throw new Exception("ONLY one of name or taskReferenceName MUST be provided. Both were provided"); + } + + Task current = null; + foreach (var task in Tasks) + { + if (task.TaskDefName == name || task.WorkflowTask.TaskReferenceName == taskReferenceName) + { + current = task; + break; + } + } + return current; + } } } diff --git a/Conductor/Client/Models/WorkflowDef.cs b/Conductor/Client/Models/WorkflowDef.cs index 62f8941d..e47d0920 100644 --- a/Conductor/Client/Models/WorkflowDef.cs +++ b/Conductor/Client/Models/WorkflowDef.cs @@ -1,12 +1,12 @@ -using System.Linq; -using System.IO; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; -using System.Text; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; namespace Conductor.Client.Models { @@ -232,26 +232,26 @@ public override string ToString() { var sb = new StringBuilder(); sb.Append("class WorkflowDef {\n"); - sb.Append(" CreateTime: ").Append(CreateTime).Append("\n"); - sb.Append(" CreatedBy: ").Append(CreatedBy).Append("\n"); - sb.Append(" Description: ").Append(Description).Append("\n"); - sb.Append(" FailureWorkflow: ").Append(FailureWorkflow).Append("\n"); - sb.Append(" InputParameters: ").Append(InputParameters).Append("\n"); - sb.Append(" InputTemplate: ").Append(InputTemplate).Append("\n"); - sb.Append(" Name: ").Append(Name).Append("\n"); - sb.Append(" OutputParameters: ").Append(OutputParameters).Append("\n"); - sb.Append(" OwnerApp: ").Append(OwnerApp).Append("\n"); - sb.Append(" OwnerEmail: ").Append(OwnerEmail).Append("\n"); - sb.Append(" Restartable: ").Append(Restartable).Append("\n"); - sb.Append(" SchemaVersion: ").Append(SchemaVersion).Append("\n"); - sb.Append(" Tasks: ").Append(Tasks).Append("\n"); - sb.Append(" TimeoutPolicy: ").Append(TimeoutPolicy).Append("\n"); - sb.Append(" TimeoutSeconds: ").Append(TimeoutSeconds).Append("\n"); - sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n"); - sb.Append(" UpdatedBy: ").Append(UpdatedBy).Append("\n"); - sb.Append(" Variables: ").Append(Variables).Append("\n"); - sb.Append(" Version: ").Append(Version).Append("\n"); - sb.Append(" WorkflowStatusListenerEnabled: ").Append(WorkflowStatusListenerEnabled).Append("\n"); + sb.Append(" CreateTime: ").Append(CreateTime).Append("\n"); + sb.Append(" CreatedBy: ").Append(CreatedBy).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append(" FailureWorkflow: ").Append(FailureWorkflow).Append("\n"); + sb.Append(" InputParameters: ").Append(InputParameters).Append("\n"); + sb.Append(" InputTemplate: ").Append(InputTemplate).Append("\n"); + sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append(" OutputParameters: ").Append(OutputParameters).Append("\n"); + sb.Append(" OwnerApp: ").Append(OwnerApp).Append("\n"); + sb.Append(" OwnerEmail: ").Append(OwnerEmail).Append("\n"); + sb.Append(" Restartable: ").Append(Restartable).Append("\n"); + sb.Append(" SchemaVersion: ").Append(SchemaVersion).Append("\n"); + sb.Append(" Tasks: ").Append(Tasks).Append("\n"); + sb.Append(" TimeoutPolicy: ").Append(TimeoutPolicy).Append("\n"); + sb.Append(" TimeoutSeconds: ").Append(TimeoutSeconds).Append("\n"); + sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n"); + sb.Append(" UpdatedBy: ").Append(UpdatedBy).Append("\n"); + sb.Append(" Variables: ").Append(Variables).Append("\n"); + sb.Append(" Version: ").Append(Version).Append("\n"); + sb.Append(" WorkflowStatusListenerEnabled: ").Append(WorkflowStatusListenerEnabled).Append("\n"); sb.Append("}\n"); return sb.ToString(); } @@ -286,111 +286,111 @@ public bool Equals(WorkflowDef input) return false; return - ( - this.CreateTime == input.CreateTime || - (this.CreateTime != null && - this.CreateTime.Equals(input.CreateTime)) - ) && - ( - this.CreatedBy == input.CreatedBy || - (this.CreatedBy != null && - this.CreatedBy.Equals(input.CreatedBy)) - ) && - ( - this.Description == input.Description || - (this.Description != null && - this.Description.Equals(input.Description)) - ) && - ( - this.FailureWorkflow == input.FailureWorkflow || - (this.FailureWorkflow != null && - this.FailureWorkflow.Equals(input.FailureWorkflow)) - ) && - ( - this.InputParameters == input.InputParameters || - this.InputParameters != null && - input.InputParameters != null && - this.InputParameters.SequenceEqual(input.InputParameters) - ) && - ( - this.InputTemplate == input.InputTemplate || - this.InputTemplate != null && - input.InputTemplate != null && - this.InputTemplate.SequenceEqual(input.InputTemplate) - ) && - ( - this.Name == input.Name || - (this.Name != null && - this.Name.Equals(input.Name)) - ) && - ( - this.OutputParameters == input.OutputParameters || - this.OutputParameters != null && - input.OutputParameters != null && - this.OutputParameters.SequenceEqual(input.OutputParameters) - ) && - ( - this.OwnerApp == input.OwnerApp || - (this.OwnerApp != null && - this.OwnerApp.Equals(input.OwnerApp)) - ) && - ( - this.OwnerEmail == input.OwnerEmail || - (this.OwnerEmail != null && - this.OwnerEmail.Equals(input.OwnerEmail)) - ) && - ( - this.Restartable == input.Restartable || - (this.Restartable != null && - this.Restartable.Equals(input.Restartable)) - ) && - ( - this.SchemaVersion == input.SchemaVersion || - (this.SchemaVersion != null && - this.SchemaVersion.Equals(input.SchemaVersion)) - ) && - ( - this.Tasks == input.Tasks || - this.Tasks != null && - input.Tasks != null && - this.Tasks.SequenceEqual(input.Tasks) - ) && - ( - this.TimeoutPolicy == input.TimeoutPolicy || - (this.TimeoutPolicy != null && - this.TimeoutPolicy.Equals(input.TimeoutPolicy)) - ) && - ( - this.TimeoutSeconds == input.TimeoutSeconds || - (this.TimeoutSeconds != null && - this.TimeoutSeconds.Equals(input.TimeoutSeconds)) - ) && - ( - this.UpdateTime == input.UpdateTime || - (this.UpdateTime != null && - this.UpdateTime.Equals(input.UpdateTime)) - ) && - ( - this.UpdatedBy == input.UpdatedBy || - (this.UpdatedBy != null && - this.UpdatedBy.Equals(input.UpdatedBy)) - ) && - ( - this.Variables == input.Variables || - this.Variables != null && - input.Variables != null && - this.Variables.SequenceEqual(input.Variables) - ) && - ( - this.Version == input.Version || - (this.Version != null && - this.Version.Equals(input.Version)) - ) && - ( - this.WorkflowStatusListenerEnabled == input.WorkflowStatusListenerEnabled || - (this.WorkflowStatusListenerEnabled != null && - this.WorkflowStatusListenerEnabled.Equals(input.WorkflowStatusListenerEnabled)) - ); + ( + this.CreateTime == input.CreateTime || + (this.CreateTime != null && + this.CreateTime.Equals(input.CreateTime)) + ) && + ( + this.CreatedBy == input.CreatedBy || + (this.CreatedBy != null && + this.CreatedBy.Equals(input.CreatedBy)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ) && + ( + this.FailureWorkflow == input.FailureWorkflow || + (this.FailureWorkflow != null && + this.FailureWorkflow.Equals(input.FailureWorkflow)) + ) && + ( + this.InputParameters == input.InputParameters || + this.InputParameters != null && + input.InputParameters != null && + this.InputParameters.SequenceEqual(input.InputParameters) + ) && + ( + this.InputTemplate == input.InputTemplate || + this.InputTemplate != null && + input.InputTemplate != null && + this.InputTemplate.SequenceEqual(input.InputTemplate) + ) && + ( + this.Name == input.Name || + (this.Name != null && + this.Name.Equals(input.Name)) + ) && + ( + this.OutputParameters == input.OutputParameters || + this.OutputParameters != null && + input.OutputParameters != null && + this.OutputParameters.SequenceEqual(input.OutputParameters) + ) && + ( + this.OwnerApp == input.OwnerApp || + (this.OwnerApp != null && + this.OwnerApp.Equals(input.OwnerApp)) + ) && + ( + this.OwnerEmail == input.OwnerEmail || + (this.OwnerEmail != null && + this.OwnerEmail.Equals(input.OwnerEmail)) + ) && + ( + this.Restartable == input.Restartable || + (this.Restartable != null && + this.Restartable.Equals(input.Restartable)) + ) && + ( + this.SchemaVersion == input.SchemaVersion || + (this.SchemaVersion != null && + this.SchemaVersion.Equals(input.SchemaVersion)) + ) && + ( + this.Tasks == input.Tasks || + this.Tasks != null && + input.Tasks != null && + this.Tasks.SequenceEqual(input.Tasks) + ) && + ( + this.TimeoutPolicy == input.TimeoutPolicy || + (this.TimeoutPolicy != null && + this.TimeoutPolicy.Equals(input.TimeoutPolicy)) + ) && + ( + this.TimeoutSeconds == input.TimeoutSeconds || + (this.TimeoutSeconds != null && + this.TimeoutSeconds.Equals(input.TimeoutSeconds)) + ) && + ( + this.UpdateTime == input.UpdateTime || + (this.UpdateTime != null && + this.UpdateTime.Equals(input.UpdateTime)) + ) && + ( + this.UpdatedBy == input.UpdatedBy || + (this.UpdatedBy != null && + this.UpdatedBy.Equals(input.UpdatedBy)) + ) && + ( + this.Variables == input.Variables || + this.Variables != null && + input.Variables != null && + this.Variables.SequenceEqual(input.Variables) + ) && + ( + this.Version == input.Version || + (this.Version != null && + this.Version.Equals(input.Version)) + ) && + ( + this.WorkflowStatusListenerEnabled == input.WorkflowStatusListenerEnabled || + (this.WorkflowStatusListenerEnabled != null && + this.WorkflowStatusListenerEnabled.Equals(input.WorkflowStatusListenerEnabled)) + ); } /// @@ -455,5 +455,15 @@ public override int GetHashCode() { yield break; } + + /// + /// Returns the dictionary representation of the WorkflowDef object. + /// + /// Dictionary representing the WorkflowDef object + public Dictionary ToDictionary() + { + string json = JsonConvert.SerializeObject(this, Formatting.None); + return JsonConvert.DeserializeObject>(json); + } } } diff --git a/Conductor/Client/Models/WorkflowRun.cs b/Conductor/Client/Models/WorkflowRun.cs index 63fc0c8d..093deb13 100644 --- a/Conductor/Client/Models/WorkflowRun.cs +++ b/Conductor/Client/Models/WorkflowRun.cs @@ -1,11 +1,11 @@ -using System.Linq; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; -using System.Text; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; namespace Conductor.Client.Models { @@ -321,5 +321,46 @@ public override int GetHashCode() { yield break; } + + /// + /// Get the tasks + /// + /// + /// + /// + /// + public Task GetTask(string name = null, string taskReferenceName = null) + { + if (name == null && taskReferenceName == null) + { + throw new Exception("ONLY one of name or taskReferenceName MUST be provided. None were provided"); + } + if (name != null && taskReferenceName != null) + { + throw new Exception("ONLY one of name or taskReferenceName MUST be provided. Both were provided"); + } + + Task current = null; + foreach (var task in Tasks) + { + if (task.TaskDefName == name || task.WorkflowTask.TaskReferenceName == taskReferenceName) + { + current = task; + break; + } + } + return current; + } + + /// + /// Returns the current task based on the status + /// + public Task CurrentTask + { + get + { + return Tasks.FirstOrDefault(task => task.Status == Task.StatusEnum.SCHEDULED || task.Status == Task.StatusEnum.INPROGRESS); + } + } } } diff --git a/Conductor/Client/Models/WorkflowTask.cs b/Conductor/Client/Models/WorkflowTask.cs index 1487e76e..fadde0c3 100644 --- a/Conductor/Client/Models/WorkflowTask.cs +++ b/Conductor/Client/Models/WorkflowTask.cs @@ -1,12 +1,12 @@ -using System.Linq; -using System.IO; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; -using System.Text; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; namespace Conductor.Client.Models { @@ -166,7 +166,12 @@ public enum WorkflowTaskTypeEnum /// Enum LLMINDEXTEXT for value: LLM_INDEX_TEXT /// [EnumMember(Value = "LLM_TEXT_COMPLETE")] - LLMTEXTCOMPLETE = 29 + LLMTEXTCOMPLETE = 29, + /// + /// Enum WAITFORWEBHOOK for value: WAIT_FOR_WEBHOOK + /// + [EnumMember(Value = "WAIT_FOR_WEBHOOK")] + WAITFORWEBHOOK = 30 } /// /// Gets or Sets WorkflowTaskType @@ -206,7 +211,7 @@ public enum WorkflowTaskTypeEnum /// taskReferenceName (required). /// type. /// workflowTaskType. - public WorkflowTask(bool? asyncComplete = default(bool?), string caseExpression = default(string), string caseValueParam = default(string), Dictionary> decisionCases = default(Dictionary>), List defaultCase = default(List), List defaultExclusiveJoinTask = default(List), string description = default(string), string dynamicForkJoinTasksParam = default(string), string dynamicForkTasksInputParamName = default(string), string dynamicForkTasksParam = default(string), string dynamicTaskNameParam = default(string), string evaluatorType = default(string), string expression = default(string), List> forkTasks = default(List>), Dictionary inputParameters = default(Dictionary), List joinOn = default(List), string loopCondition = default(string), List loopOver = default(List), string name = default(string), bool? optional = default(bool?), bool? rateLimited = default(bool?), int? retryCount = default(int?), string scriptExpression = default(string), string sink = default(string), int? startDelay = default(int?), SubWorkflowParams subWorkflowParam = default(SubWorkflowParams), TaskDef taskDefinition = default(TaskDef), string taskReferenceName = default(string), string type = default(string), WorkflowTaskTypeEnum? workflowTaskType = default(WorkflowTaskTypeEnum?)) + public WorkflowTask(bool? asyncComplete = default(bool?), string caseExpression = default(string), string caseValueParam = default(string), Dictionary> decisionCases = default(Dictionary>), List defaultCase = default(List), List defaultExclusiveJoinTask = default(List), string description = default(string), string dynamicForkJoinTasksParam = default(string), string dynamicForkTasksInputParamName = default(string), string dynamicForkTasksParam = default(string), string dynamicTaskNameParam = default(string), string evaluatorType = default(string), string expression = default(string), List> forkTasks = default(List>), Dictionary inputParameters = default(Dictionary), List joinOn = default(List), string loopCondition = default(string), List loopOver = default(List), string name = default(string), bool? optional = default(bool?), bool? rateLimited = default(bool?), int? retryCount = default(int?), string scriptExpression = default(string), string sink = default(string), int? startDelay = default(int?), SubWorkflowParams subWorkflowParam = default(SubWorkflowParams), TaskDef taskDefinition = default(TaskDef), string taskReferenceName = default(string), string type = default(string), WorkflowTaskTypeEnum? workflowTaskType = default(WorkflowTaskTypeEnum?), Dictionary onStateChange = default(Dictionary)) { // to ensure "name" is required (not null) if (name == null) @@ -254,6 +259,7 @@ public enum WorkflowTaskTypeEnum this.TaskDefinition = taskDefinition; this.Type = type; this.WorkflowTaskType = workflowTaskType; + this.OnStateChange = onStateChange; } /// @@ -430,6 +436,11 @@ public enum WorkflowTaskTypeEnum [DataMember(Name = "type", EmitDefaultValue = false)] public string Type { get; set; } + /// + /// Gets or Sets OnStateChange + /// + [DataMember(Name = "onStateChange", EmitDefaultValue = false)] + public Dictionary OnStateChange { get; set; } /// /// Returns the string presentation of the object @@ -439,36 +450,36 @@ public override string ToString() { var sb = new StringBuilder(); sb.Append("class WorkflowTask {\n"); - sb.Append(" AsyncComplete: ").Append(AsyncComplete).Append("\n"); - sb.Append(" CaseExpression: ").Append(CaseExpression).Append("\n"); - sb.Append(" CaseValueParam: ").Append(CaseValueParam).Append("\n"); - sb.Append(" DecisionCases: ").Append(DecisionCases).Append("\n"); - sb.Append(" DefaultCase: ").Append(DefaultCase).Append("\n"); - sb.Append(" DefaultExclusiveJoinTask: ").Append(DefaultExclusiveJoinTask).Append("\n"); - sb.Append(" Description: ").Append(Description).Append("\n"); - sb.Append(" DynamicForkJoinTasksParam: ").Append(DynamicForkJoinTasksParam).Append("\n"); - sb.Append(" DynamicForkTasksInputParamName: ").Append(DynamicForkTasksInputParamName).Append("\n"); - sb.Append(" DynamicForkTasksParam: ").Append(DynamicForkTasksParam).Append("\n"); - sb.Append(" DynamicTaskNameParam: ").Append(DynamicTaskNameParam).Append("\n"); - sb.Append(" EvaluatorType: ").Append(EvaluatorType).Append("\n"); - sb.Append(" Expression: ").Append(Expression).Append("\n"); - sb.Append(" ForkTasks: ").Append(ForkTasks).Append("\n"); - sb.Append(" InputParameters: ").Append(InputParameters).Append("\n"); - sb.Append(" JoinOn: ").Append(JoinOn).Append("\n"); - sb.Append(" LoopCondition: ").Append(LoopCondition).Append("\n"); - sb.Append(" LoopOver: ").Append(LoopOver).Append("\n"); - sb.Append(" Name: ").Append(Name).Append("\n"); - sb.Append(" Optional: ").Append(Optional).Append("\n"); - sb.Append(" RateLimited: ").Append(RateLimited).Append("\n"); - sb.Append(" RetryCount: ").Append(RetryCount).Append("\n"); - sb.Append(" ScriptExpression: ").Append(ScriptExpression).Append("\n"); - sb.Append(" Sink: ").Append(Sink).Append("\n"); - sb.Append(" StartDelay: ").Append(StartDelay).Append("\n"); - sb.Append(" SubWorkflowParam: ").Append(SubWorkflowParam).Append("\n"); - sb.Append(" TaskDefinition: ").Append(TaskDefinition).Append("\n"); - sb.Append(" TaskReferenceName: ").Append(TaskReferenceName).Append("\n"); - sb.Append(" Type: ").Append(Type).Append("\n"); - sb.Append(" WorkflowTaskType: ").Append(WorkflowTaskType).Append("\n"); + sb.Append(" AsyncComplete: ").Append(AsyncComplete).Append("\n"); + sb.Append(" CaseExpression: ").Append(CaseExpression).Append("\n"); + sb.Append(" CaseValueParam: ").Append(CaseValueParam).Append("\n"); + sb.Append(" DecisionCases: ").Append(DecisionCases).Append("\n"); + sb.Append(" DefaultCase: ").Append(DefaultCase).Append("\n"); + sb.Append(" DefaultExclusiveJoinTask: ").Append(DefaultExclusiveJoinTask).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append(" DynamicForkJoinTasksParam: ").Append(DynamicForkJoinTasksParam).Append("\n"); + sb.Append(" DynamicForkTasksInputParamName: ").Append(DynamicForkTasksInputParamName).Append("\n"); + sb.Append(" DynamicForkTasksParam: ").Append(DynamicForkTasksParam).Append("\n"); + sb.Append(" DynamicTaskNameParam: ").Append(DynamicTaskNameParam).Append("\n"); + sb.Append(" EvaluatorType: ").Append(EvaluatorType).Append("\n"); + sb.Append(" Expression: ").Append(Expression).Append("\n"); + sb.Append(" ForkTasks: ").Append(ForkTasks).Append("\n"); + sb.Append(" InputParameters: ").Append(InputParameters).Append("\n"); + sb.Append(" JoinOn: ").Append(JoinOn).Append("\n"); + sb.Append(" LoopCondition: ").Append(LoopCondition).Append("\n"); + sb.Append(" LoopOver: ").Append(LoopOver).Append("\n"); + sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append(" Optional: ").Append(Optional).Append("\n"); + sb.Append(" RateLimited: ").Append(RateLimited).Append("\n"); + sb.Append(" RetryCount: ").Append(RetryCount).Append("\n"); + sb.Append(" ScriptExpression: ").Append(ScriptExpression).Append("\n"); + sb.Append(" Sink: ").Append(Sink).Append("\n"); + sb.Append(" StartDelay: ").Append(StartDelay).Append("\n"); + sb.Append(" SubWorkflowParam: ").Append(SubWorkflowParam).Append("\n"); + sb.Append(" TaskDefinition: ").Append(TaskDefinition).Append("\n"); + sb.Append(" TaskReferenceName: ").Append(TaskReferenceName).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append(" WorkflowTaskType: ").Append(WorkflowTaskType).Append("\n"); sb.Append("}\n"); return sb.ToString(); } @@ -503,163 +514,163 @@ public bool Equals(WorkflowTask input) return false; return - ( - this.AsyncComplete == input.AsyncComplete || - (this.AsyncComplete != null && - this.AsyncComplete.Equals(input.AsyncComplete)) - ) && - ( - this.CaseExpression == input.CaseExpression || - (this.CaseExpression != null && - this.CaseExpression.Equals(input.CaseExpression)) - ) && - ( - this.CaseValueParam == input.CaseValueParam || - (this.CaseValueParam != null && - this.CaseValueParam.Equals(input.CaseValueParam)) - ) && - ( - this.DecisionCases == input.DecisionCases || - this.DecisionCases != null && - input.DecisionCases != null && - this.DecisionCases.SequenceEqual(input.DecisionCases) - ) && - ( - this.DefaultCase == input.DefaultCase || - this.DefaultCase != null && - input.DefaultCase != null && - this.DefaultCase.SequenceEqual(input.DefaultCase) - ) && - ( - this.DefaultExclusiveJoinTask == input.DefaultExclusiveJoinTask || - this.DefaultExclusiveJoinTask != null && - input.DefaultExclusiveJoinTask != null && - this.DefaultExclusiveJoinTask.SequenceEqual(input.DefaultExclusiveJoinTask) - ) && - ( - this.Description == input.Description || - (this.Description != null && - this.Description.Equals(input.Description)) - ) && - ( - this.DynamicForkJoinTasksParam == input.DynamicForkJoinTasksParam || - (this.DynamicForkJoinTasksParam != null && - this.DynamicForkJoinTasksParam.Equals(input.DynamicForkJoinTasksParam)) - ) && - ( - this.DynamicForkTasksInputParamName == input.DynamicForkTasksInputParamName || - (this.DynamicForkTasksInputParamName != null && - this.DynamicForkTasksInputParamName.Equals(input.DynamicForkTasksInputParamName)) - ) && - ( - this.DynamicForkTasksParam == input.DynamicForkTasksParam || - (this.DynamicForkTasksParam != null && - this.DynamicForkTasksParam.Equals(input.DynamicForkTasksParam)) - ) && - ( - this.DynamicTaskNameParam == input.DynamicTaskNameParam || - (this.DynamicTaskNameParam != null && - this.DynamicTaskNameParam.Equals(input.DynamicTaskNameParam)) - ) && - ( - this.EvaluatorType == input.EvaluatorType || - (this.EvaluatorType != null && - this.EvaluatorType.Equals(input.EvaluatorType)) - ) && - ( - this.Expression == input.Expression || - (this.Expression != null && - this.Expression.Equals(input.Expression)) - ) && - ( - this.ForkTasks == input.ForkTasks || - this.ForkTasks != null && - input.ForkTasks != null && - this.ForkTasks.SequenceEqual(input.ForkTasks) - ) && - ( - this.InputParameters == input.InputParameters || - this.InputParameters != null && - input.InputParameters != null && - this.InputParameters.SequenceEqual(input.InputParameters) - ) && - ( - this.JoinOn == input.JoinOn || - this.JoinOn != null && - input.JoinOn != null && - this.JoinOn.SequenceEqual(input.JoinOn) - ) && - ( - this.LoopCondition == input.LoopCondition || - (this.LoopCondition != null && - this.LoopCondition.Equals(input.LoopCondition)) - ) && - ( - this.LoopOver == input.LoopOver || - this.LoopOver != null && - input.LoopOver != null && - this.LoopOver.SequenceEqual(input.LoopOver) - ) && - ( - this.Name == input.Name || - (this.Name != null && - this.Name.Equals(input.Name)) - ) && - ( - this.Optional == input.Optional || - (this.Optional != null && - this.Optional.Equals(input.Optional)) - ) && - ( - this.RateLimited == input.RateLimited || - (this.RateLimited != null && - this.RateLimited.Equals(input.RateLimited)) - ) && - ( - this.RetryCount == input.RetryCount || - (this.RetryCount != null && - this.RetryCount.Equals(input.RetryCount)) - ) && - ( - this.ScriptExpression == input.ScriptExpression || - (this.ScriptExpression != null && - this.ScriptExpression.Equals(input.ScriptExpression)) - ) && - ( - this.Sink == input.Sink || - (this.Sink != null && - this.Sink.Equals(input.Sink)) - ) && - ( - this.StartDelay == input.StartDelay || - (this.StartDelay != null && - this.StartDelay.Equals(input.StartDelay)) - ) && - ( - this.SubWorkflowParam == input.SubWorkflowParam || - (this.SubWorkflowParam != null && - this.SubWorkflowParam.Equals(input.SubWorkflowParam)) - ) && - ( - this.TaskDefinition == input.TaskDefinition || - (this.TaskDefinition != null && - this.TaskDefinition.Equals(input.TaskDefinition)) - ) && - ( - this.TaskReferenceName == input.TaskReferenceName || - (this.TaskReferenceName != null && - this.TaskReferenceName.Equals(input.TaskReferenceName)) - ) && - ( - this.Type == input.Type || - (this.Type != null && - this.Type.Equals(input.Type)) - ) && - ( - this.WorkflowTaskType == input.WorkflowTaskType || - (this.WorkflowTaskType != null && - this.WorkflowTaskType.Equals(input.WorkflowTaskType)) - ); + ( + this.AsyncComplete == input.AsyncComplete || + (this.AsyncComplete != null && + this.AsyncComplete.Equals(input.AsyncComplete)) + ) && + ( + this.CaseExpression == input.CaseExpression || + (this.CaseExpression != null && + this.CaseExpression.Equals(input.CaseExpression)) + ) && + ( + this.CaseValueParam == input.CaseValueParam || + (this.CaseValueParam != null && + this.CaseValueParam.Equals(input.CaseValueParam)) + ) && + ( + this.DecisionCases == input.DecisionCases || + this.DecisionCases != null && + input.DecisionCases != null && + this.DecisionCases.SequenceEqual(input.DecisionCases) + ) && + ( + this.DefaultCase == input.DefaultCase || + this.DefaultCase != null && + input.DefaultCase != null && + this.DefaultCase.SequenceEqual(input.DefaultCase) + ) && + ( + this.DefaultExclusiveJoinTask == input.DefaultExclusiveJoinTask || + this.DefaultExclusiveJoinTask != null && + input.DefaultExclusiveJoinTask != null && + this.DefaultExclusiveJoinTask.SequenceEqual(input.DefaultExclusiveJoinTask) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ) && + ( + this.DynamicForkJoinTasksParam == input.DynamicForkJoinTasksParam || + (this.DynamicForkJoinTasksParam != null && + this.DynamicForkJoinTasksParam.Equals(input.DynamicForkJoinTasksParam)) + ) && + ( + this.DynamicForkTasksInputParamName == input.DynamicForkTasksInputParamName || + (this.DynamicForkTasksInputParamName != null && + this.DynamicForkTasksInputParamName.Equals(input.DynamicForkTasksInputParamName)) + ) && + ( + this.DynamicForkTasksParam == input.DynamicForkTasksParam || + (this.DynamicForkTasksParam != null && + this.DynamicForkTasksParam.Equals(input.DynamicForkTasksParam)) + ) && + ( + this.DynamicTaskNameParam == input.DynamicTaskNameParam || + (this.DynamicTaskNameParam != null && + this.DynamicTaskNameParam.Equals(input.DynamicTaskNameParam)) + ) && + ( + this.EvaluatorType == input.EvaluatorType || + (this.EvaluatorType != null && + this.EvaluatorType.Equals(input.EvaluatorType)) + ) && + ( + this.Expression == input.Expression || + (this.Expression != null && + this.Expression.Equals(input.Expression)) + ) && + ( + this.ForkTasks == input.ForkTasks || + this.ForkTasks != null && + input.ForkTasks != null && + this.ForkTasks.SequenceEqual(input.ForkTasks) + ) && + ( + this.InputParameters == input.InputParameters || + this.InputParameters != null && + input.InputParameters != null && + this.InputParameters.SequenceEqual(input.InputParameters) + ) && + ( + this.JoinOn == input.JoinOn || + this.JoinOn != null && + input.JoinOn != null && + this.JoinOn.SequenceEqual(input.JoinOn) + ) && + ( + this.LoopCondition == input.LoopCondition || + (this.LoopCondition != null && + this.LoopCondition.Equals(input.LoopCondition)) + ) && + ( + this.LoopOver == input.LoopOver || + this.LoopOver != null && + input.LoopOver != null && + this.LoopOver.SequenceEqual(input.LoopOver) + ) && + ( + this.Name == input.Name || + (this.Name != null && + this.Name.Equals(input.Name)) + ) && + ( + this.Optional == input.Optional || + (this.Optional != null && + this.Optional.Equals(input.Optional)) + ) && + ( + this.RateLimited == input.RateLimited || + (this.RateLimited != null && + this.RateLimited.Equals(input.RateLimited)) + ) && + ( + this.RetryCount == input.RetryCount || + (this.RetryCount != null && + this.RetryCount.Equals(input.RetryCount)) + ) && + ( + this.ScriptExpression == input.ScriptExpression || + (this.ScriptExpression != null && + this.ScriptExpression.Equals(input.ScriptExpression)) + ) && + ( + this.Sink == input.Sink || + (this.Sink != null && + this.Sink.Equals(input.Sink)) + ) && + ( + this.StartDelay == input.StartDelay || + (this.StartDelay != null && + this.StartDelay.Equals(input.StartDelay)) + ) && + ( + this.SubWorkflowParam == input.SubWorkflowParam || + (this.SubWorkflowParam != null && + this.SubWorkflowParam.Equals(input.SubWorkflowParam)) + ) && + ( + this.TaskDefinition == input.TaskDefinition || + (this.TaskDefinition != null && + this.TaskDefinition.Equals(input.TaskDefinition)) + ) && + ( + this.TaskReferenceName == input.TaskReferenceName || + (this.TaskReferenceName != null && + this.TaskReferenceName.Equals(input.TaskReferenceName)) + ) && + ( + this.Type == input.Type || + (this.Type != null && + this.Type.Equals(input.Type)) + ) && + ( + this.WorkflowTaskType == input.WorkflowTaskType || + (this.WorkflowTaskType != null && + this.WorkflowTaskType.Equals(input.WorkflowTaskType)) + ); } /// diff --git a/Conductor/Definition/ConductorWorkflow.cs b/Conductor/Definition/ConductorWorkflow.cs index e22a01a9..1e3e8a5c 100644 --- a/Conductor/Definition/ConductorWorkflow.cs +++ b/Conductor/Definition/ConductorWorkflow.cs @@ -88,5 +88,39 @@ public StartWorkflowRequest GetStartWorkflowRequest() version: Version ); } + + /// + /// creates a json path for input parameters + /// + /// + /// + public string Input(string jsonPath) + { + if (jsonPath == null) + { + return "${" + "workflow.input" + "}"; + } + else + { + return "${" + $"workflow.input.{jsonPath}" + "}"; + } + } + + /// + /// creates a json path for output parameters + /// + /// + /// + public string Output(string jsonPath = null) + { + if (jsonPath == null) + { + return "${" + "workflow.output" + "}"; + } + else + { + return "${" + $"workflow.output.{jsonPath}" + "}"; + } + } } } diff --git a/Conductor/Definition/TaskType/DynamicTask.cs b/Conductor/Definition/TaskType/DynamicTask.cs new file mode 100644 index 00000000..8fed1aa1 --- /dev/null +++ b/Conductor/Definition/TaskType/DynamicTask.cs @@ -0,0 +1,23 @@ +namespace Conductor.Definition.TaskType +{ + public class DynamicTask : Task + { + /// + /// Gets or Sets DynamicTaskParam + /// + public string DynamicTaskParam { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public DynamicTask(string dynamicTask, string taskRefName, string dynamicTaskParam = "taskToExecute") + : base(taskRefName, WorkflowTaskTypeEnum.DYNAMIC) + { + WithInput(dynamicTaskParam, dynamicTask); + DynamicTaskParam = dynamicTaskParam; + } + } +} diff --git a/Conductor/Definition/TaskType/HumanTask.cs b/Conductor/Definition/TaskType/HumanTask.cs new file mode 100644 index 00000000..f3bb44ed --- /dev/null +++ b/Conductor/Definition/TaskType/HumanTask.cs @@ -0,0 +1,133 @@ +using Newtonsoft.Json.Converters; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace Conductor.Definition.TaskType +{ + public class HumanTask : Task + { + /// + /// Defines AssignmentCompletionStrategy + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum AssignmentCompletionStrategyEnum + { + /// + /// Enum LEAVE_OPEN for value: LEAVE_OPEN + /// + [EnumMember(Value = "LEAVE_OPEN")] + LEAVE_OPEN, + + /// + /// Enum TERMINATE for value: TERMINATE + /// + [EnumMember(Value = "TERMINATE")] + TERMINATE + } + + /// + /// Defines TriggerType + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum TriggerTypeEnum + { + /// + /// Enum ASSIGNED for value: ASSIGNED + /// + [EnumMember(Value = "ASSIGNED")] + ASSIGNED, + /// + /// Enum PENDING for value: PENDING + /// + [EnumMember(Value = "PENDING")] + PENDING, + + /// + /// Enum IN_PROGRESS for value: IN_PROGRESS + /// + [EnumMember(Value = "IN_PROGRESS")] + IN_PROGRESS, + + /// + /// Enum COMPLETED for value: COMPLETED + /// + [EnumMember(Value = "COMPLETED")] + COMPLETED, + + /// + /// Enum TIMED_OUT for value: TIMED_OUT + /// + [EnumMember(Value = "TIMED_OUT")] + TIMED_OUT, + + /// + /// Enum ASSIGNEE_CHANGED for value: ASSIGNEE_CHANGED + /// + [EnumMember(Value = "ASSIGNEE_CHANGED")] + ASSIGNEE_CHANGED + } + + /// + /// Gets or sets DisplayName + /// + public string DisplayName { get; set; } + + /// + /// Gets or sets FormTemplate + /// + public string FormTemplate { get; set; } + + /// + /// Gets or sets FormVersion + /// + public int FormVersion { get; set; } + + /// + /// Gets or sets AssignmentCompletionStrategy + /// + public AssignmentCompletionStrategyEnum AssignmentCompletionStrategy { get; set; } + + public const string ASSIGNMENTCOMPLETIONSTRATEGY = "assignmentCompletionStrategy"; + public const string DISPLAYNAME = "displayName"; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + /// + public HumanTask(string taskRefName, string displayName = null, string formTemplate = null, int formVersion = 0, AssignmentCompletionStrategyEnum assignmentCompletionStrategy = AssignmentCompletionStrategyEnum.LEAVE_OPEN) + : base(taskRefName, WorkflowTaskTypeEnum.HUMAN) + { + DisplayName = displayName; + FormTemplate = formTemplate; + FormVersion = formVersion; + AssignmentCompletionStrategy = assignmentCompletionStrategy; + InitializeInputParameters(); + } + + /// + /// Adds the HumanTaskDefinition to InputParameters + /// + private void InitializeInputParameters() + { + var humanTaskDefinition = new Dictionary +{ +{ ASSIGNMENTCOMPLETIONSTRATEGY, AssignmentCompletionStrategy.ToString() }, +{ DISPLAYNAME, DisplayName }, +{ +"userFormTemplate", new Dictionary +{ +{ "name", FormTemplate }, +{ "version", FormVersion } +} +} +}; + + InputParameters["humanTaskDefinition"] = humanTaskDefinition; + } + } +} \ No newline at end of file diff --git a/Conductor/Definition/TaskType/LlmTasks/LlmIndexDocuments.cs b/Conductor/Definition/TaskType/LlmTasks/LlmIndexDocuments.cs index 3f2a544e..7137f5dc 100644 --- a/Conductor/Definition/TaskType/LlmTasks/LlmIndexDocuments.cs +++ b/Conductor/Definition/TaskType/LlmTasks/LlmIndexDocuments.cs @@ -1,4 +1,5 @@ using Conductor.Client; +using Conductor.Definition.TaskType.LlmTasks.Utils; using System.Collections.Generic; namespace Conductor.Definition.TaskType.LlmTasks @@ -36,7 +37,7 @@ public class LlmIndexDocuments : Task /// /// Gets or Sets EmbeddingModel /// - public string EmbeddingModel { get; set; } + public EmbeddingModel EmbeddingModel { get; set; } /// /// Gets or Sets Url @@ -66,7 +67,7 @@ public class LlmIndexDocuments : Task /// /// Gets or Sets DocId /// - public int? DocId { get; set; } + public string DocId { get; set; } /// /// Gets or Sets TaskName @@ -89,8 +90,8 @@ public class LlmIndexDocuments : Task /// /// /// - public LlmIndexDocuments(string taskReferenceName, string vectorDB, string nameSpace, string index, string embeddingModelProvider, string embeddingModel, string url, - string mediaType, int? chunkSize, int? chunkOverlap, int? docId, string taskName = null, Dictionary metaData = null) : base(taskReferenceName, WorkflowTaskTypeEnum.LLMINDEXDOCUMENT) + public LlmIndexDocuments(string taskReferenceName = default(string), string vectorDB = default(string), string nameSpace = default(string), string index = default(string), string embeddingModelProvider = default(string), EmbeddingModel embeddingModel = default(EmbeddingModel), string url = default(string), + string mediaType = default(string), int? chunkSize = default(int?), int? chunkOverlap = default(int?), string docId = default(string), string taskName = null, Dictionary metaData = null) : base(taskReferenceName, WorkflowTaskTypeEnum.LLMINDEXDOCUMENT) { TaskRefName = taskReferenceName; VectorDB = vectorDB; @@ -124,7 +125,7 @@ private void InitializeInputs() WithInput(Constants.CHUNKOVERLAP, ChunkOverlap); } - if (DocId.HasValue) + if (!string.IsNullOrEmpty(DocId)) { WithInput(Constants.DOCID, DocId); } @@ -139,4 +140,4 @@ private void InitializeInputs() WithInput(Constants.METADATA, MetaData); } } -} +} \ No newline at end of file diff --git a/Conductor/Definition/TaskType/Task.cs b/Conductor/Definition/TaskType/Task.cs index ec10fb1b..e790b9fa 100644 --- a/Conductor/Definition/TaskType/Task.cs +++ b/Conductor/Definition/TaskType/Task.cs @@ -5,6 +5,11 @@ namespace Conductor.Definition.TaskType { public abstract class Task : WorkflowTask { + /// + /// Initializes a new instance of the class. + /// + /// + /// public Task(string taskReferenceName, WorkflowTask.WorkflowTaskTypeEnum taskType) : base( name: taskReferenceName, @@ -14,10 +19,34 @@ public Task(string taskReferenceName, WorkflowTask.WorkflowTaskTypeEnum taskType ) { } + /// + /// Adds the task parameters to InputParameters + /// + /// + /// + /// public Task WithInput(string key, object value) { - InputParameters.Add(key, value); + // Updates or adds key-value pair + InputParameters[key] = value; return this; } + + /// + /// creates a json path for output parameters + /// + /// + /// + public string Output(string jsonPath = null) + { + if (jsonPath == null) + { + return "${" + $"{this.TaskReferenceName}.output" + "}"; + } + else + { + return "${" + $"{this.TaskReferenceName}.output.{jsonPath}" + "}"; + } + } } } diff --git a/Conductor/Definition/TaskType/WaitForWebhookTask.cs b/Conductor/Definition/TaskType/WaitForWebhookTask.cs new file mode 100644 index 00000000..24fb35eb --- /dev/null +++ b/Conductor/Definition/TaskType/WaitForWebhookTask.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Conductor.Definition.TaskType +{ + public class WaitForWebHookTask : Task + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public WaitForWebHookTask(string taskReferenceName, Dictionary matches) : base(taskReferenceName, WorkflowTaskTypeEnum.WAITFORWEBHOOK) + { + InputParameters = matches; + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Copilot/Customer.cs b/Conductor/Examples/Copilot/Customer.cs new file mode 100644 index 00000000..6e6a1edf --- /dev/null +++ b/Conductor/Examples/Copilot/Customer.cs @@ -0,0 +1,40 @@ +namespace Conductor.Examples.Copilot +{ + public class Customer + { + /// + /// Gets or sets Id + /// + public int Id { get; set; } + + /// + /// Gets or sets Name + /// + public string Name { get; set; } + + /// + /// Gets or sets AnnualSpend + /// + public double AnnualSpend { get; set; } + + /// + /// Gets or sets Country + /// + public string Country { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public Customer(int id = default(int), string name = default(string), double annualSpend = default(double), string country = default(string)) + { + Id = id; + Name = name; + AnnualSpend = annualSpend; + Country = country; + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Copilot/OpenAICopilot.cs b/Conductor/Examples/Copilot/OpenAICopilot.cs new file mode 100644 index 00000000..783e8336 --- /dev/null +++ b/Conductor/Examples/Copilot/OpenAICopilot.cs @@ -0,0 +1,216 @@ +using conductor.csharp.Client.Extensions; +using conductor.Examples; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Definition.TaskType.LlmTasks; +using Conductor.Examples.Workers; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Tasks = Conductor.Definition.TaskType.Task; + +namespace Conductor.Examples.Copilot +{ + public class OpenAICopilot + { + private readonly WorkflowResourceApi _workflowClient; + private readonly MetadataResourceApi _metaDataClient; + private readonly WorkflowExecutor _workflowExecutor; + private readonly Client.Configuration _configuration; + private readonly ILogger _logger; + + //consts + + public const string FUNCTIONCHATBOX = "my_function_chatbot"; + public const string FUNCTIONCHATBOXDESCRIPTION = "test_function_chatbot"; + + public OpenAICopilot() + { + _configuration = new Client.Configuration(); + _workflowExecutor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _metaDataClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + //_metaDataClient = _orkesApiClient.GetClient(); + } + + [WorkerTask("get_customer_list", 5, "taskDomain", 200, "workerId")] + public List GetCustomerList() + { + var customers = new List(); + var random = new Random(); + for (int i = 0; i < 100; i++) + { + var customerName = GenerateRandomString(random, ExampleConstants.RANDOMCHARACTERS, 5); + var spend = random.Next(100000, 9000000); + customers.Add(new Customer + { + Id = i, + Name = "Customer " + customerName, + AnnualSpend = spend, + Country = "US" + }); + } + return customers; + } + + [WorkerTask("get_top_n", 5, "taskDomain", 200, "workerId")] + public List GetTopNCustomers(int n, List customers) + { + var sortedCustomers = customers.OrderByDescending(c => c.AnnualSpend).ToList(); + var end = Math.Min(n + 1, sortedCustomers.Count); + return sortedCustomers.GetRange(1, end - 1); + } + + [WorkerTask("generate_promo_code", 5, "taskDomain", 200, "workerId")] + public string GeneratePromoCode() + { + var random = new Random(); + var promoCode = GenerateRandomString(random, ExampleConstants.RANDOMCHARACTERS, 5); + return promoCode; + } + + [WorkerTask("send_email", 5, "taskDomain", 200, "workerId")] + public string SendEmail(List customers, string promoCode) + { + return $"Sent {promoCode} to {customers.Count} customers"; + } + + [WorkerTask(taskType: "create_workflow", 5, "taskDomain", 520, "workerId")] + public Dictionary CreateWorkflow(List steps, Dictionary inputs) + { + var workflow = new ConductorWorkflow() + .WithName(ExampleConstants.COPILOTEXECUTION) + .WithDescription(ExampleConstants.COPILOTDESCRIPTION) + .WithVersion(1); + for (int i = 0; i < steps.Count; i++) + { + if (steps[i] != "review") + { + Tasks task = new HumanTask(taskRefName: "review", displayName: "review email", formVersion: 0, formTemplate: "email_review"); + var inputValue = inputs["step"]; + task.InputParameters.Add("step", inputValue); + workflow.WithTask(task); + } + else + { + Tasks task = new SimpleTask(taskName: steps[i], taskReferenceName: steps[i]); + var inputValue = inputs["step"]; + task.InputParameters.Add("step", inputValue); + workflow.WithTask(task); + } + } + + _workflowExecutor.RegisterWorkflow(workflow, true); + _logger.LogInformation($"\n\n\nRegistered workflow by name {workflow.Name}\n"); + return workflow.ToDictionary(); + } + + public static string GenerateRandomString(Random random, string characters, int length) + { + return new string(Enumerable.Repeat(characters, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } + + public void OpenAICopilotTest() + { + string llmProvider = ExampleConstants.OPENAITEXT + Environment.UserName; + string chatCompleteModel = ExampleConstants.OPENAIGPT; + string promtName = ExampleConstants.OPENAI_PROMPTNAME; + var openAIConfig = new OpenAIConfig(); + var orchestrator = new Orchestrator(_configuration); + var taskDefs = new List() +{ +new TaskDef() { Description = "test", Name = ExampleConstants.OPENAITASKDEFINITIONNAME }, +new TaskDef() { Description = "test", Name = ExampleConstants.OPENAITASKDEFNAME } +}; + + _metaDataClient.RegisterTaskDef(taskDefs); + orchestrator.AddAIIntegration(llmProvider, Client.Ai.Configuration.LLMProviderEnum.OPEN_AI, new List { chatCompleteModel }, ExampleConstants.OPENAICONFIG, openAIConfig); + orchestrator.AddPromptTemplate(ExampleConstants.PROMPT_TEXT, ExampleConstants.PROMPTTEMPLATEDESCRIPTION, promtName); + orchestrator.AssociatePromptTemplate(llmProvider, new List { chatCompleteModel }, promtName); + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(FUNCTIONCHATBOX) + .WithDescription(FUNCTIONCHATBOXDESCRIPTION) + .WithVersion(1); + Tasks userInput = new WaitTask("get_user_input", new TimeSpan(1)); + var chatComplete = new LlmChatComplete(taskReferenceName: ExampleConstants.CHATCOMPLETEREF, llmProvider: llmProvider, model: chatCompleteModel, messages: new List { new ChatMessage("user", userInput.Output("query")) }, instructionsTemplate: promtName, maxTokens: 2048); + Tasks functionCall = new DynamicTask("SUB_WORKFLOW", "fn_call_ref"); + functionCall.WithInput("steps", chatComplete.Output("function_parameters.steps")); + functionCall.WithInput(ExampleConstants.INPUTS, chatComplete.Output("function_parameters.inputs")); + functionCall.WithInput("subWorkflowName", ExampleConstants.COPILOTEXECUTION); + functionCall.WithInput("subWorkflowVersion", 1); + + Tasks subWorkFlow = new SubWorkflowTask("execute_workflow", new SubWorkflowParams(ExampleConstants.COPILOTEXECUTION)); + + //Pass task reference name once the annotation is in place + var registerWorkFlow = CreateWorkflow(steps: new List { chatComplete.Output("function_parameters.steps") }, inputs: new Dictionary +{ +{ "step", "function_parameters.inputs" } +}); + var callFunction = new SwitchTask("to_call_or_not", chatComplete.Output("function")); + callFunction.WithDecisionCase(ExampleConstants.CREATEWORKFLOW, new WorkflowTask[] { (WorkflowTask)registerWorkFlow["Tasks"], subWorkFlow }); + Tasks defaultFunc = new DynamicTask(chatComplete.Output("function"), "call_one_fun_ref"); + defaultFunc.WithInput(ExampleConstants.INPUTS, chatComplete.Output("function_parameters")); + defaultFunc.WithInput(ExampleConstants.DYNAMICTASKINPUTPARAM, ExampleConstants.INPUTS); + callFunction.WithDefaultCase(defaultFunc); + + workflow.WithTask(userInput); + workflow.WithTask(chatComplete); + workflow.WithTask(callFunction); + workflow.WithTimeoutPolicy(WorkflowDef.TimeoutPolicyEnum.TIMEOUTWF, 120); + + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle, new DynamicWorker("get_user_input"), new DynamicWorker("chat_complete_ref"), new DynamicWorker("to_call_or_not"))); + waitHandle.WaitOne(); + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", startWorkflow.Name, 1, userInput.TaskReferenceName); + string workFlowId = workflowRun.WorkflowId; + _logger.LogInformation(ExampleConstants.USEROPTIONS); + string query = Console.ReadLine(); + var inputTask = workflowRun.GetTask(taskReferenceName: userInput.TaskReferenceName); + + WorkflowStateUpdate workflowStateUpdate = new WorkflowStateUpdate() + { + TaskReferenceName = userInput.TaskReferenceName, + TaskResult = new TaskResult + { + TaskId = inputTask.TaskId, + OutputData = new Dictionary +{ +{"query", query } +}, + Status = TaskResult.StatusEnum.COMPLETED + } + }; + workflowRun = _workflowClient.UpdateWorkflow(workflowId: workFlowId, request: workflowStateUpdate, waitForSeconds: 30); + object result = workflowRun.Output["result"]; + + // Convert the 'result' object to JSON with indentation + string output = JsonConvert.SerializeObject(result, Formatting.Indented); + + // Printing the JSON-formatted output to the console + _logger.LogInformation($"\n{output}\n"); + } + } +} diff --git a/Conductor/Examples/DynamicWorkflow.cs b/Conductor/Examples/DynamicWorkflow.cs new file mode 100644 index 00000000..fc167ad5 --- /dev/null +++ b/Conductor/Examples/DynamicWorkflow.cs @@ -0,0 +1,95 @@ +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Examples.Workers; +using Conductor.Executor; +using System.Collections.Generic; +using System.Threading; + +namespace Conductor.Examples +{ + public class DynamicWorkflow + { + private readonly WorkflowResourceApi _workflowClient; + private readonly MetadataResourceApi _metaDataClient; + private readonly WorkflowExecutor _workflowExecutor; + + //const + private const string WorkflowName = "dynamic_workflow"; + private const string WorkflowDescription = "test_dynamic_workflow"; + + public DynamicWorkflow() + { + var config = new Configuration(); + _workflowExecutor = new WorkflowExecutor(config); + _workflowClient = ApiExtensions.GetClient(); + _metaDataClient = ApiExtensions.GetClient(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(config, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + //_metaDataClient = _orkesApiClient.GetClient(); + } + + [WorkerTask(taskType: "GetEmail", 5, "taskDomain", 520, "workerId")] + public string GetUserEmail(string userId) + { + return $"{userId}@example.com"; + } + + [WorkerTask(taskType: "SendEmail", 5, "taskDomain", 520, "workerId")] + public string SendEmail(string email, string subject, string body) + { + return $"sending email to {email} with subject {subject} and body {body}"; + } + + public void DynamicWorkFlowMain() + { + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(WorkflowName) + .WithDescription(WorkflowDescription) + .WithVersion(1); + + workflow.WithInputParameter("userId"); + + //Once the annotator is ready we have to remove this piece of code as the task creation will be taken care inside the wrapper method + var getEmailTask = new SimpleTask("GetEmail", "GetEmail").WithInput("InputVaraible", workflow.Input("userId")); + getEmailTask.Description = "Test Get email"; + workflow.WithTask(getEmailTask); + + var SendEmailTask = new SimpleTask("SendEmail", "Send_Email_refTask").WithInput("InputVaraible", workflow.Input("userId")); + SendEmailTask.Description = "Test Get email"; + workflow.WithTask(SendEmailTask); + + TaskDef taskDef = new TaskDef() { Description = "test", Name = "dynamic_workflow-task" }; + + _metaDataClient.RegisterTaskDef(new List() { taskDef }); + _workflowExecutor.RegisterWorkflow(workflow, true); + + var testInput = new Dictionary +{ +{ "userId", "Test" } +}; + + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Input = testInput, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + + var workflowTask = _workflowExecutor.StartWorkflow(startWorkflow); + var waitHandle = new ManualResetEvent(false); + + //For testing purpose the worker is created manually for now. Once the annotation logic is in place we can getrid of this + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle, new DynamicWorker("GetEmail"))); + waitHandle.WaitOne(); + } + } +} diff --git a/Conductor/Examples/ExampleConstant.cs b/Conductor/Examples/ExampleConstant.cs new file mode 100644 index 00000000..8f7d738e --- /dev/null +++ b/Conductor/Examples/ExampleConstant.cs @@ -0,0 +1,126 @@ +namespace conductor.Examples +{ + /// + /// Global level constant variables for examples + /// + public static class ExampleConstants + { + //Co-Pilot example + public const string RANDOMCHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + public const string OPENAITEXT = "open_ai_"; + public const string OPENAI_PROMPTNAME = "chat_function_instructions"; + public const string OPENAIGPT = "gpt-4"; + public const string COPILOTEXECUTION = "copilot_execution"; + public const string COPILOTDESCRIPTION = "copilot execution description"; + public const string CHATCOMPLETEREF = "chat_complete_ref"; + public const string INPUTS = "inputs"; + public const string CREATEWORKFLOW = "create_workflow"; + public const string PROMPTTEMPLATEDESCRIPTION = "chat instructions"; + public const string DYNAMICTASKINPUTPARAM = "dynamicTaskInputParam"; + public const string CHATBOT = "my_chatbot"; + public const string CHATBOTDESCRIPTION = "test_ai_chatgpt"; + public const string LOGMESSAGE = "This is an automated bot that randomly thinks about a scientific discovery and analyzes it further by asking more deeper questions about the topic"; + public const string OPENAICONFIG = "openai config"; + public const string PROMPT_TEXT = @"You are a helpful assistant that can answer questions using tools provided. +You have the following tools specified as functions in .net: +1. get_customer_list() -> Customer (useful to get the list of customers / all the customers / customers) +2. generate_promo_code() -> str (useful to generate a promocode for the customer) +3. send_email(customer: Customer, promo_code: str) (useful when sending an email to a customer, promo code is the output of the generate_promo_code function) +4. get_top_n(n: int, customers: List[Customer]) -> List[Customer] +( +useful to get the top N customers based on their spend. +customers as input can come from the output of get_customer_list function using ${get_customer_list.output.result} +reference. +This function needs a list of customers as input to get the top N. +). +5. create_workflow(steps: List[str], inputs: dict[str, dict]) -> dict +(Useful to chain the function calls. +inputs are: +steps: which is the list of .net functions to be executed +inputs: a dictionary with key as the function name and value as the dictionary object that is given as the input +to the function when calling +). +6. review(input: str) (useful when you wan a human to review something) +note, if you have to execute multiple steps, then you MUST use create_workflow function. +Do not call a function from another function to chain them. + +When asked a question, you can use one of these functions to answer the question if required. + +If you have to call these functions, respond with a .net code that will call this function. +Make sure, when you have to call a function return in the following valid JSON format that can be parsed directly as a json object: +{ +""type"": ""function"", +""function"": ""ACTUAL_.NET_FUNCTION_NAME_TO_CALL_WITHOUT_PARAMETERS"" +""function_parameters"": ""PARAMETERS FOR THE FUNCTION as a JSON map with key as parameter name and value as parameter value"" +} + +Rule: Think about the steps to do this, but your output MUST be the above JSON formatted response. +ONLY send the JSON response - nothing else! +"; + public const string USEROPTIONS = "I am a helpful bot that can help with your customer management. \r\n \r\n Here are some examples:\r\n \r\n " + + "1. Get me the list of top N customers\r\n " + + "2. Get the list of all the customers\r\n " + + "3. Get the list of top N customers and send them a promo code"; + + //OpenAI Keys + public const string OPENAIPROMPTTEXT = "give an evening greeting to ${friend_name}. go: "; + public const string OPENAITASKDEFINITIONNAME = "get_weather_07"; + public const string OPENAITASKDEFNAME = "get_price_from_amazon_07"; + public const string OPENAIPROMPTNAME = "say_hi_to_friend"; + public const string OPENAICHATGPT_PROMPTNAME = "chat_instructions"; + public const string OPEN_AI_PINECONE = "Pinecone_"; + public const string VECTOR_DB_EMBEDDING_MODEL = "text-embedding-ada-002"; + public const string VECTOR_DB_TEXT_COMPLETE_MODEL = "text-davinci-003"; + public const string VECTOR_DB_PROMPT_NAME = "us_constitution_qna"; + public const string OPENAI_LOGMESSAGE = "Output of the LLM chain workflow:"; + public const string VECTOR_DB_PROMPT_TEXT = @"Here is the fragment of the us constitution ${text}. +I have a question ${question}. +Given the text fragment from the constitution - please answer the question. +If you cannot answer from within this context of text then say I don't know."; + + public const string VECTOR_DB_INDEX_REFNAME = "index_text_ref"; + public const string VECTOR_DB_US_NAMESPACE = "us_constitution"; + public const string VECTOR_DB_INDEX = "test"; + public const string VECTOR_DB_TEXT = "hello - how are you?"; + public const string HELLO = "hello"; + public const string DOCID = "hello_1"; + public const string LLM_GENERATE_EMBEDDINGS_TASKREF = "generate_embeddings_ref"; + public const string LLM_QUERY_EMBEDDINGS_TASKREF = "query_vectordb"; + public const string LLM_SEARCH_INDEX_TASKREF = "search_vectordb"; + public const string VECTOR_DB_QUESTION = "what is the first amendment to the constitution?"; + public const string OPENAICHATGPT_PROMPTTEXT = @"You are a helpful bot that knows about science. +You can give answers on the science questions. +Your answers are always in the context of science, if you don't know something, you respond saying you do not know. +Do not answer anything outside of this context - even if the user asks to override these instructions."; + public const string OPENAICHATGPT_QUESTIONGENERATOR_PROMPT = "You are an expert in the scientific knowledge.\r\n " + + "Think of a random scientific discovery and create a question about it."; + public const string OPENAICHATGPT_Q_PROMPTNAME = "generate_science_question"; + public const string OPENAICHATGPT_QUESTIONGENERATOR = @"You are an expert in science and events surrounding major scientific discoveries. +Here the context: +${context +} +And so far we have discussed the following questions: +${past_questions +} +Generate a follow-up question to dive deeper into the topic. Ensure you do not repeat the question from the previous +list to make discussion more broad. +Do not deviate from the topic and keep the question consistent with the theme."; + public const string OPENAICHATGPT_FOLLOWUP_PROMPTNAME = "follow_up_question"; + public const string OPENAI_MESSAGE = @"AI Function call example. +This chatbot is programmed to handle two types of queries: +1. Get the weather for a location +2. Get the price of an item "; + public const string OPENAI_FUNCTION_PROMPTTEXT = @"You are a helpful assistant that can answer questions using tools provided. +You have the following tools specified as functions in .net: +1. get_weather(city:str) -> str (useful to get weather for a city input is the city name or zipcode) +2. get_price_from_amazon(str: item) -> float (useful to get the price of an item from amazon) +When asked a question, you can use one of these functions to answer the question if required. +If you have to call these functions, respond with a .net code that will call this function. +When you have to call a function return in the following valid JSON format that can be parsed using json util: +{ +""type"": ""function"", +""function"": ""ACTUAL_.NET_FUNCTION_NAME_TO_CALL_WITHOUT_PARAMETERS"" +""function_parameters"": ""PARAMETERS FOR THE FUNCTION as a JSON map with key as parameter name and value as parameter value"""; + } +} + diff --git a/Conductor/Examples/GreetingsMain.cs b/Conductor/Examples/GreetingsMain.cs new file mode 100644 index 00000000..dfbb7dd9 --- /dev/null +++ b/Conductor/Examples/GreetingsMain.cs @@ -0,0 +1,36 @@ +using Conductor.Client; +using Conductor.Definition; +using Conductor.Examples.Workers; +using Conductor.Executor; +using System.Threading; + +namespace Conductor.Examples +{ + public class GreetingsMain + { + private readonly Configuration _configuration; + public GreetingsMain() + { + _configuration = new Client.Configuration(); + } + public (ConductorWorkflow, string workflowId) RegisterWorkflow(WorkflowExecutor workflowExecutor) + { + GreetingsWorkflow greetingsWorkflow = new GreetingsWorkflow(); + var workflow = greetingsWorkflow.CreateGreetingsWorkflow(); + workflowExecutor.RegisterWorkflow(workflow, true); + var workflowId = workflowExecutor.StartWorkflow(workflow); + return (workflow, workflowId); + } + + public void GreetingsMainMethod() + { + WorkflowExecutor workflowExecutor = new WorkflowExecutor(_configuration); + (ConductorWorkflow workflow, string workflowId) = RegisterWorkflow(workflowExecutor); + + var waitHandle = new ManualResetEvent(false); + //Remove DynamicWorker once the annotation is in place + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle, new DynamicWorker("greetings_task_test"))); + waitHandle.WaitOne(); + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Orkes/OpenAIChatGpt.cs b/Conductor/Examples/Orkes/OpenAIChatGpt.cs new file mode 100644 index 00000000..2aa920ed --- /dev/null +++ b/Conductor/Examples/Orkes/OpenAIChatGpt.cs @@ -0,0 +1,124 @@ +using conductor.csharp.Client.Extensions; +using conductor.Examples; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Definition.TaskType.LlmTasks; +using Conductor.Examples.Orkes.Workers; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Conductor.Examples.Orkes +{ + public class OpenAIChatGpt + { + private readonly Client.Configuration _configuration; + private readonly Orchestrator _orchestrator; + private readonly OpenAIConfig _openAIConfig; + private readonly WorkflowExecutor _workflowExecutor; + private readonly WorkflowResourceApi _workflowClient; + private readonly ILogger _logger; + + //Const + private const string QUESTIONREF = "gen_question_ref"; + private const string FOLLOWUPQUESTIONREF = "followup_question_ref"; + private const string PROMPTTEMPLATEDESCRIPTION = "Generates a question"; + private const string PROMPTTEMPLATEDESC = "Generates a question about the context"; + + public OpenAIChatGpt() + { + _configuration = new Client.Configuration(); + _openAIConfig = new OpenAIConfig(); + _orchestrator = new Orchestrator(_configuration); + _workflowExecutor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + } + + public void OpenAIChatGPTTest() + { + string llmProvider = ExampleConstants.OPENAITEXT + Environment.UserName; + string chatCompleteModel = ExampleConstants.OPENAIGPT; + _orchestrator.AddAIIntegration(llmProvider, Client.Ai.Configuration.LLMProviderEnum.OPEN_AI, new List { chatCompleteModel }, ExampleConstants.OPENAICONFIG, _openAIConfig); + + _orchestrator.AddPromptTemplate(ExampleConstants.OPENAICHATGPT_PROMPTTEXT, ExampleConstants.PROMPTTEMPLATEDESCRIPTION, ExampleConstants.OPENAICHATGPT_PROMPTNAME); + _orchestrator.AddPromptTemplate(ExampleConstants.OPENAICHATGPT_QUESTIONGENERATOR_PROMPT, PROMPTTEMPLATEDESCRIPTION, ExampleConstants.OPENAICHATGPT_Q_PROMPTNAME); + _orchestrator.AddPromptTemplate(ExampleConstants.OPENAICHATGPT_QUESTIONGENERATOR, PROMPTTEMPLATEDESC, ExampleConstants.OPENAICHATGPT_FOLLOWUP_PROMPTNAME); + + _orchestrator.AssociatePromptTemplate(llmProvider, new List { chatCompleteModel }, ExampleConstants.OPENAICHATGPT_PROMPTNAME); + _orchestrator.AssociatePromptTemplate(llmProvider, new List { chatCompleteModel }, ExampleConstants.OPENAICHATGPT_Q_PROMPTNAME); + _orchestrator.AssociatePromptTemplate(llmProvider, new List { chatCompleteModel }, ExampleConstants.OPENAICHATGPT_FOLLOWUP_PROMPTNAME); + + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(ExampleConstants.CHATBOT) + .WithDescription(ExampleConstants.CHATBOTDESCRIPTION) + .WithVersion(1); + var questionGen = new LlmChatComplete(taskReferenceName: QUESTIONREF, llmProvider: llmProvider, model: chatCompleteModel, instructionsTemplate: ExampleConstants.OPENAICHATGPT_Q_PROMPTNAME, messages: new List(), temperature: 1); + var followUpGen = new LlmChatComplete(taskReferenceName: FOLLOWUPQUESTIONREF, llmProvider: llmProvider, model: chatCompleteModel, instructionsTemplate: ExampleConstants.OPENAICHATGPT_FOLLOWUP_PROMPTNAME, messages: new List()); + ChatMessage chatMessage = new ChatMessage(message: "${chat_complete_ref.input.messages}", role: "user"); + string assistantResult = "${chat_complete_ref.output.result}"; + var collectHistoryTask = Workers.ChatWorkers.CollectHistory(userInput: followUpGen.Output("result"), seedQuestion: questionGen.Output("result"), assistantResponse: assistantResult, history: new List() { chatMessage }); + //We have to update the messages parameter to get the value from "collectHistoryTask.output('result')" + var chatComplete = new LlmChatComplete(taskReferenceName: ExampleConstants.CHATCOMPLETEREF, llmProvider: llmProvider, model: chatCompleteModel, messages: collectHistoryTask, instructionsTemplate: ExampleConstants.OPENAICHATGPT_PROMPTNAME, maxTokens: 2048); + followUpGen.PromptVariable("context", chatComplete.Output("result")); + followUpGen.PromptVariable("past_questions", "${collect_history_ref.input.history[?(@.role=='user')].message}"); + + string collectorJs = ConversationCollector.GetConversation(); + var collect = new JavascriptTask(taskReferenceName: "collect_ref", script: collectorJs); + + //we have to add collector collectHistoryTask once the annotation implementation is done + WorkflowTask[] loopTasks = new WorkflowTask[] { chatComplete, followUpGen }; + var chatLoop = new LoopTask(taskReferenceName: "loop", iterations: 3, loopOver: loopTasks); + + workflow.WithTask(questionGen, chatLoop, collect); + workflow.WithTimeoutPolicy(WorkflowDef.TimeoutPolicyEnum.TIMEOUTWF, 120); + _workflowExecutor.RegisterWorkflow(workflow, true); + + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle)); + waitHandle.WaitOne(); + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", startWorkflow.Name, 1); + _logger.LogInformation(ExampleConstants.LOGMESSAGE); + _logger.LogInformation($"{workflowRun.GetTask(taskReferenceName: questionGen.TaskReferenceName).OutputData["result"]}"); + + string workflowId = workflowRun.WorkflowId; + while (workflowRun.Status != WorkflowRun.StatusEnum.COMPLETED) + { + var workflowResult = _workflowClient.GetWorkflow(workflowId: workflowId, includeTasks: true); + var followUpQ = workflowResult.GetTask(taskReferenceName: followUpGen.TaskReferenceName); + if (followUpQ != null && followUpQ.Status == Client.Models.Task.StatusEnum.FAILEDWITHTERMINALERROR) + { + _logger.LogInformation($"\t>> Thinking about... {followUpQ.OutputData["result"].ToString().Trim()}"); + } + Thread.Sleep(500); + } + + object result = workflowRun.Output["result"]; + + // Convert the 'result' object to JSON with indentation + string output = JsonConvert.SerializeObject(result, Formatting.Indented); + + // Printing the JSON-formatted output to the console + _logger.LogInformation($"\n{output}\n"); + } + } +} diff --git a/Conductor/Examples/Orkes/OpenAIChatUserInput.cs b/Conductor/Examples/Orkes/OpenAIChatUserInput.cs new file mode 100644 index 00000000..493e2bc9 --- /dev/null +++ b/Conductor/Examples/Orkes/OpenAIChatUserInput.cs @@ -0,0 +1,120 @@ +using conductor.csharp.Client.Extensions; +using conductor.Examples; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Definition.TaskType.LlmTasks; +using Conductor.Examples.Orkes.Workers; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Threading; +using Tasks = Conductor.Definition.TaskType.Task; + +namespace Conductor.Examples.Orkes +{ + public class OpenAIChatUserInput + { + private readonly Client.Configuration _configuration; + private readonly Orchestrator _orchestrator; + private readonly OpenAIConfig _openAIConfig; + private readonly WorkflowExecutor _workflowExecutor; + private readonly WorkflowResourceApi _workflowClient; + private readonly TaskResourceApi _taskClient; + private readonly ILogger _logger; + + public OpenAIChatUserInput() + { + _configuration = new Client.Configuration(); + _openAIConfig = new OpenAIConfig(); + _orchestrator = new Orchestrator(_configuration); + _workflowExecutor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + } + + public void OpenAIChatGPTTest() + { + string llmProvider = ExampleConstants.OPENAITEXT + Environment.UserName; + string chatCompleteModel = ExampleConstants.OPENAIGPT; + _orchestrator.AddAIIntegration(llmProvider, Client.Ai.Configuration.LLMProviderEnum.OPEN_AI, new List { chatCompleteModel }, ExampleConstants.OPENAICONFIG, _openAIConfig); + _orchestrator.AddPromptTemplate(ExampleConstants.OPENAICHATGPT_PROMPTTEXT, ExampleConstants.PROMPTTEMPLATEDESCRIPTION, ExampleConstants.OPENAICHATGPT_PROMPTNAME); + _orchestrator.AssociatePromptTemplate(llmProvider, new List { chatCompleteModel }, ExampleConstants.OPENAICHATGPT_PROMPTNAME); + + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(ExampleConstants.CHATBOT) + .WithDescription(ExampleConstants.CHATBOTDESCRIPTION) + .WithVersion(1); + + Tasks userInput = new WaitTask("user_input_ref", new TimeSpan(1)); + string assistantResult = "${chat_complete_ref.output.result}"; + var collectHistoryTask = Workers.ChatWorkers.CollectHistory(userInput: userInput.Output("question"), assistantResponse: assistantResult, history: new List(), seedQuestion: ""); + //We have to update the messages parameter to get the value from "collectHistoryTask.output('result')" + var chatComplete = new LlmChatComplete(taskReferenceName: ExampleConstants.CHATCOMPLETEREF, llmProvider: llmProvider, model: chatCompleteModel, messages: collectHistoryTask, instructionsTemplate: ExampleConstants.OPENAICHATGPT_PROMPTNAME, maxTokens: 2048); + + string collectorJs = ConversationCollector.GetConversation(); + var collect = new JavascriptTask(taskReferenceName: "collect_ref", script: collectorJs); + + //we have to add collector collectHistoryTask once the annotation implementation is done + WorkflowTask[] loopTasks = new WorkflowTask[] { userInput, chatComplete }; + var chatLoop = new LoopTask(taskReferenceName: "loop", iterations: 5, loopOver: loopTasks); + + + workflow.WithTask(chatLoop, collect); + workflow.WithTimeoutPolicy(WorkflowDef.TimeoutPolicyEnum.TIMEOUTWF, 120); + _workflowExecutor.RegisterWorkflow(workflow, true); + + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle)); + waitHandle.WaitOne(); + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", startWorkflow.Name, 1); + _logger.LogInformation(ExampleConstants.LOGMESSAGE); + var workflowId = workflowRun.WorkflowId; + while (workflowRun.Status != WorkflowRun.StatusEnum.COMPLETED) + { + if (workflowRun.CurrentTask.TaskDefName == userInput.TaskReferenceName) + { + var assistantTask = workflowRun.GetTask(taskReferenceName: chatComplete.TaskReferenceName); + if (assistantTask != null) + { + _logger.LogInformation("Assistant", assistantTask.OutputData["result"]); + } + + if (workflowRun.CurrentTask.TaskDefName == userInput.TaskReferenceName) + { + _logger.LogInformation("Ask a Question: >> "); + string question = Console.ReadLine(); + _taskClient.UpdateTaskSync(output: new Dictionary { { "question", question } }, workflowId: workflowId, taskRefName: userInput.TaskReferenceName, status: TaskResult.StatusEnum.COMPLETED); + + } + } + Thread.Sleep(500); + var workflowResult = _workflowClient.GetWorkflow(workflowId: workflowId, includeTasks: true); + object result = workflowResult.Output["result"]; + + // Convert the 'result' object to JSON with indentation + string output = JsonConvert.SerializeObject(result, Formatting.Indented); + + // Printing the JSON-formatted output to the console + _logger.LogInformation($"\n{output}\n"); + } + } + } +} diff --git a/Conductor/Examples/Orkes/OpenAIFunctionExample.cs b/Conductor/Examples/Orkes/OpenAIFunctionExample.cs new file mode 100644 index 00000000..d752ee6c --- /dev/null +++ b/Conductor/Examples/Orkes/OpenAIFunctionExample.cs @@ -0,0 +1,164 @@ +using conductor.csharp.Client.Extensions; +using conductor.Examples; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Definition.TaskType.LlmTasks; +using Conductor.Examples.Orkes.Workers; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Conductor.Examples.Orkes +{ + public class OpenAIFunctionExample + { + private readonly Client.Configuration _configuration; + private readonly Orchestrator _orchestrator; + private readonly OpenAIConfig _openAIConfig; + private readonly WorkflowExecutor _workflowExecutor; + private readonly WorkflowResourceApi _workflowClient; + private readonly TaskResourceApi _taskClient; + private readonly MetadataResourceApi _metadataClient; + private readonly ILogger _logger; + + //const + private const string TASKDEFINITIONDESCRIPTION = "Get Weather Description"; + private const string TASKDEFDESCRIPTION = "Get price from Amazon Description"; + private const string FUNCTIONAICHATBOT = "my_function_chatbot"; + private const string TESTAIFUNCTION = "test_AI_Function"; + + public OpenAIFunctionExample() + { + _configuration = new Client.Configuration(); + _openAIConfig = new OpenAIConfig(); + _orchestrator = new Orchestrator(_configuration); + _workflowExecutor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _metadataClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + //_metadataClient = _orkesApiClient.GetClient(); + + } + + [WorkerTask(taskType: "getWeather", batchSize: 5, domain: "taskDomain", pollIntervalMs: 200, workerId: "workerId")] + public string GetWeather(string city) + { + return $"Weather in {city} today is rainy"; + } + + [WorkerTask(taskType: "getPriceFromAmazon", batchSize: 5, domain: "taskDomain", pollIntervalMs: 200, workerId: "workerId")] + public float GetPriceFromAmazon(string products) + { + return 42.42F; + } + + public void OpenAIFunctionExampleTest() + { + string llmProvider = ExampleConstants.OPENAITEXT + Environment.UserName; + string chatCompleteModel = ExampleConstants.OPENAIGPT; + List taskDefs = new List() +{ +new TaskDef() { Name = ExampleConstants.OPENAITASKDEFINITIONNAME, Description = TASKDEFINITIONDESCRIPTION}, +new TaskDef() { Name = ExampleConstants.OPENAITASKDEFNAME, Description = TASKDEFDESCRIPTION} +}; + + _metadataClient.RegisterTaskDef(taskDefs); + _orchestrator.AddAIIntegration(llmProvider, Client.Ai.Configuration.LLMProviderEnum.OPEN_AI, new List { chatCompleteModel }, "openai config", _openAIConfig); + _orchestrator.AddPromptTemplate(ExampleConstants.OPENAI_PROMPTNAME, ExampleConstants.OPENAI_FUNCTION_PROMPTTEXT, ExampleConstants.PROMPTTEMPLATEDESCRIPTION); + _orchestrator.AssociatePromptTemplate(llmProvider, new List { chatCompleteModel }, ExampleConstants.OPENAI_PROMPTNAME); + + var workflow = new ConductorWorkflow() + .WithName(FUNCTIONAICHATBOT) + .WithDescription(TESTAIFUNCTION) + .WithVersion(1); + + var user_input = new WaitTask("get_user_input", new TimeSpan(1)); + var Message = "${chat_complete_ref.input.messages}"; + + var collectHistoryTask = ChatWorkers.CollectHistory( + userInput: user_input.Output("question"), + assistantResponse: "${chat_complete_ref.output.result}", + history: JsonConvert.DeserializeObject>(Message), + seedQuestion: null); + + var chatComplete = new LlmChatComplete( + taskReferenceName: ExampleConstants.CHATCOMPLETEREF, + llmProvider: llmProvider, + model: chatCompleteModel, + instructionsTemplate: Constants.PROMPTNAME, + //We have to update the messages parameter to get the value from "collectHistoryTask.output('result')" + messages: collectHistoryTask + ); + + var functionCall = new DynamicTask( + taskRefName: "fn_call_ref", + dynamicTask: chatComplete.Output("function") + ); + + functionCall.InputParameters["inputs"] = chatComplete.Output("function_parameters"); + functionCall.InputParameters[ExampleConstants.DYNAMICTASKINPUTPARAM] = ExampleConstants.INPUTS; + + //we have to add collectHistoryTask once the annotation implementation is done + WorkflowTask[] loop_tasks = new WorkflowTask[] { user_input, chatComplete, functionCall }; + var chat_loop = new LoopTask("loop", 3, loop_tasks); + + workflow.WithTask(chat_loop); + + workflow.WithTimeoutPolicy(WorkflowDef.TimeoutPolicyEnum.TIMEOUTWF, 120); + _logger.LogInformation(ExampleConstants.OPENAI_MESSAGE); + _workflowExecutor.RegisterWorkflow(workflow, true); + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle)); + waitHandle.WaitOne(); + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", user_input.TaskReferenceName, 1); + var workflowId = workflowRun.WorkflowId; + + while (workflowRun.Status != WorkflowRun.StatusEnum.COMPLETED) + { + if (workflowRun.CurrentTask.TaskDefName == user_input.TaskReferenceName) + { + var function_call_task = workflowRun.GetTask("fn_call_ref"); + if (function_call_task != null) + { + var assistant = function_call_task.OutputData["result"]; + _logger.LogInformation($"assistant: {assistant}"); + } + + if (workflowRun.CurrentTask.TaskDefName == user_input.TaskReferenceName) + { + _logger.LogInformation("Question: >> "); + string question = Console.ReadLine(); + _taskClient.UpdateTaskSync(new Dictionary { { "question", question } }, workflowId, user_input.TaskReferenceName, TaskResult.StatusEnum.COMPLETED); + } + } + + Thread.Sleep(500); + var workflowResult = _workflowClient.GetWorkflow(workflowId: workflowId, includeTasks: true); + var result = workflowResult.Output["result"]; + string output = JsonConvert.SerializeObject(result, Formatting.Indented); + _logger.LogInformation($"\n{output}\n"); + } + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Orkes/OpenAIHelloworld.cs b/Conductor/Examples/Orkes/OpenAIHelloworld.cs new file mode 100644 index 00000000..4b427063 --- /dev/null +++ b/Conductor/Examples/Orkes/OpenAIHelloworld.cs @@ -0,0 +1,96 @@ +using conductor.csharp.Client.Extensions; +using conductor.Examples; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Definition.TaskType.LlmTasks; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Conductor.Examples.Orkes +{ + public class OpenAIHelloworld + { + private readonly Client.Configuration _configuration; + private readonly Orchestrator _orchestrator; + private readonly OpenAIConfig _openAIConfig; + private readonly WorkflowExecutor _workflowExecutor; + private readonly WorkflowResourceApi _workflowClient; + private readonly ILogger logger; + + //const + private const string TEXTEMBEDDINGCOMPLETEMODEL = "text-embedding-ada-002"; + private const string WORKFLOWNAME = "say_hi_to_the_friend"; + private const string WORKFLOWDESCRIPTION = "test_ai_chatgpt"; + + public OpenAIHelloworld() + { + _configuration = new Client.Configuration(); + _openAIConfig = new OpenAIConfig(); + _orchestrator = new Orchestrator(_configuration); + _workflowExecutor = new WorkflowExecutor(_configuration); + logger = ApplicationLogging.CreateLogger(); + _workflowClient = ApiExtensions.GetClient(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + } + + [WorkerTask("get_friends_name", 5, "taskDomain", 200, "workerId")] + public string GetFriendName() + { + string name = Environment.UserName; + return (string.IsNullOrEmpty(name.Trim())) ? "anonymous" : name; + } + + public void OpenAIHelloworldTest() + { + string llmProvider = ExampleConstants.OPENAITEXT + GetFriendName(); + string textCompleteModel = ExampleConstants.OPENAIGPT; + _orchestrator.AddAIIntegration(llmProvider, Client.Ai.Configuration.LLMProviderEnum.OPEN_AI, new List { textCompleteModel, TEXTEMBEDDINGCOMPLETEMODEL }, "openai config", _openAIConfig); + _orchestrator.AddPromptTemplate(ExampleConstants.OPENAIPROMPTNAME, ExampleConstants.OPENAIPROMPTTEXT, "test prompt"); + _orchestrator.AssociatePromptTemplate(llmProvider, new List { textCompleteModel }, ExampleConstants.OPENAIPROMPTNAME); + + PromptTemplateTestRequest promptTemplateTestRequest = new PromptTemplateTestRequest(); + promptTemplateTestRequest.PromptVariables = new Dictionary { { "friend_name", "Orkes" } }; + promptTemplateTestRequest.LlmProvider = llmProvider; + promptTemplateTestRequest.Model = textCompleteModel; + promptTemplateTestRequest.Prompt = ExampleConstants.OPENAIPROMPTTEXT; + _orchestrator.TestPromptTemplate(promptTemplateTestRequest); + + //Update this logic to use GetFriendName() once the annotaion is in place + var getName = new SimpleTask("get_friends_name", "get_friends_name_ref"); + var textComplete = new LlmTextComplete(taskRefName: "say_hi_ref", llmProvider: llmProvider, model: textCompleteModel, promptName: ExampleConstants.OPENAIPROMPTNAME); + textComplete.PromptVariable("friend_name", getName.Output("result")); + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(WORKFLOWNAME) + .WithDescription(WORKFLOWDESCRIPTION) + .WithVersion(1); + workflow.WithTask(getName); + workflow.WithTask(textComplete); + _workflowExecutor.RegisterWorkflow(workflow, true); + + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle)); + waitHandle.WaitOne(); + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", startWorkflow.Name, 1); + logger.LogInformation($"{ExampleConstants.OPENAI_LOGMESSAGE}: {workflowRun.Output["result"]}\n\n"); + } + } +} diff --git a/Conductor/Examples/Orkes/SyncUpdates.cs b/Conductor/Examples/Orkes/SyncUpdates.cs new file mode 100644 index 00000000..e7a52f41 --- /dev/null +++ b/Conductor/Examples/Orkes/SyncUpdates.cs @@ -0,0 +1,94 @@ +using conductor.csharp.Client.Extensions; +using Conductor.Api; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Examples.Copilot; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; + +namespace Conductor.Examples.Orkes +{ + public class SyncUpdates + { + private readonly Client.Configuration _configuration; + private readonly WorkflowExecutor executor; + private readonly WorkflowResourceApi _workflowClient; + private readonly ILogger _logger; + + public SyncUpdates() + { + _configuration = new Client.Configuration(); + executor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //Local testing + //OrkesApiClient orkesApiClient = new OrkesApiClient(configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + } + public void SyncUpdatesTest() + { + // prepare and Create + var workflow = Create_Workflow(executor); + var startReq = new StartWorkflowRequest() { Name = workflow.Name, Version = workflow.Version }; + var workflowId = executor.StartWorkflow(startReq); + + _logger.LogInformation($"started {workflowId}"); + + var task_result = new TaskResult() + { + Status = TaskResult.StatusEnum.COMPLETED + }; + + var state_update = new WorkflowStateUpdate() + { + TaskReferenceName = "wait_task_ref", + TaskResult = task_result, + Variables = new Dictionary +{ +{ "case", "case1" } +} + }; + + var workflowRun = _workflowClient.UpdateWorkflow(workflowId, state_update, waitForSeconds: 1, + waitUntilTaskRefs: new List() { "wait_task_ref_1", "wait_task_ref_2" }); + + var last_task_ref = workflowRun.Tasks[workflowRun.Tasks.Count - 1].ReferenceTaskName; + _logger.LogInformation($"workflow: {workflowRun.Status}, last task = {last_task_ref}"); + + state_update.TaskReferenceName = last_task_ref; + workflowRun = _workflowClient.UpdateWorkflow(workflowId, state_update, waitForSeconds: 1); + + _logger.LogInformation($"workflow: {workflowRun.Status}, last task = {last_task_ref}"); + } + + public ConductorWorkflow Create_Workflow(WorkflowExecutor executor) + { + var workflow = new ConductorWorkflow() + .WithName("sync_task_variable_updates") + .WithVersion(1). + WithDescription("sync task variable updates"); + + var http = new HttpTask("http_ref", new HttpTaskSettings() { uri = "https://orkes-api-tester.orkesconductor.com/api" }); + var wait = new WaitTask("wait_task_ref", TimeSpan.FromSeconds(1)); + var wait_case_1 = new WaitTask("wait_task_ref_1", TimeSpan.FromSeconds(1)); + var wait_case_2 = new WaitTask("wait_task_ref_2", TimeSpan.FromSeconds(1)); + + var switchTask = new SwitchTask("switch_ref", "${workflow.variables.case}"); + switchTask.WithDecisionCase("case1", wait_case_1); + switchTask.WithDecisionCase("case2", wait_case_2); + + workflow.WithTask(http) + .WithTask(wait) + .WithTask(switchTask); + + executor.RegisterWorkflow(workflow, true); + + return workflow; + } + } +} diff --git a/Conductor/Examples/Orkes/TaskStatusChangeAudit.cs b/Conductor/Examples/Orkes/TaskStatusChangeAudit.cs new file mode 100644 index 00000000..d3837859 --- /dev/null +++ b/Conductor/Examples/Orkes/TaskStatusChangeAudit.cs @@ -0,0 +1,139 @@ +using conductor.csharp.Client.Extensions; +using Conductor.Api; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.Examples.Copilot; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Threading; + +namespace csharp_examples +{ + public class TaskStatusChangeAudit + { + private readonly Conductor.Client.Configuration _configuration; + private readonly WorkflowExecutor executor; + private readonly WorkflowResourceApi _workflowClient; + private readonly MetadataResourceApi _metaDataClient; + private readonly ILogger _logger; + private const string TYPE = "audit_log"; + private const string SIMPLETASK1 = "simple_task_1"; + private const string SIMPLETASK2 = "simple_task_2"; + private const string SIMPLETYPE = "SIMPLE"; + private const string SIMPLE_TASK2_REF_NAME = "simple_task_2_ref"; + private const string SIMPLE_TASK1_REF_NAME = "simple_task_1_ref"; + private const string WORKFLOW_DEF_NAME = "test_audit_logs"; + + public TaskStatusChangeAudit() + { + _configuration = new Conductor.Client.Configuration(); + executor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _metaDataClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //Local testing + //OrkesApiClient orkesApiClient = new OrkesApiClient(configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + //_metaDataClient = _orkesApiClient.GetClient(); + } + + [WorkerTask("audit_log", 5, "taskDomain", 200, "workerId")] + public void AuditLog(object workflowInput, string status, string name) + { + _logger.LogInformation($"task {name} is in {status} status, with workflow input as {workflowInput}"); + } + + [WorkerTask("simple_task_1", 5, "taskDomain", 200, "workerId")] + public static string SimpleTask1(Task task) + { + return "OK"; + } + + [WorkerTask("simple_task_2", 5, "taskDomain", 200, "workerId")] + public static TaskResult SimpleTask2(Task task) + { + return new TaskResult { Status = TaskResult.StatusEnum.FAILEDWITHTERMINALERROR }; + } + + public void TaskStatusChangeAuditTest() + { + var workflowDef = new WorkflowDef() { Name = WORKFLOW_DEF_NAME, Version = 1 }; + // Create an instance of StateChangeEvent + StateChangeEvent stateChangeEvent = new StateChangeEvent( + type: TYPE, + payload: new Dictionary + { + { "workflow_input", "${workflow.input}" }, + { "status", "${simple_task_1_ref.status}" }, + { "name", SIMPLE_TASK1_REF_NAME } + }); + + var task1 = new WorkflowTask() + { + Type = SIMPLETYPE, + Name = SIMPLETASK1, + TaskReferenceName = SIMPLE_TASK1_REF_NAME, + OnStateChange = new Dictionary(){ + { + "", new StateChangeConfig(eventType: new List { StateChangeEventType.OnStart }, events: new List() { stateChangeEvent }) + } + } + }; + + var task_def = new TaskDef(); + task_def.Name = SIMPLETASK2; + task_def.RetryCount = 0; + + StateChangeEvent stateChangeEvent2 = new StateChangeEvent( + type: TYPE, + payload: new Dictionary + { + { "workflow_input", "${workflow.input}" }, + { "status", "${simple_task_2_ref.status}" }, + { "name", SIMPLE_TASK2_REF_NAME } + }); + var task2 = new WorkflowTask() + { + Type = SIMPLETYPE, + Name = SIMPLETASK2, + TaskReferenceName = SIMPLE_TASK2_REF_NAME, + TaskDefinition = task_def, + OnStateChange = new Dictionary(){ + { + "", new StateChangeConfig(eventType: new List(){ + StateChangeEventType.OnStart, + StateChangeEventType.OnFailed, + StateChangeEventType.OnScheduled, + },events: new List() { stateChangeEvent2 }) + } + } + }; + + workflowDef.Tasks.Add(task1); + workflowDef.Tasks.Add(task2); + + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Conductor.Examples.Utils.WorkerUtil.StartBackGroundTask(waitHandle)); + waitHandle.WaitOne(); + + executor.RegisterWorkflow(workflowDef, true); + + var startReq = new StartWorkflowRequest() + { + Name = workflowDef.Name, + Version = workflowDef.Version, + Input = { + { "a", "aa" }, + { "b", "bb" }, + { "c", 42 } + } + }; + + var workflowId = executor.StartWorkflow(startReq); + _logger.LogInformation(workflowId); + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Orkes/VectorDbHelloWorld.cs b/Conductor/Examples/Orkes/VectorDbHelloWorld.cs new file mode 100644 index 00000000..fecd62cd --- /dev/null +++ b/Conductor/Examples/Orkes/VectorDbHelloWorld.cs @@ -0,0 +1,144 @@ +using conductor.Examples; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.DefinitaskNametion.TaskType.LlmTasks; +using Conductor.Definition; +using Conductor.Definition.TaskType.LlmTasks; +using Conductor.Definition.TaskType.LlmTasks.Utils; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Conductor.Examples.Orkes +{ + public class VectorDBHelloWorld + { + private readonly Conductor.Client.Configuration _configuration; + private readonly Orchestrator _orchestrator; + private readonly OpenAIConfig _openAIConfig; + private readonly WorkflowExecutor _workflowExecutor; + private readonly WorkflowResourceApi _workflowClient; + private readonly ILogger logger; + private const string WorlfowName = "test_vector_db"; + private const string Description = "Test-ai_vectorDB"; + + public VectorDBHelloWorld() + { + _configuration = new Conductor.Client.Configuration(); + _openAIConfig = new OpenAIConfig(); + _orchestrator = new Orchestrator(_configuration); + _workflowExecutor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + } + + [WorkerTask("get_friends_name", 5, "taskDomain", 200, "workerId")] + public string GetFriendName() + { + string name = Environment.UserName; + return (string.IsNullOrEmpty(name.Trim())) ? "anonymous" : name; + } + + public void VectorDBHelloWoroldTest() + { + string vectorDB = ExampleConstants.OPEN_AI_PINECONE + Environment.UserName; + string llmProvider = ExampleConstants.OPENAITEXT + Environment.UserName; + string embeddingModel = ExampleConstants.VECTOR_DB_EMBEDDING_MODEL; + string textCompleteModel = ExampleConstants.VECTOR_DB_TEXT_COMPLETE_MODEL; + string chatCompleteModel = ExampleConstants.OPENAIGPT; + var openAIConfig = new OpenAIConfig(); + + _orchestrator.AddVectorStore(dbIntegrationName: vectorDB, provider: Conductor.Client.Ai.Configuration.VectorDBEnum.PINECONE_DB, + indices: new List { "hello_world" }, description: "pinecone db", config: new PineconeConfig()); + + var promptName = ExampleConstants.VECTOR_DB_PROMPT_NAME; + var promptText = ExampleConstants.VECTOR_DB_PROMPT_TEXT; + + _orchestrator.AddPromptTemplate(name: promptName, promptTemplate: promptText, description: ExampleConstants.VECTOR_DB_PROMPT_NAME); + _orchestrator.AssociatePromptTemplate(PromptName: promptName, integrationProvider: llmProvider, aiModels: new List { textCompleteModel }); + + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(WorlfowName) + .WithDescription(Description) + .WithVersion(1); + _workflowExecutor.RegisterWorkflow(workflow, true); + + var indexText = new LlmIndexText(taskReferenceName: ExampleConstants.VECTOR_DB_INDEX_REFNAME, vectorDB: vectorDB, index: ExampleConstants.VECTOR_DB_INDEX, nameSpace: ExampleConstants.HELLO, text: ExampleConstants.VECTOR_DB_TEXT, + embeddingModel: new EmbeddingModel(provider: llmProvider, model: embeddingModel), docid: ExampleConstants.DOCID, + metaData: new Dictionary() + { + { + "doctype","testing only" + } + + }); + + EmbeddingModel model = new EmbeddingModel(provider: llmProvider, model: embeddingModel); + var indexDocument = new LlmIndexDocuments(taskReferenceName: ExampleConstants.VECTOR_DB_INDEX_REFNAME, vectorDB: vectorDB, index: ExampleConstants.VECTOR_DB_INDEX, nameSpace: ExampleConstants.VECTOR_DB_US_NAMESPACE, + embeddingModel: model, + embeddingModelProvider: model.Provider, + url: "https://constitutioncenter.org/media/files/constitution.pdf", + mediaType: "application/pdf", + docId: ExampleConstants.VECTOR_DB_US_NAMESPACE, + metaData: + new Dictionary() + { + { + "doc_url","https://constitutioncenter.org/media/files/constitution.pdf" + } + }); + + var generateEmbeddings = new LlmGenerateEmbeddings(taskReferenceName: ExampleConstants.LLM_GENERATE_EMBEDDINGS_TASKREF, llmProvider: llmProvider, model: embeddingModel, text: "xxxxxxxx"); + string outputString = generateEmbeddings.Output("result[0]"); + List embeddings = new List(); + if (int.TryParse(outputString, out int outputInt)) + { + embeddings.Add(outputInt); + } + + var queryIndex = new LlmQueryEmbeddings(taskReferenceName: ExampleConstants.LLM_QUERY_EMBEDDINGS_TASKREF, vectorDB: vectorDB, index: ExampleConstants.VECTOR_DB_INDEX, nameSpace: ExampleConstants.VECTOR_DB_US_NAMESPACE, embeddings: embeddings); + + var searchIndex = new LlmSearchIndex(taskReferenceName: ExampleConstants.LLM_SEARCH_INDEX_TASKREF, vectorDB: vectorDB, nameSpace: ExampleConstants.VECTOR_DB_US_NAMESPACE, index: ExampleConstants.VECTOR_DB_INDEX, embeddingModel: embeddingModel, + embeddingModelProvider: llmProvider, query: ExampleConstants.VECTOR_DB_QUESTION, MaxResults: 2); + + var textComplete = new LlmTextComplete(taskRefName: ExampleConstants.VECTOR_DB_PROMPT_NAME, llmProvider: llmProvider, model: textCompleteModel, promptName: promptName); + + var chatComplete = new LlmChatComplete(taskReferenceName: ExampleConstants.CHATCOMPLETEREF, llmProvider: llmProvider, model: chatCompleteModel, instructionsTemplate: promptName, + messages: new List { new ChatMessage(role: "user", message: ExampleConstants.VECTOR_DB_QUESTION) }); + + chatComplete.PromptVariable(variable: "text", value: searchIndex.Output("result..text")); + chatComplete.PromptVariable("question", ExampleConstants.VECTOR_DB_QUESTION); + + textComplete.PromptVariable(variable: "text", value: searchIndex.Output("result..text")); + textComplete.PromptVariable("question", ExampleConstants.VECTOR_DB_QUESTION); + + workflow.WithTask(searchIndex); + workflow.WithTask(chatComplete); + + _workflowExecutor.RegisterWorkflow(workflow, true); + + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle)); + waitHandle.WaitOne(); + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", startWorkflow.Name, 1); + logger.LogInformation($"{ExampleConstants.OPENAI_LOGMESSAGE} {workflowRun.Output["result"]}\n\n"); + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Orkes/WaitForWebhook.cs b/Conductor/Examples/Orkes/WaitForWebhook.cs new file mode 100644 index 00000000..65cbc34f --- /dev/null +++ b/Conductor/Examples/Orkes/WaitForWebhook.cs @@ -0,0 +1,108 @@ +using conductor.csharp.Client.Extensions; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Ai; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Examples.Orkes; +using Conductor.Examples.Workers; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Threading; + +namespace Conductor.Examples +{ + public class WaitForWebhook + { + private readonly Client.Configuration _configuration; + private readonly Orchestrator _orchestrator; + private readonly OpenAIConfig _openAIConfig; + private readonly WorkflowExecutor _workflowExecutor; + private readonly WorkflowResourceApi _workflowClient; + private readonly MetadataResourceApi _metadataClient; + private readonly ILogger _logger; + + //Const + private const string WORKFLOWNAME = "dynamic_workflow"; + private const string WORKFLOWDESC = "test_dynamic_workflow"; + + public WaitForWebhook() + { + _configuration = new Client.Configuration(); + _workflowExecutor = new WorkflowExecutor(_configuration); + _workflowClient = ApiExtensions.GetClient(); + _metadataClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //For local testing + //var _orkesApiClient = new OrkesApiClient(_configuration, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //_workflowClient = _orkesApiClient.GetClient(); + //_metaDataClient = _orkesApiClient.GetClient(); + } + + [WorkerTask(taskType: "GetEmail", 5, "taskDomain", 520, "workerId")] + public string GetUserEmail(string userId) + { + return $"{userId}@example.com"; + } + + [WorkerTask(taskType: "SendEmail", 5, "taskDomain", 520, "workerId")] + public string SendEmail(string email, string subject, string body) + { + return $"sending email to {email} with subject {subject} and body {body}"; + } + + public void WaitForWebhookTest() + { + ConductorWorkflow workflow = new ConductorWorkflow() + .WithName(WORKFLOWNAME) + .WithDescription(WORKFLOWDESC) + .WithVersion(1); + + workflow.WithInputParameter("userId"); + + //Update this line to use GetUserEmail() once the annotation is in place + var getEmailTask = new SimpleTask("GetEmail", "GetEmail").WithInput("userId", workflow.Input("userId")); + getEmailTask.Description = "Test Get email"; + + workflow.WithTask(getEmailTask); + + ////Update this line to use SendEmail() once the annotation is in place + var SendEmailTask = new SimpleTask("SendEmail", "Send_Email_refTask") + .WithInput("email", getEmailTask.Output("userId")) + .WithInput("subject", "Hello from Orkes") + .WithInput("body", "Test Email"); + + workflow.WithTask(SendEmailTask); + + var WaitForWebhookTask = new WaitForWebHookTask("wait_ref", new Dictionary { { "type", "customer" }, { "id", workflow.Input("userId") } }); + workflow.WithTask(WaitForWebhookTask); + + _workflowExecutor.RegisterWorkflow(workflow, true); + + var testInput = new Dictionary + { + { "userId", "Test" } + }; + + StartWorkflowRequest startWorkflow = new StartWorkflowRequest() + { + Name = workflow.Name, + Input = testInput, + Version = workflow.Version, + WorkflowDef = workflow, + CreatedBy = Constants.OWNER_EMAIL + }; + + var workflowRun = _workflowClient.ExecuteWorkflow(startWorkflow, "1234", startWorkflow.Name, 1); + var waitHandle = new ManualResetEvent(false); + var backgroundTask = System.Threading.Tasks.Task.Run(async () => await Utils.WorkerUtil.StartBackGroundTask(waitHandle, new DynamicWorker("GetEmail"))); + waitHandle.WaitOne(); + _logger.LogInformation($"\nworkflow execution {workflowRun.WorkflowId}\n"); + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Orkes/Workers/ChatWorkers.cs b/Conductor/Examples/Orkes/Workers/ChatWorkers.cs new file mode 100644 index 00000000..9cb998e6 --- /dev/null +++ b/Conductor/Examples/Orkes/Workers/ChatWorkers.cs @@ -0,0 +1,39 @@ +using Conductor.Client.Worker; +using Conductor.Definition.TaskType.LlmTasks; +using System.Collections.Generic; + +namespace Conductor.Examples.Orkes.Workers +{ + public class ChatWorkers + { + public const string USERROLE = "user"; + public const string ASSISTANTROLE = "assistant"; + + [WorkerTask(taskType: "prep", 5, "taskDomain", 2000, "workerId")] + public static List CollectHistory(string userInput, string seedQuestion, string assistantResponse, List history) + { + var allHistory = new List(); + + if (history != null) + { + allHistory.AddRange(history); + } + + if (assistantResponse != null) + { + allHistory.Add(new ChatMessage(message: assistantResponse, role: ASSISTANTROLE)); + } + + if (userInput != null) + { + allHistory.Add(new ChatMessage(message: userInput, role: USERROLE)); + } + else + { + allHistory.Add(new ChatMessage(message: seedQuestion, role: USERROLE)); + } + + return allHistory; + } + } +} diff --git a/Conductor/Examples/Orkes/Workers/ConversationCollector.cs b/Conductor/Examples/Orkes/Workers/ConversationCollector.cs new file mode 100644 index 00000000..fb19201e --- /dev/null +++ b/Conductor/Examples/Orkes/Workers/ConversationCollector.cs @@ -0,0 +1,34 @@ +namespace Conductor.Examples.Orkes.Workers +{ + public static class ConversationCollector + { + public static string CollectorJs { get; } + + static ConversationCollector() + { + CollectorJs = @" + (function(){ + var history = $.history; + var lastAnswer = $.last_answer; + var conversation = []; + for(var i = 0; i < history.length - 1; i += 2) { + conversation.push({ + 'question': history[i].message, + 'answer': history[i + 1].message + }); + } + conversation.push({ + 'question': history[i].message, + 'answer': lastAnswer + }); + return conversation; + })(); + "; + } + + public static string GetConversation() + { + return CollectorJs; + } + } +} diff --git a/Conductor/Examples/Orkes/WorkflowRerun.cs b/Conductor/Examples/Orkes/WorkflowRerun.cs new file mode 100644 index 00000000..7b8063a6 --- /dev/null +++ b/Conductor/Examples/Orkes/WorkflowRerun.cs @@ -0,0 +1,100 @@ +using conductor.csharp.Client.Extensions; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.IO; + +namespace Conductor.Examples.Orkes +{ + public class WorkflowRerun + { + private readonly WorkflowExecutor _executor; + private readonly Configuration _Config; + private readonly MetadataResourceApi _metaDataClient; + private readonly WorkflowResourceApi _workflowClient; + private readonly ILogger _logger; + private const string TASK_REFRENECE_NAME1 = "simple_task_ref1_case1_1"; + private const string TASK_REFRENECE_NAME2 = "simple_task_ref1_case1_2"; + private const string NAME = "rerun_test"; + + public WorkflowRerun() + { + _Config = new Configuration(); + _executor = new WorkflowExecutor(_Config); + _metaDataClient = ApiExtensions.GetClient(); + _workflowClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + } + + /// + /// Method to register the workflow + /// + public void ReadAndRegisterWorkflow() + { + using (StreamReader file = File.OpenText("ReRunWorkflow.json")) + { + JsonSerializer serializer = new JsonSerializer(); + WorkflowDef workflow = (WorkflowDef)serializer.Deserialize(file, typeof(WorkflowDef)); + _logger.LogInformation($"loaded workflow {workflow}"); + _metaDataClient.UpdateWorkflowDefinitions(body: new List { workflow }, overwrite: true); + } + } + + /// + /// Method to start the workflow + /// + /// + public string StartWorkflow() + { + var startReq = new StartWorkflowRequest() + { + Name = NAME, + Version = 1, + Input = new Dictionary() { { "case", "case1" } } + }; + + var workflowId = _executor.StartWorkflow(startReq); + return workflowId; + } + + /// + /// Method to test the work flow reeun scenario + /// + public void WorkflowRerunTest() + { + // Let's load up the workflow definition from a file and register + ReadAndRegisterWorkflow(); + + var workflowId = StartWorkflow(); + _logger.LogInformation($"started workflow with id {workflowId}"); + + var updateRequest = new WorkflowStateUpdate() + { + TaskReferenceName = TASK_REFRENECE_NAME1, + TaskResult = new TaskResult() { Status = TaskResult.StatusEnum.COMPLETED }, + }; + + var workflowRun = _workflowClient.UpdateWorkflow(workflowId, updateRequest, + waitUntilTaskRefs: new List() { TASK_REFRENECE_NAME2 }, waitForSeconds: 0); + + updateRequest.TaskReferenceName = TASK_REFRENECE_NAME2; + + workflowRun = _workflowClient.UpdateWorkflow(workflowId, updateRequest, + waitUntilTaskRefs: new List() { TASK_REFRENECE_NAME1 }, waitForSeconds: 0); + + var task = workflowRun.GetTask(taskReferenceName: TASK_REFRENECE_NAME2); + + var rerunReq = new RerunWorkflowRequest() + { + ReRunFromTaskId = task.TaskId + }; + + _workflowClient.Rerun(rerunReq, workflowId); + } + } +} \ No newline at end of file diff --git a/Conductor/Examples/Utils/ReRunWorkflow.json b/Conductor/Examples/Utils/ReRunWorkflow.json new file mode 100644 index 00000000..f815b7ac --- /dev/null +++ b/Conductor/Examples/Utils/ReRunWorkflow.json @@ -0,0 +1,107 @@ +{ + "name": "rerun_test", + "description": "rerun_test", + "version": 1, + "tasks": [ + { + "name": "http_task", + "taskReferenceName": "http_task_ref", + "inputParameters": { + "http_request": { + "uri": "https://orkes-api-tester.orkesconductor.com/api", + "method": "GET", + "accept": "application/json", + "contentType": "application/json" + } + }, + "type": "HTTP" + }, + { + "name": "switch_task_1", + "taskReferenceName": "switch_task_ref_1", + "inputParameters": { + "switchCaseValue": "${workflow.input.case}" + }, + "type": "SWITCH", + "decisionCases": { + "case1": [ + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref1_case1_1", + "inputParameters": {}, + "type": "SIMPLE" + }, + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref1_case1_2", + "inputParameters": {}, + "type": "SIMPLE" + } + ], + "case2": [ + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref1_case2_1", + "inputParameters": {}, + "type": "SIMPLE" + }, + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref1_case2_2", + "inputParameters": {}, + "type": "SIMPLE" + } + ] + }, + "evaluatorType": "value-param", + "expression": "switchCaseValue" + }, + { + "name": "switch_task_2", + "taskReferenceName": "switch_task_ref_2", + "inputParameters": { + "switchCaseValue": "${workflow.input.case}" + }, + "type": "SWITCH", + "decisionCases": { + "case1": [ + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref2_case1_1", + "inputParameters": {}, + "type": "SIMPLE" + }, + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref2_case1_2", + "inputParameters": {}, + "type": "SIMPLE" + } + ], + "case2": [ + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref2_case2_1", + "inputParameters": {}, + "type": "SIMPLE" + }, + { + "name": "simple_task", + "taskReferenceName": "simple_task_ref2_case2_2", + "inputParameters": {}, + "type": "SIMPLE" + } + ] + }, + "evaluatorType": "value-param", + "expression": "switchCaseValue" + } + ], + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} +} \ No newline at end of file diff --git a/Conductor/Examples/Utils/WorkerUtil.cs b/Conductor/Examples/Utils/WorkerUtil.cs new file mode 100644 index 00000000..85556e62 --- /dev/null +++ b/Conductor/Examples/Utils/WorkerUtil.cs @@ -0,0 +1,36 @@ +using Conductor.Client.Extensions; +using Conductor.Client.Interfaces; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Conductor.Examples.Utils +{ + public static class WorkerUtil + { + /// + /// Method to start the background job + /// + /// + /// + /// + public static async Task StartBackGroundTask(ManualResetEvent waitHandle, params IWorkflowTask[] workers) + { + try + { + var host = WorkflowTaskHost.CreateWorkerHost(Microsoft.Extensions.Logging.LogLevel.Information, workers); + await host.StartAsync(); + Thread.Sleep(10000); + waitHandle.Set(); + await host.StopAsync(); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"Error in background task: {ex.Message}"); + waitHandle.Set(); + return false; + } + } + } +} diff --git a/Conductor/Examples/Workers/DynamicWorker.cs b/Conductor/Examples/Workers/DynamicWorker.cs new file mode 100644 index 00000000..841bacb8 --- /dev/null +++ b/Conductor/Examples/Workers/DynamicWorker.cs @@ -0,0 +1,32 @@ +using Conductor.Client.Extensions; +using Conductor.Client.Interfaces; +using Conductor.Client.Models; +using Conductor.Client.Worker; +using System; +using System.Threading; + +namespace Conductor.Examples.Workers +{ + public class DynamicWorker : IWorkflowTask + { + public string TaskType { get; } + + public WorkflowTaskExecutorConfiguration WorkerSettings { get; } + + public DynamicWorker(string taskType = "workflow-task") + { + TaskType = taskType; + WorkerSettings = new WorkflowTaskExecutorConfiguration(); + } + + public System.Threading.Tasks.Task Execute(Client.Models.Task task, CancellationToken token = default) + { + return System.Threading.Tasks.Task.FromResult(task.Completed()); + } + + public TaskResult Execute(Client.Models.Task task) + { + throw new NotImplementedException(); + } + } +} diff --git a/Conductor/Examples/Workers/GreetingsWorkflow.cs b/Conductor/Examples/Workers/GreetingsWorkflow.cs new file mode 100644 index 00000000..34267c7e --- /dev/null +++ b/Conductor/Examples/Workers/GreetingsWorkflow.cs @@ -0,0 +1,29 @@ +using Conductor.Client.Worker; +using Conductor.Definition; +using Conductor.Definition.TaskType; + +namespace Conductor.Examples +{ + public class GreetingsWorkflow + { + private const string WorkflowName = "Test_Workflow_Greeting"; + private const string WorkflowDescription = "test description"; + + [WorkerTask("greetings_task", 5, "taskDomain", 420, "workerId")] + public string Greet(string name) + { + return $"Hello {name}"; + } + + public ConductorWorkflow CreateGreetingsWorkflow() + { + var wf = new ConductorWorkflow() + .WithName(WorkflowName) + .WithDescription(WorkflowDescription) + .WithVersion(1) + //Here call Greet() instead of creating SimpleTask manually. + .WithTask(new SimpleTask("greetings_task_test", "greet_ref_test")); + return wf; + } + } +} \ No newline at end of file diff --git a/Tests/Definition/WorkflowDefinitionTests.cs b/Tests/Definition/WorkflowDefinitionTests.cs index 4feaec69..89cbb169 100644 --- a/Tests/Definition/WorkflowDefinitionTests.cs +++ b/Tests/Definition/WorkflowDefinitionTests.cs @@ -4,6 +4,7 @@ using Conductor.Definition.TaskType; using Conductor.Executor; using System; +using System.Collections.Generic; using Xunit; namespace Tests.Definition @@ -64,6 +65,7 @@ private ConductorWorkflow GetConductorWorkflow() .WithTask(GetWaitTask()) .WithTask(GetSetVariableTask()) .WithTask(GetTerminateTask()) + .WithTask(GetWaitForWebhookTask()) ; } @@ -183,5 +185,12 @@ private WorkflowTask GetSwitchTask(string taskReferenceName = "switch_task_refer "variable", "${workflow.input." + WORKFLOW_INPUT_PARAMETER + "}" ); } + + private WorkflowTask GetWaitForWebhookTask(string taskRefernceName = "wair_for_webhook_task_reference_name", Dictionary matches = null) + { + return new WaitForWebHookTask( + taskReferenceName: taskRefernceName, + matches: matches ?? new Dictionary()); + } } } diff --git a/Tests/Worker/TestWorkflows.cs b/Tests/Worker/TestWorkflows.cs new file mode 100644 index 00000000..3b3aeff5 --- /dev/null +++ b/Tests/Worker/TestWorkflows.cs @@ -0,0 +1,99 @@ +using conductor.csharp.Client.Extensions; +using Conductor.Api; +using Conductor.Client; +using Conductor.Client.Extensions; +using Conductor.Client.Models; +using Conductor.Definition; +using Conductor.Definition.TaskType; +using Conductor.Executor; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using Xunit; + +namespace conductor_csharp.test.Worker +{ + public class TestWorkflows + { + private readonly OrkesApiClient _orkesApiClient; + private readonly WorkflowResourceApi workflowClient; + private readonly WorkflowExecutor _workflowExecutor; + private ILogger _logger; + + public TestWorkflows() + { + var config = new Configuration(); + _workflowExecutor = new WorkflowExecutor(config); + workflowClient = ApiExtensions.GetClient(); + _logger = ApplicationLogging.CreateLogger(); + + //For local testing + //_orkesApiClient = new OrkesApiClient(config, new OrkesAuthenticationSettings(Constants.KEY_ID, Constants.KEY_SECRET)); + //workflowClient = _orkesApiClient.GetClient(); + } + + [Fact] + public void TestWorkflowExecution() + { + var workflow = new ConductorWorkflow() + .WithName("unit_testing_example") + .WithDescription("test unit test") + .WithVersion(1); + var task1 = new SimpleTask("hello_C_1", "hello_ref_C_1"); + var task2 = new SimpleTask("hello_C_2", "hello_ref_C_2"); + var task3 = new SimpleTask("hello_C_3", "hello_ref_C_3"); + + + var decision = new SwitchTask("switch_ref", task1.Output("city")); + decision.WithDecisionCase("NYC", task2); + decision.WithDefaultCase(task3); + + var http = new HttpTask("http", new HttpTaskSettings { uri = "https://orkes-api-tester.orkesconductor.com/api" }); + workflow.WithTask(http); + workflow.WithTask(task1); + workflow.WithTask(decision); + + var taskRefToMockOutput = new Dictionary>(); + + taskRefToMockOutput[task1.TaskReferenceName] = new List +{ +new TaskMock { ExecutionTime= 1, Status = TaskMock.StatusEnum.FAILED, QueueWaitTime= 10, Output = new Dictionary {{ "key", "failed" }}}, +new TaskMock{ ExecutionTime= 1, Status = TaskMock.StatusEnum.COMPLETED, QueueWaitTime=10, Output = new Dictionary {{"city", "NYC"}}} +}; + + taskRefToMockOutput[task2.TaskReferenceName] = new List +{ +new TaskMock{ ExecutionTime= 1, Status = TaskMock.StatusEnum.COMPLETED, QueueWaitTime= 10, Output = new Dictionary < string, Object > {{ "key", "task2.output" }}} +}; + + taskRefToMockOutput[http.TaskReferenceName] = new List +{ +new TaskMock{ ExecutionTime= 1, Status = TaskMock.StatusEnum.COMPLETED, QueueWaitTime= 10, Output = new Dictionary {{"key", "http.output"}}} +}; + + _workflowExecutor.RegisterWorkflow(workflow, true); + + var testRequest = new WorkflowTestRequest(name: workflow.Name, version: workflow.Version, taskRefToMockOutput: taskRefToMockOutput, workflowDef: workflow); + var run = workflowClient.TestWorkflow(testRequest); + + _logger.LogInformation($"Completed the test run {run}"); + _logger.LogInformation($"Status: {run.Status}"); + Assert.Equal("COMPLETED", run.Status.ToString()); + + _logger.LogInformation($"First task (HTTP) status: {run.Tasks[0].TaskType}"); + Assert.Equal("HTTP", run.Tasks[0].TaskType); + + _logger.LogInformation($"{run.Tasks[1].ReferenceTaskName} status: {run.Tasks[1].Status} (expected to be FAILED)"); + Assert.Equal("FAILED", run.Tasks[1].Status.ToString()); + + _logger.LogInformation($"{run.Tasks[2].ReferenceTaskName} status: {run.Tasks[2].Status} (expected to be COMPLETED)"); + Assert.Equal("COMPLETED", run.Tasks[2].Status.ToString()); + + _logger.LogInformation($"{run.Tasks[4].ReferenceTaskName} status: {run.Tasks[4].Status} (expected to be COMPLETED)"); + Assert.Equal("COMPLETED", run.Tasks[4].Status.ToString()); + + //Assert that task2 was executed + Assert.Equal(task2.TaskReferenceName, run.Tasks[4].ReferenceTaskName); + } + } +} \ No newline at end of file diff --git a/csharp-examples/WorkFlowExamples.cs b/csharp-examples/WorkFlowExamples.cs index 3b55d90a..c18e5edd 100644 --- a/csharp-examples/WorkFlowExamples.cs +++ b/csharp-examples/WorkFlowExamples.cs @@ -5,6 +5,8 @@ using Conductor.Api; using Conductor.Client.Authentication; using Newtonsoft.Json; +using Conductor.Examples; +using Conductor.Examples.Copilot; namespace csharp_examples { @@ -23,6 +25,27 @@ public class WorkFlowExamples private const string VARIABLE_NAME_2 = ""; private const string VARIABLE_NEW_VALUE_2 = ""; + //Method to tets dynamic workflow + public void TestDynamicWorkFlow() + { + DynamicWorkflow dynamicWorkflow = new DynamicWorkflow(); + dynamicWorkflow.DynamicWorkFlowMain(); + } + + //Method to test greetings workflow + public void TestGreetingsWorkFlow() + { + GreetingsMain greetingsMain = new GreetingsMain(); + greetingsMain.GreetingsMainMethod(); + } + + //Method to test OpenAICopilot example + public void TestOpenAICopilot() + { + OpenAICopilot openAICopilotTest = new OpenAICopilot(); + openAICopilotTest.OpenAICopilotTest(); + } + public void RegisterWorkFlow() { // Method-1 for using custom serialization settings - START