diff --git a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs index ba2327b3a0f5..99429133c4d9 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs @@ -9,6 +9,7 @@ using Microsoft.SemanticKernel.AI.TextCompletion; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Orchestration; +using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SemanticFunctions; using Microsoft.SemanticKernel.SkillDefinition; using Moq; @@ -144,6 +145,73 @@ public async Task ItCanCreatePlanAsync(string goal) } } + [Fact] + public async Task EmptyGoalThrowsAsync() + { + // Arrange + var kernel = new Mock(); + // kernel.Setup(x => x.Log).Returns(new Mock().Object); + + var planner = new Microsoft.SemanticKernel.Planning.SequentialPlanner(kernel.Object); + + // Act + await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("")); + } + + [Fact] + public async Task InvalidXMLThrowsAsync() + { + // Arrange + var kernel = new Mock(); + // kernel.Setup(x => x.Log).Returns(new Mock().Object); + var memory = new Mock(); + var skills = new Mock(); + + var functionsView = new FunctionsView(); + skills.Setup(x => x.GetFunctionsView(It.IsAny(), It.IsAny())).Returns(functionsView); + + var planString = + @"notvalid<"; + var returnContext = new SKContext( + new ContextVariables(planString), + memory.Object, + skills.Object, + new Mock().Object + ); + + var context = new SKContext( + new ContextVariables(), + memory.Object, + skills.Object, + new Mock().Object + ); + + var mockFunctionFlowFunction = new Mock(); + mockFunctionFlowFunction.Setup(x => x.InvokeAsync( + It.IsAny(), + null, + null, + null + )).Callback( + (c, s, l, ct) => c.Variables.Update("Hello world!") + ).Returns(() => Task.FromResult(returnContext)); + + // Mock Skills + kernel.Setup(x => x.Skills).Returns(skills.Object); + kernel.Setup(x => x.CreateNewContext()).Returns(context); + + kernel.Setup(x => x.RegisterSemanticFunction( + It.IsAny(), + It.IsAny(), + It.IsAny() + )).Returns(mockFunctionFlowFunction.Object); + + var planner = new Microsoft.SemanticKernel.Planning.SequentialPlanner(kernel.Object); + + // Act + await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("goal")); + } + // Method to create Mock objects private static Mock CreateMockFunction(FunctionView functionView) { diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanner.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanner.cs index ebbf98409989..61a276794c15 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanner.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanner.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Threading.Tasks; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; @@ -55,6 +56,11 @@ public SequentialPlanner( /// The plan. public async Task CreatePlanAsync(string goal) { + if (string.IsNullOrEmpty(goal)) + { + throw new PlanningException(PlanningException.ErrorCodes.InvalidGoal, "The goal specified is empty"); + } + string relevantFunctionsManual = await this._context.GetFunctionsManualAsync(goal, this.Config).ConfigureAwait(false); this._context.Variables.Set("available_functions", relevantFunctionsManual); @@ -64,9 +70,15 @@ public async Task CreatePlanAsync(string goal) string planResultString = planResult.Result.Trim(); - var plan = planResultString.ToPlanFromXml(goal, this._context); - - return plan; + try + { + var plan = planResultString.ToPlanFromXml(goal, this._context); + return plan; + } + catch (Exception e) + { + throw new PlanningException(PlanningException.ErrorCodes.InvalidPlan, "Plan parsing error, invalid XML", e); + } } private SequentialPlannerConfig Config { get; }