From 8924709952fa123f1d53abf937063c5158f17224 Mon Sep 17 00:00:00 2001 From: Teresa Hoang <125500434+teresaqhoang@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:02:10 -0700 Subject: [PATCH] Refactor Stepwise Planner handler, added logic to allow use of stepwise plan result as bot response (#514) ### Motivation and Context This pull request refactors the use of Stepwise Planner in CC. The changes include a fix to embed the result of the Stepwise Planner into the meta prompt if a valid result is returned and adds a new property to PlannerOptions to indicate whether to use the planner result as the bot response. Additionally, the ChatSkill class has been updated to handle the new changes, including adding the ability to pass a response string to the HandleBotResponseAsync method and changing the StepwiseThoughtProcess property to a PlanExecutionMetadata object. The PromptDialog component has also been updated to display the Stepwise Planner supplement and raw view when appropriate, and the BotResponsePrompt interface has been updated to include a rawView property (to show the Stepwise Thought Process if the webapi is configured to return the stepwise response directly). ### Description Logic: 1. If Stepwise Planner doesn't return a valid result (i.e., "Result not found"), continue with regular response generation. Planner result will not be included in meta prompt. ![image](https://github.com/microsoft/chat-copilot/assets/125500434/d25168b4-3bb2-4148-ad3a-1ded6d93fe75) 2. If Stepwise Planner returns a valid result, that result will be supplemented with complementary text to guide the model in using this response in the meta prompt. ![image](https://github.com/microsoft/chat-copilot/assets/125500434/61622a4c-61f4-4b10-8af0-36d9aed014b2) 3. If Stepwise Planner returns a valid result and the webapi is configured to use this result directly, it will be immediately returned to the client. ![image](https://github.com/microsoft/chat-copilot/assets/125500434/8f4cdf8e-67e7-4fde-9bcb-e2fdc42fbc3b) Change Details: - Update ExternalInformation property in BotResponsePrompt to use SemanticDependency of PlanExecutionMetadata instead of ISemanticDependency. - Rename StepwiseThoughtProcess class to PlanExecutionMetadata. - Add RawResult property to PlanExecutionMetadata to store the raw result of the planner. - Add UseStepwiseResultAsBotResponse property to PlannerOptions to indicate whether to use the planner result as the bot response. - Add StepwisePlannerSupplement property to PromptsOptions to help guide model in using a response from StepwisePlanner. - Update ChatSkill to use SemanticDependency of PlanExecutionMetadata instead of StepwiseThoughtProcess. - Add logic to handle using plan result as bot response if UseStepwiseResultAsBotResponse is true. - Update HandleBotResponseAsync method to accept rawResult parameter. Stepwise Thought Proceses available in Prompt details anytime the Stepwise planner is used, even if no result is found. ![image](https://github.com/microsoft/chat-copilot/assets/125500434/6b14dc70-f79e-47be-a0b3-0833c9b14117) ### Contribution Checklist - [x] The code builds clean without any errors or warnings - [x] The PR follows the [Contribution Guidelines](https://github.com/microsoft/chat-copilot/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/chat-copilot/blob/main/CONTRIBUTING.md#development-scripts) raises no violations ~- [ ] All unit tests pass, and I have added new tests where possible~ - [x] I didn't break anyone :smile: --- webapi/Models/Response/BotResponsePrompt.cs | 4 +- ...ghtProcess.cs => PlanExecutionMetadata.cs} | 13 ++- ...cDependencies.cs => SemanticDependency.cs} | 0 webapi/Options/PlannerOptions.cs | 6 ++ webapi/Options/PromptsOptions.cs | 5 + webapi/Skills/ChatSkills/ChatSkill.cs | 48 ++++++--- .../ChatSkills/ExternalInformationSkill.cs | 99 +++++++++++++++++-- webapi/appsettings.json | 4 + .../chat/prompt-dialog/PromptDialog.tsx | 39 +++++--- .../StepwiseThoughtProcessView.tsx | 4 +- webapp/src/libs/models/BotResponsePrompt.ts | 7 +- ...ghtProcess.ts => PlanExecutionMetadata.ts} | 4 +- 12 files changed, 189 insertions(+), 44 deletions(-) rename webapi/Models/Response/{StepwiseThoughtProcess.cs => PlanExecutionMetadata.cs} (73%) rename webapi/Models/Response/{SemanticDependencies.cs => SemanticDependency.cs} (100%) rename webapp/src/libs/models/{StepwiseThoughtProcess.ts => PlanExecutionMetadata.ts} (73%) diff --git a/webapi/Models/Response/BotResponsePrompt.cs b/webapi/Models/Response/BotResponsePrompt.cs index 4e6df23ce..ccd0d8bb4 100644 --- a/webapi/Models/Response/BotResponsePrompt.cs +++ b/webapi/Models/Response/BotResponsePrompt.cs @@ -44,7 +44,7 @@ public class BotResponsePrompt /// Relevant additional knowledge extracted using a planner. /// [JsonPropertyName("externalInformation")] - public ISemanticDependency ExternalInformation { get; set; } + public SemanticDependency ExternalInformation { get; set; } /// /// The collection of context messages associated with this chat completions request. @@ -58,7 +58,7 @@ public BotResponsePrompt( string audience, string userIntent, string chatMemories, - ISemanticDependency externalInformation, + SemanticDependency externalInformation, string chatHistory, ChatCompletionContextMessages metaPromptTemplate ) diff --git a/webapi/Models/Response/StepwiseThoughtProcess.cs b/webapi/Models/Response/PlanExecutionMetadata.cs similarity index 73% rename from webapi/Models/Response/StepwiseThoughtProcess.cs rename to webapi/Models/Response/PlanExecutionMetadata.cs index 360d53e9d..449eab83e 100644 --- a/webapi/Models/Response/StepwiseThoughtProcess.cs +++ b/webapi/Models/Response/PlanExecutionMetadata.cs @@ -5,9 +5,9 @@ namespace CopilotChat.WebApi.Models.Response; /// -/// Information about a pass through stepwise planner. +/// Metadata about plan execution. /// -public class StepwiseThoughtProcess +public class PlanExecutionMetadata { /// /// Steps taken execution stat. @@ -34,10 +34,17 @@ public class StepwiseThoughtProcess [JsonPropertyName("plannerType")] public PlanType PlannerType { get; set; } = PlanType.Stepwise; - public StepwiseThoughtProcess(string stepsTaken, string timeTaken, string skillsUsed) + /// + /// Raw result of the planner. + /// + [JsonIgnore] + public string RawResult { get; set; } = string.Empty; + + public PlanExecutionMetadata(string stepsTaken, string timeTaken, string skillsUsed, string rawResult) { this.StepsTaken = stepsTaken; this.TimeTaken = timeTaken; this.SkillsUsed = skillsUsed; + this.RawResult = rawResult; } } diff --git a/webapi/Models/Response/SemanticDependencies.cs b/webapi/Models/Response/SemanticDependency.cs similarity index 100% rename from webapi/Models/Response/SemanticDependencies.cs rename to webapi/Models/Response/SemanticDependency.cs diff --git a/webapi/Options/PlannerOptions.cs b/webapi/Options/PlannerOptions.cs index 31a6a55eb..86c79e249 100644 --- a/webapi/Options/PlannerOptions.cs +++ b/webapi/Options/PlannerOptions.cs @@ -66,6 +66,12 @@ public class ErrorOptions /// public ErrorOptions ErrorHandling { get; set; } = new ErrorOptions(); + /// + /// Optional flag to indicate whether to use the planner result as the bot response. + /// + [RequiredOnPropertyValue(nameof(Type), PlanType.Stepwise)] + public bool UseStepwiseResultAsBotResponse { get; set; } = false; + /// /// The configuration for the stepwise planner. /// diff --git a/webapi/Options/PromptsOptions.cs b/webapi/Options/PromptsOptions.cs index 67415a526..98c06cdcc 100644 --- a/webapi/Options/PromptsOptions.cs +++ b/webapi/Options/PromptsOptions.cs @@ -71,6 +71,11 @@ public class PromptsOptions /// [Required, NotEmptyOrWhitespace] public string PlanResultsDescription { get; set; } = string.Empty; + /// + /// Supplement to help guide model in using a response from StepwisePlanner. + /// + [Required, NotEmptyOrWhitespace] public string StepwisePlannerSupplement { get; set; } = string.Empty; + internal string[] SystemAudiencePromptComponents => new string[] { this.SystemAudience, diff --git a/webapi/Skills/ChatSkills/ChatSkill.cs b/webapi/Skills/ChatSkills/ChatSkill.cs index f02e59b07..914751d54 100644 --- a/webapi/Skills/ChatSkills/ChatSkill.cs +++ b/webapi/Skills/ChatSkills/ChatSkill.cs @@ -20,6 +20,7 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.ChatCompletion; using Microsoft.SemanticKernel.AI.TextCompletion; @@ -476,7 +477,7 @@ await this.SaveNewResponseAsync( chatContext.Variables.Set(TokenUtils.GetFunctionKey(this._logger, "SystemMetaPrompt")!, TokenUtils.GetContextMessagesTokenCount(promptTemplate).ToString(CultureInfo.CurrentCulture)); // TODO: [Issue #150, sk#2106] Accommodate different planner contexts once core team finishes work to return prompt and token usage. - var plannerDetails = new SemanticDependency(planResult, null, deserializedPlan.Type.ToString()); + var plannerDetails = new SemanticDependency(planResult, null, deserializedPlan.Type.ToString()); // Get bot response and stream to client var promptView = new BotResponsePrompt(systemInstructions, "", deserializedPlan.UserIntent, "", plannerDetails, chatHistoryString, promptTemplate); @@ -564,8 +565,8 @@ private async Task GetChatResponseAsync(string chatId, string userI () => this.AcquireExternalInformationAsync(chatContext, userIntent, externalInformationTokenLimit, cancellationToken: cancellationToken), nameof(AcquireExternalInformationAsync)); // Extract additional details about stepwise planner execution in chat context - var plannerDetails = new SemanticDependency( - planResult, + var plannerDetails = new SemanticDependency( + this._externalInformationSkill.StepwiseThoughtProcess?.RawResult ?? planResult, this._externalInformationSkill.StepwiseThoughtProcess ); @@ -585,6 +586,13 @@ private async Task GetChatResponseAsync(string chatId, string userI ); } + // If plan result is to be used as bot response, save the Stepwise result as a new response to the chat history and return. + if (this._externalInformationSkill.UseStepwiseResultAsBotResponse(planResult)) + { + var promptDetails = new BotResponsePrompt("", "", userIntent, "", plannerDetails, "", new ChatHistory()); + return await this.HandleBotResponseAsync(chatId, userId, chatContext, promptDetails, cancellationToken, null, this._externalInformationSkill.StepwiseThoughtProcess!.RawResult); + } + // Query relevant semantic and document memories await this.UpdateBotResponseStatusOnClientAsync(chatId, "Extracting semantic and document memories", cancellationToken); var chatMemoriesTokenLimit = (int)(remainingTokenBudget * this._promptOptions.MemoriesResponseContextWeight); @@ -614,9 +622,7 @@ private async Task GetChatResponseAsync(string chatId, string userI // Stream the response to the client var promptView = new BotResponsePrompt(systemInstructions, audience, userIntent, memoryText, plannerDetails, chatHistory, promptTemplate); - ChatMessage chatMessage = await this.HandleBotResponseAsync(chatId, userId, chatContext, promptView, cancellationToken, citationMap.Values.AsEnumerable()); - - return chatMessage; + return await this.HandleBotResponseAsync(chatId, userId, chatContext, promptView, cancellationToken, citationMap.Values.AsEnumerable()); } /// @@ -650,16 +656,32 @@ private async Task HandleBotResponseAsync( SKContext chatContext, BotResponsePrompt promptView, CancellationToken cancellationToken, - IEnumerable? citations = null) + IEnumerable? citations = null, + string? responseContent = null) { - // Get bot response and stream to client - await this.UpdateBotResponseStatusOnClientAsync(chatId, "Generating bot response", cancellationToken); - ChatMessage chatMessage = await AsyncUtils.SafeInvokeAsync( - () => this.StreamResponseToClientAsync(chatId, userId, promptView, cancellationToken, citations), nameof(StreamResponseToClientAsync)); + ChatMessage chatMessage; + if (responseContent.IsNullOrEmpty()) + { + // Get bot response and stream to client + await this.UpdateBotResponseStatusOnClientAsync(chatId, "Generating bot response", cancellationToken); + chatMessage = await AsyncUtils.SafeInvokeAsync( + () => this.StreamResponseToClientAsync(chatId, userId, promptView, cancellationToken, citations), nameof(StreamResponseToClientAsync)); + } + else + { + chatMessage = await this.CreateBotMessageOnClient( + chatId, + userId, + JsonSerializer.Serialize(promptView), + responseContent!, + cancellationToken, + citations + ); + } // Save the message into chat history await this.UpdateBotResponseStatusOnClientAsync(chatId, "Saving message to chat history", cancellationToken); - await this._chatMessageRepository.UpsertAsync(chatMessage); + await this._chatMessageRepository.UpsertAsync(chatMessage!); // Extract semantic chat memory await this.UpdateBotResponseStatusOnClientAsync(chatId, "Generating semantic chat memory", cancellationToken); @@ -675,7 +697,7 @@ await AsyncUtils.SafeInvokeAsync( // Calculate total token usage for dependency functions and prompt template await this.UpdateBotResponseStatusOnClientAsync(chatId, "Calculating token usage", cancellationToken); - chatMessage.TokenUsage = this.GetTokenUsages(chatContext, chatMessage.Content); + chatMessage!.TokenUsage = this.GetTokenUsages(chatContext, chatMessage.Content); // Update the message on client and in chat history with final completion token usage await this.UpdateMessageOnClient(chatMessage, cancellationToken); diff --git a/webapi/Skills/ChatSkills/ExternalInformationSkill.cs b/webapi/Skills/ChatSkills/ExternalInformationSkill.cs index f6088529a..e834a9f40 100644 --- a/webapi/Skills/ChatSkills/ExternalInformationSkill.cs +++ b/webapi/Skills/ChatSkills/ExternalInformationSkill.cs @@ -17,9 +17,12 @@ using CopilotChat.WebApi.Skills.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Planning; +using Microsoft.SemanticKernel.Planning.Stepwise; using Microsoft.SemanticKernel.SkillDefinition; +using Microsoft.SemanticKernel.TemplateEngine.Prompt; namespace CopilotChat.WebApi.Skills.ChatSkills; @@ -47,7 +50,13 @@ public class ExternalInformationSkill /// Options for the planner. /// private readonly PlannerOptions? _plannerOptions; - public PlannerOptions? PlannerOptions => this._plannerOptions; + public PlannerOptions? PlannerOptions + { + get + { + return this._plannerOptions; + } + } /// /// Proposed plan to return for approval. @@ -57,7 +66,7 @@ public class ExternalInformationSkill /// /// Stepwise thought process to return for view. /// - public StepwiseThoughtProcess? StepwiseThoughtProcess { get; private set; } + public PlanExecutionMetadata? StepwiseThoughtProcess { get; private set; } /// /// Header to indicate plan results. @@ -101,15 +110,11 @@ public async Task InvokePlannerAsync( var contextString = this.GetChatContextString(context); var goal = $"Given the following context, accomplish the user intent.\nContext:\n{contextString}\n{userIntent}"; + + // Run stepwise planner if PlannerOptions.Type == Stepwise if (this._planner.PlannerOptions?.Type == PlanType.Stepwise) { - var plannerContext = context.Clone(); - plannerContext = await this._planner.RunStepwisePlannerAsync(goal, context, cancellationToken); - this.StepwiseThoughtProcess = new StepwiseThoughtProcess( - plannerContext.Variables["stepsTaken"], - plannerContext.Variables["timeTaken"], - plannerContext.Variables["skillCount"]); - return $"{plannerContext.Variables.Input.Trim()}\n"; + return await this.RunStepwisePlannerAsync(goal, context, cancellationToken); } // Create a plan and set it in context for approval. @@ -193,8 +198,84 @@ public async Task ExecutePlanAsync( return $"{functionsUsed}\n{ResultHeader}{planResult.Trim()}"; } + /// + /// Determines whether to use the stepwise planner result as the bot response, thereby bypassing meta prompt generation and completion. + /// + /// The result obtained from the stepwise planner. + /// + /// True if the stepwise planner result should be used as the bot response, + /// false otherwise. + /// + /// + /// This method checks the following conditions: + /// 1. The plan result is not null, empty, or whitespace. + /// 2. The planner options are specified, and the plan type is set to Stepwise. + /// 3. The UseStepwiseResultAsBotResponse option is enabled. + /// 4. The StepwiseThoughtProcess is not null. + /// + public bool UseStepwiseResultAsBotResponse(string planResult) + { + return !string.IsNullOrWhiteSpace(planResult) + && this._plannerOptions?.Type == PlanType.Stepwise + && this._plannerOptions.UseStepwiseResultAsBotResponse + && this.StepwiseThoughtProcess != null; + } + #region Private + /// + /// Executes the stepwise planner with a given goal and context, and returns the result along with descriptive text. + /// Also sets any metadata associated with stepwise planner execution. + /// + /// The goal to be achieved by the stepwise planner. + /// The SKContext containing the necessary information for the planner. + /// A CancellationToken to observe while waiting for the task to complete. + /// + /// A formatted string containing the result of the stepwise planner and a supplementary message to guide the model in using the result. + /// + private async Task RunStepwisePlannerAsync(string goal, SKContext context, CancellationToken cancellationToken) + { + var plannerContext = context.Clone(); + plannerContext = await this._planner.RunStepwisePlannerAsync(goal, context, cancellationToken); + + // Populate the execution metadata. + var plannerResult = plannerContext.Variables.Input.Trim(); + this.StepwiseThoughtProcess = new PlanExecutionMetadata( + plannerContext.Variables["stepsTaken"], + plannerContext.Variables["timeTaken"], + plannerContext.Variables["skillCount"], + plannerResult); + + // Return empty string if result was not found so it's omitted from the meta prompt. + if (plannerResult.Contains("Result not found, review 'stepsTaken' to see what happened.", StringComparison.OrdinalIgnoreCase)) + { + return string.Empty; + } + + // Parse the steps taken to determine which functions were used. + if (plannerContext.Variables.TryGetValue("stepsTaken", out var stepsTaken)) + { + var steps = JsonSerializer.Deserialize>(stepsTaken); + var functionsUsed = new HashSet(); + steps?.ForEach(step => + { + if (!step.Action.IsNullOrEmpty()) { functionsUsed.Add(step.Action); } + }); + + var planFunctions = string.Join(", ", functionsUsed); + plannerContext.Variables.Set("planFunctions", functionsUsed.Count > 0 ? planFunctions : "N/A"); + } + + // Render the supplement to guide the model in using the result. + var promptRenderer = new PromptTemplateEngine(); + var resultSupplement = await promptRenderer.RenderAsync( + this._promptOptions.StepwisePlannerSupplement, + plannerContext, + cancellationToken); + + return $"{resultSupplement}\n\nResult:\n\"{plannerResult}\""; + } + /// /// Merge any variables from context into plan parameters. /// diff --git a/webapi/appsettings.json b/webapi/appsettings.json index f24ddc2f0..578a51405 100644 --- a/webapi/appsettings.json +++ b/webapi/appsettings.json @@ -48,11 +48,14 @@ // - Set Planner:Type to "Sequential" to enable the multi-step SequentialPlanner // Note: SequentialPlanner works best with `gpt-4`. See the "Enabling Sequential Planner" section in webapi/README.md for configuration instructions. // - Set Planner:Type to "Stepwise" to enable MRKL style planning + // - Set Planner:UseStepwiseResultAsBotResponse to "true" to use the result of the planner as the bot response. If false, planner result will be imbedded in meta prompt. + // - This is helpful because the Stepwise Planner returns a sensical chat response as its result. // - Set Planner:RelevancyThreshold to a decimal between 0 and 1.0. // - Set Planner:Model to a chat completion model (e.g., gpt-35-turbo, gpt-4). // "Planner": { "Type": "Sequential", + "UseStepwiseResultAsBotResponse": true, // The minimum relevancy score for a function to be considered. // Set RelevancyThreshold to a value between 0 and 1 if using the SequentialPlanner or Stepwise planner with gpt-3.5-turbo. // Ignored when Planner:Type is "Action" @@ -166,6 +169,7 @@ "SystemResponse": "Either return [silence] or provide a response to the last message. ONLY PROVIDE A RESPONSE IF the last message WAS ADDRESSED TO THE 'BOT' OR 'COPILOT'. If it appears the last message was not for you, send [silence] as the bot response.", "InitialBotMessage": "Hello, thank you for democratizing AI's productivity benefits with open source! How can I help you today?", "ProposedPlanBotMessage": "As an AI language model, my knowledge is based solely on the data that was used to train me, but I can use the following functions to get fresh information: {{$planFunctions}}. Do you agree to proceed?", + "StepwisePlannerSupplement": "This result was obtained using the Stepwise Planner, which used a series of thoughts and actions to fulfill the user intent. The planner attempted to use the following functions to gather necessary information: {{$planFunctions}}.", "PlanResultsDescription": "This is the result of invoking the functions listed after \"FUNCTIONS USED:\" to retrieve additional information outside of the data you were trained on. This information was retrieved on {{TimeSkill.Now}}. You can use this data to help answer the user's query.", "KnowledgeCutoffDate": "Saturday, January 1, 2022", "SystemAudience": "Below is a chat history between an intelligent AI bot named Copilot with one or more participants.", diff --git a/webapp/src/components/chat/prompt-dialog/PromptDialog.tsx b/webapp/src/components/chat/prompt-dialog/PromptDialog.tsx index 6fbb9174a..b94e62cb1 100644 --- a/webapp/src/components/chat/prompt-dialog/PromptDialog.tsx +++ b/webapp/src/components/chat/prompt-dialog/PromptDialog.tsx @@ -19,6 +19,7 @@ import { TabValue, Tooltip, makeStyles, + mergeClasses, shorthands, } from '@fluentui/react-components'; import { Info16Regular } from '@fluentui/react-icons'; @@ -26,7 +27,7 @@ import React from 'react'; import { BotResponsePrompt, DependencyDetails, PromptSectionsNameMap } from '../../../libs/models/BotResponsePrompt'; import { ChatMessageType, IChatMessage } from '../../../libs/models/ChatMessage'; import { PlanType } from '../../../libs/models/Plan'; -import { StepwiseThoughtProcess } from '../../../libs/models/StepwiseThoughtProcess'; +import { PlanExecutionMetadata } from '../../../libs/models/PlanExecutionMetadata'; import { useDialogClasses } from '../../../styles'; import { TokenUsageGraph } from '../../token-usage/TokenUsageGraph'; import { formatParagraphTextContent } from '../../utils/TextUtils'; @@ -39,6 +40,10 @@ const useClasses = makeStyles({ minWidth: 'auto', marginLeft: 'auto', // align to right }, + text: { + width: '100%', + overflowWrap: 'break-word', + }, }); interface IPromptDialogProps { @@ -71,8 +76,18 @@ export const PromptDialog: React.FC = ({ message }) => { const information = value as DependencyDetails; if (information.context) { // TODO: [Issue #150, sk#2106] Accommodate different planner contexts once core team finishes work to return prompt and token usage. - const details = information.context as StepwiseThoughtProcess; + const details = information.context as PlanExecutionMetadata; isStepwiseThoughtProcess = details.plannerType === PlanType.Stepwise; + + // Backend can be configured to return the raw response from Stepwise Planner. In this case, no meta prompt was generated or completed + // and we should show the Stepwise thought process as the raw content view. + if ((prompt as BotResponsePrompt).metaPromptTemplate.length <= 0) { + (prompt as BotResponsePrompt).rawView = ( +
+                                {JSON.stringify(JSON.parse(details.stepsTaken), null, 2)}
+                            
+ ); + } } if (!isStepwiseThoughtProcess) { @@ -134,15 +149,17 @@ export const PromptDialog: React.FC = ({ message }) => { > {selectedTab === 'formatted' && promptDetails} {selectedTab === 'rawContent' && - (prompt as BotResponsePrompt).metaPromptTemplate.map((contextMessage, index) => { - return ( -
-

{`Role: ${contextMessage.Role.Label}`}

- {formatParagraphTextContent(`Content: ${contextMessage.Content}`)} - -
- ); - })} + ((prompt as BotResponsePrompt).metaPromptTemplate.length > 0 + ? (prompt as BotResponsePrompt).metaPromptTemplate.map((contextMessage, index) => { + return ( +
+

{`Role: ${contextMessage.Role.Label}`}

+ {formatParagraphTextContent(`Content: ${contextMessage.Content}`)} + +
+ ); + }) + : (prompt as BotResponsePrompt).rawView)} diff --git a/webapp/src/components/chat/prompt-dialog/stepwise-planner/StepwiseThoughtProcessView.tsx b/webapp/src/components/chat/prompt-dialog/stepwise-planner/StepwiseThoughtProcessView.tsx index 7f8ad18a1..e6de489c1 100644 --- a/webapp/src/components/chat/prompt-dialog/stepwise-planner/StepwiseThoughtProcessView.tsx +++ b/webapp/src/components/chat/prompt-dialog/stepwise-planner/StepwiseThoughtProcessView.tsx @@ -11,8 +11,8 @@ import { import { useState } from 'react'; import { COPY } from '../../../../assets/strings'; import { DependencyDetails } from '../../../../libs/models/BotResponsePrompt'; +import { PlanExecutionMetadata } from '../../../../libs/models/PlanExecutionMetadata'; import { StepwiseStep } from '../../../../libs/models/StepwiseStep'; -import { StepwiseThoughtProcess } from '../../../../libs/models/StepwiseThoughtProcess'; import { formatParagraphTextContent } from '../../../utils/TextUtils'; import { StepwiseStepView, useStepClasses } from './StepwiseStepView'; @@ -33,7 +33,7 @@ interface IStepwiseThoughtProcessViewProps { export const StepwiseThoughtProcessView: React.FC = ({ thoughtProcess }) => { const classes = useClasses(); const stepClasses = useStepClasses(); - const stepwiseDetails = thoughtProcess.context as StepwiseThoughtProcess; + const stepwiseDetails = thoughtProcess.context as PlanExecutionMetadata; const steps = JSON.parse(stepwiseDetails.stepsTaken) as StepwiseStep[]; const testResultNotFound = thoughtProcess.result.matchAll(COPY.STEPWISE_RESULT_NOT_FOUND_REGEX); diff --git a/webapp/src/libs/models/BotResponsePrompt.ts b/webapp/src/libs/models/BotResponsePrompt.ts index 8b4ebef95..485b97c37 100644 --- a/webapp/src/libs/models/BotResponsePrompt.ts +++ b/webapp/src/libs/models/BotResponsePrompt.ts @@ -1,4 +1,4 @@ -import { StepwiseThoughtProcess } from './StepwiseThoughtProcess'; +import { PlanExecutionMetadata } from './PlanExecutionMetadata'; // The final prompt sent to generate bot response. export interface BotResponsePrompt { @@ -23,6 +23,9 @@ export interface BotResponsePrompt { // The collection of context messages associated with this chat completions request. // Also serves as the rendered prompt template. metaPromptTemplate: ContextMessage[]; + + // Raw content view to show if backend can be configured to return the raw response from Stepwise Planner. In this case, no meta prompt was generated or completed. + rawView: any; } export const PromptSectionsNameMap: Record = { @@ -37,7 +40,7 @@ export const PromptSectionsNameMap: Record = { // Information about semantic dependencies of the prompt. export interface DependencyDetails { // Context of the dependency. This can be either the prompt template or planner details. - context: string | StepwiseThoughtProcess; + context: string | PlanExecutionMetadata; // Result of the dependency. This is the output that's injected into the prompt. result: string; diff --git a/webapp/src/libs/models/StepwiseThoughtProcess.ts b/webapp/src/libs/models/PlanExecutionMetadata.ts similarity index 73% rename from webapp/src/libs/models/StepwiseThoughtProcess.ts rename to webapp/src/libs/models/PlanExecutionMetadata.ts index 8290cb794..4312f8436 100644 --- a/webapp/src/libs/models/StepwiseThoughtProcess.ts +++ b/webapp/src/libs/models/PlanExecutionMetadata.ts @@ -1,7 +1,7 @@ import { PlanType } from './Plan'; -// Information about a pass through stepwise planner. -export interface StepwiseThoughtProcess { +// Metadata about plan execution. +export interface PlanExecutionMetadata { // Steps taken execution stat. stepsTaken: string;