Skip to content

Commit

Permalink
Fine-tune prompts. ClosesL #254 (#321)
Browse files Browse the repository at this point in the history
## 🎯 Aim

The aim is to upgrade prompts, especially setup and new to allow a more
natural way to talk about those topics. In `/new` command the aim is to
provide functionality to create a new project together with Copilot by
providing the required information to create the yo scaffolding command

## 📷 Result

![chat-in-action-new](https://github.com/user-attachments/assets/9425077e-243c-4ce8-87de-4e857317ce50)

![chat-in-action-new2](https://github.com/user-attachments/assets/e9a89b1e-d2c4-45db-8495-f58ac7cd2602)

![chat-in-action-setup](https://github.com/user-attachments/assets/bd0e9ed0-d79f-4006-9f62-60d164996caa)

## ✅ What was done

- [X] Updated prompt grounding
- [X] Updated readme
- [X] Added new way to create new project together with Copilot

## 🔗 Related issue

Closes #254
  • Loading branch information
Adam-it committed Oct 19, 2024
1 parent e2c85cc commit 7373c4b
Show file tree
Hide file tree
Showing 17 changed files with 236 additions and 77 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,11 @@ It is also possible to set the default behavior when you're about to scaffold a

Now you may use SPFx Toolkit as a chat participant in GitHub Copilot chat extension. Simply, mention @spfx in the chat to ask dedicated questions regarding SharePoint Framework development.

![SPFx Toolkit chat in action](./assets/images/chat-in-action.gif)
![SPFx Toolkit chat in action](./assets/images/chat-in-action-new.gif)

![SPFx Toolkit chat in action](./assets/images/chat-in-action-new2.gif)

![SPFx Toolkit chat in action](./assets/images/chat-in-action-setup.gif)

@spfx is your dedicated AI Copilot that will help you with anything that is needed to develop your SharePoint Framework project. It has predefined commands that are tailored toward a specific activity for which you require guidance.

Expand Down
Binary file modified assets/images/chat-commands.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/chat-in-action-new.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/chat-in-action-new2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/chat-in-action-setup.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/images/chat-in-action.gif
Binary file not shown.
Binary file modified assets/images/chat-intro.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,18 @@
"commands": [
{
"name": "setup",
"isSticky": true,
"description": "Prepare your local workspace for SharePoint Framework development"
},
{
"name": "new",
"isSticky": true,
"description": "Create a new SharePoint Framework project."
},
{
"name": "code",
"description": "Let's write some SPFx code"
"isSticky": true,
"description": "Let's write some SPFx code (I am still learning this, please consider this as beta)"
}
]
}
Expand Down
141 changes: 85 additions & 56 deletions src/chat/PromptHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
import * as vscode from 'vscode';
import { Commands, promptCodeContext, promptContext, promptNewContext, promptSetupContext } from '../constants';
import { AdaptiveCardTypes, Commands, ComponentTypes, ExtensionTypes, msSampleGalleryLink, promptCodeContext, promptContext, promptGeneralContext, promptNewContext, promptSetupContext } from '../constants';
import { ProjectInformation } from '../services/dataType/ProjectInformation';

const MODEL_SELECTOR: vscode.LanguageModelChatSelector = { vendor: 'copilot', family: 'gpt-4' };

export class PromptHandlers {
public static history: string[] = [];
public static previousCommand: string = '';

public static async handle(request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<any> {
stream.progress(PromptHandlers.getRandomProgressMessage());
const chatCommand = (request.command && ['setup', 'new', 'code'].indexOf(request.command) > -1) ? request.command : '';
const chatCommand = (request.command && ['setup', 'new', 'code'].indexOf(request.command.toLowerCase()) > -1) ? request.command.toLowerCase() : '';

const messages = [vscode.LanguageModelChatMessage.Assistant(promptContext)];
const messages: vscode.LanguageModelChatMessage[] = [];
messages.push(vscode.LanguageModelChatMessage.Assistant(promptContext));
messages.push(vscode.LanguageModelChatMessage.Assistant(PromptHandlers.getChatCommandPrompt(chatCommand)));

if (PromptHandlers.previousCommand !== chatCommand) {
PromptHandlers.history = [];
PromptHandlers.previousCommand = chatCommand;
} else {
PromptHandlers.history.forEach(message => messages.push(vscode.LanguageModelChatMessage.Assistant(message)));
}

messages.push(vscode.LanguageModelChatMessage.User(request.prompt));
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
PromptHandlers.history.push(request.prompt);
const [model] = await vscode.lm.selectChatModels({ vendor: 'copilot', family: 'gpt-4o' });
try {
const chatResponse = await model.sendRequest(messages, {}, token);
let query = '';
for await (const fragment of chatResponse.text) {
query += fragment;
stream.markdown(fragment);
}
PromptHandlers.getChatCommandButtons(chatCommand).forEach(button => stream.button(button));
PromptHandlers.history.push(query);
PromptHandlers.getChatCommandButtons(chatCommand, query).forEach(button => stream.button(button));
return { metadata: { command: chatCommand } };
} catch (err) {
if (err instanceof vscode.LanguageModelError) {
Expand All @@ -34,63 +48,23 @@ export class PromptHandlers {
}
}

private static getChatCommandButtons(chatCommand: string) {
switch (chatCommand) {
case 'setup':
return [{
command: Commands.checkDependencies,
title: vscode.l10n.t('Check if my local workspace is ready'),
},
{
command: Commands.installDependencies,
title: vscode.l10n.t('Install required dependencies'),
}];
case 'new':
return [{
command: Commands.createProject,
title: vscode.l10n.t('Create a new project'),
},
{
command: Commands.samplesGallery,
title: vscode.l10n.t('View samples'),
}];
case 'code':
if(ProjectInformation.isSPFxProject) {
return [{
command: Commands.upgradeProject,
title: vscode.l10n.t('Get upgrade guidance to latest SPFx version'),
},
{
command: Commands.validateProject,
title: vscode.l10n.t('Validate your project'),
},
{
command: Commands.renameProject,
title: vscode.l10n.t('Rename your project'),
},
{
command: Commands.pipeline,
title: vscode.l10n.t('Create a CI/CD workflow'),
}];
}

return [];
default:
return [];
}
}

private static getChatCommandPrompt(chatCommand: string): string {
let context: string = '';
switch (chatCommand) {
case 'setup':
return promptSetupContext;
context += promptSetupContext;
case 'new':
return promptNewContext;
context += promptNewContext;
context += `\n Here is some more information regarding each component type ${ComponentTypes}`;
context += `\n Here is some more information regarding each extension type ${ExtensionTypes}`;
context += `\n Here is some more information regarding each ACE type ${AdaptiveCardTypes}`;
case 'code':
return promptCodeContext;
context += promptCodeContext;
default:
return '';
context += promptGeneralContext;
}

return context;
}

private static getRandomProgressMessage(): string {
Expand All @@ -114,4 +88,59 @@ export class PromptHandlers {
];
return messages[Math.floor(Math.random() * messages.length)];
}

private static getChatCommandButtons(chatCommand: string, chatResponse: string) {
switch (chatCommand) {
case 'new':
const buttons = [];
const regex = /```([^\n]*)\n(?=[\s\S]*?yo @microsoft\/sharepoint.+)([\s\S]*?)\n?```/g;
const match = regex.exec(chatResponse);
if (match && match[2]) {
buttons.push(
{
command: Commands.createProjectCopilot,
title: vscode.l10n.t('Create project'),
arguments: [match[2]],
});
}
if (chatResponse.toLowerCase().includes(msSampleGalleryLink)) {
buttons.push({
command: Commands.samplesGallery,
title: vscode.l10n.t('Open sample gallery'),
});
}
return buttons;
case 'setup':
if (chatResponse.toLowerCase().includes('check dependencies')) {
return[{
command: Commands.checkDependencies,
title: vscode.l10n.t('Check if my local workspace is ready'),
}];
}
return [];
case 'code':
if (ProjectInformation.isSPFxProject) {
return [{
command: Commands.upgradeProject,
title: vscode.l10n.t('Get upgrade guidance to latest SPFx version'),
},
{
command: Commands.validateProject,
title: vscode.l10n.t('Validate your project'),
},
{
command: Commands.renameProject,
title: vscode.l10n.t('Rename your project'),
},
{
command: Commands.pipeline,
title: vscode.l10n.t('Create a CI/CD workflow'),
}];
}

return [];
default:
return [];
}
}
}
6 changes: 3 additions & 3 deletions src/constants/AdaptiveCardTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ export const AdaptiveCardTypes = [
{
name: 'Generic Card Template',
value: 'Generic',
description: "A flexible template that can be customized for various use cases, displaying general information and content in an adaptive card format."
description: 'A flexible template that can be customized for various use cases, displaying general information and content in an adaptive card format.'
},
{
name: 'Search Query Modifier',
value: 'Search',
description: "A template designed to modify and enhance search queries, improving the relevance and accuracy of search results displayed in adaptive cards."
description: 'A template designed to modify and enhance search queries, improving the relevance and accuracy of search results displayed in adaptive cards.'
},
{
name: 'Data Visualization Card Template',
value: 'DataVisualization',
description: "A template focused on presenting data visualizations, such as charts and graphs, within adaptive cards to provide clear and insightful data representations."
description: 'A template focused on presenting data visualizations, such as charts and graphs, within adaptive cards to provide clear and insightful data representations.'

}
];
1 change: 1 addition & 0 deletions src/constants/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const Commands = {
// Scaffolding
createProject: `${EXTENSION_NAME}.createProject`,
addToProject: `${EXTENSION_NAME}.addToProject`,
createProjectCopilot: `${EXTENSION_NAME}.createProjectCopilot`,

// Output channel
showOutputChannel: `${EXTENSION_NAME}.showOutputChannel`,
Expand Down
6 changes: 3 additions & 3 deletions src/constants/FrameworkTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ export const FrameworkTypes = [
name: 'No framework',
value: FrameworkType.none,
description:
"Use any library or write pure JavaScript for maximum flexibility."
'Use any library or write pure JavaScript for maximum flexibility.'
},
{
name: 'React',
value: FrameworkType.react,
description:
"Popular library for building dynamic and responsive UI components."
'Popular library for building dynamic and responsive UI components.'
},
{
name: 'Minimal',
value: FrameworkType.minimal,
description:
"Lightweight setup with minimal dependencies for straightforward functionality."
'Lightweight setup with minimal dependencies for straightforward functionality.'
},
];
Loading

0 comments on commit 7373c4b

Please sign in to comment.