Skip to content

Commit

Permalink
Validate goal and handle plan parsing errors in SequentialPlanner (#707)
Browse files Browse the repository at this point in the history
Added a check for empty goal in CreatePlanAsync method and threw a
PlanningException with appropriate error code. Wrapped the plan parsing
logic in a try-catch block and re-threw any exceptions as
PlanningExceptions with a different error code. This improves the
robustness and usability of the SequentialPlanner class.
  • Loading branch information
lemillermicrosoft authored and dluc committed Apr 29, 2023
1 parent 0bad4c1 commit 883443f
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -144,6 +145,73 @@ public async Task ItCanCreatePlanAsync(string goal)
}
}

[Fact]
public async Task EmptyGoalThrowsAsync()
{
// Arrange
var kernel = new Mock<IKernel>();
// kernel.Setup(x => x.Log).Returns(new Mock<ILogger>().Object);

var planner = new Microsoft.SemanticKernel.Planning.SequentialPlanner(kernel.Object);

// Act
await Assert.ThrowsAsync<PlanningException>(async () => await planner.CreatePlanAsync(""));
}

[Fact]
public async Task InvalidXMLThrowsAsync()
{
// Arrange
var kernel = new Mock<IKernel>();
// kernel.Setup(x => x.Log).Returns(new Mock<ILogger>().Object);
var memory = new Mock<ISemanticTextMemory>();
var skills = new Mock<ISkillCollection>();

var functionsView = new FunctionsView();
skills.Setup(x => x.GetFunctionsView(It.IsAny<bool>(), It.IsAny<bool>())).Returns(functionsView);

var planString =
@"<plan>notvalid<</plan>";
var returnContext = new SKContext(
new ContextVariables(planString),
memory.Object,
skills.Object,
new Mock<ILogger>().Object
);

var context = new SKContext(
new ContextVariables(),
memory.Object,
skills.Object,
new Mock<ILogger>().Object
);

var mockFunctionFlowFunction = new Mock<ISKFunction>();
mockFunctionFlowFunction.Setup(x => x.InvokeAsync(
It.IsAny<SKContext>(),
null,
null,
null
)).Callback<SKContext, CompleteRequestSettings, ILogger, CancellationToken?>(
(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<string>(),
It.IsAny<string>(),
It.IsAny<SemanticFunctionConfig>()
)).Returns(mockFunctionFlowFunction.Object);

var planner = new Microsoft.SemanticKernel.Planning.SequentialPlanner(kernel.Object);

// Act
await Assert.ThrowsAsync<PlanningException>(async () => await planner.CreatePlanAsync("goal"));
}

// Method to create Mock<ISKFunction> objects
private static Mock<ISKFunction> CreateMockFunction(FunctionView functionView)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Diagnostics;
using Microsoft.SemanticKernel.Orchestration;
Expand Down Expand Up @@ -55,6 +56,11 @@ public SequentialPlanner(
/// <returns>The plan.</returns>
public async Task<Plan> 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);

Expand All @@ -64,9 +70,15 @@ public async Task<Plan> 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; }
Expand Down

0 comments on commit 883443f

Please sign in to comment.