Skip to content

Commit

Permalink
Chat extension prototype (#240)
Browse files Browse the repository at this point in the history
## 🎯 Aim

The aim is to add a preview feature with SPFx chat participant

## 📷 Result


![chat-intro](https://github.com/pnp/vscode-viva/assets/58668583/e4166cfc-b200-4563-88c9-a53f09add7f1)


## ✅ What was done

- [X] Added chat participant

## 🔗 Related issue

Related: #239
  • Loading branch information
Adam-it authored May 25, 2024
1 parent f10b971 commit 14a9e6b
Show file tree
Hide file tree
Showing 12 changed files with 38,738 additions and 16 deletions.
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

<p align="center">
<a href="#-capabilities">Capabilities</a> |
<a href="#-preview-features">Preview features</a> |
<a href="#%EF%B8%8F-architecture">Architecture</a> |
<a href="#-wiki">Wiki</a> |
<a href="#-contributing">Contributing</a> |
Expand All @@ -33,7 +34,7 @@
</p>

![Sample Gallery](./assets/images/start.png)

## ⭐ Capabilities

The extension provides the following capabilities:
Expand Down Expand Up @@ -263,6 +264,37 @@ By default, the SharePoint Framework Toolkit will use the Node.js version that i

You can use the settings to change which Node.js version manager you want to use. You may choose between `nvm` and `nvs`. If you wish to avoid using a Node.js version manager, you can set the value to `none`

## 🧪 Preview features

> [!WARNING]
> Features described in this section are part of a pre-release and are considered as an early beta feature. They may change or be removed in a future major or minor release. To get the pre-release version of the extension you need to opt-in, please refer to the guidance in the [wiki](https://github.com/pnp/vscode-viva/wiki/1.-Get-started#-pre-release).
### 1️⃣ SPFx Toolkit GitHub Chat Participant

![SPFx Toolkit chat](./assets/images/chat-intro.png)

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 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.

![SPFx Toolkit chat commands](./assets/images/chat-commands.png)

Currently, we support the following commands:
- `/setup` - that is dedicated to providing information on how to setup your local workspace for SharePoint Framework development
- `/new` - that may be used to get guidance on how to create a new solution or find and reuse an existing sample from the PnP SPFx sample gallery
- `/code` - that is fine-tuned to provide help in coding your SharePoint Framework project
- `/action` - that will provide you additional boosters like validating the correctness of your SPFx project, scaffolding a CI/CD workflow, or renaming your project, and many more.

> [!IMPORTANT]
> In order for this feature to work you need to meet the following requirements:
> - Use the [Visual Studio Code Insiders](https://code.visualstudio.com/insiders/) release
> - Use the pre-release version of the [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) extension
> - Use pre-release of [SPFx Toolkit](https://marketplace.visualstudio.com/items?itemName=m365pnp.viva-connections-toolkit)
[Check out our docs for more details](https://github.com/pnp/vscode-viva/wiki/8.-Preview-features)

## ⚙️ Architecture

SharePoint Framework Toolkit for Visual Studio Code is an abstraction layer on top of the [SPFx](https://aka.ms/spfx) Yeoman generator and [CLI for Microsoft 365](https://pnp.github.io/cli-microsoft365/).
Expand Down
Binary file added 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.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-intro.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 38 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@
"name": "viva-connections-toolkit",
"displayName": "SharePoint Framework Toolkit",
"description": "SharePoint Framework Toolkit aims to boost your productivity in developing and managing SharePoint Framework solutions helping at every stage of your development flow, from setting up your development workspace to deploying a solution straight to your tenant without the need to leave VS Code and now even create a CI/CD pipeline to introduce automate deployment of your app. This toolkit is provided by the community.",
"version": "3.2.0",
"version": "3.2.1",
"publisher": "m365pnp",
"preview": false,
"homepage": "https://github.com/pnp/vscode-viva",
"icon": "assets/logo-large.png",
"engines": {
"vscode": "^1.65.0"
},
"vscode": "^1.90.0"
},
"categories": [
"Snippets",
"Extension Packs",
"Other"
"Other",
"AI",
"Chat"
],
"repository": {
"type": "git",
Expand Down Expand Up @@ -45,6 +47,33 @@
"license": "MIT",
"main": "./dist/extension.js",
"contributes": {
"chatParticipants": [
{
"id": "spfx-toolkit.pnp",
"fullName": "SPFx Toolkit",
"name": "spfx",
"description": "How can I help you with SPFx development?",
"isSticky": true,
"commands": [
{
"name": "setup",
"description": "Prepare your local workspace for SharePoint Framework development"
},
{
"name": "new",
"description": "Create a new SharePoint Framework project."
},
{
"name": "code",
"description": "Let's write some SPFx code"
},
{
"name": "action",
"description": "Boost your SharePoint Framework project"
}
]
}
],
"walkthroughs": [
{
"id": "spfx-toolkit-intro",
Expand All @@ -64,8 +93,8 @@
"title": "Check and get required dependencies",
"description": "Validate if your local workspace is ready for SharePoint Framework development. \n[Check dependencies](command:spfx-toolkit.checkDependencies)",
"media": {
"image": "assets/images/validate-dependency.png",
"altText": "Validate dependencies"
"image": "assets/images/validate-dependency.png",
"altText": "Validate dependencies"
}
},
{
Expand Down Expand Up @@ -113,7 +142,7 @@
"title": "Continue your learning and get additional tooling",
"description": "SharePoint Framework Toolkit is your single point to get to the official Microsoft documentation and learning regarding SharePoint Framework and additional tooling that will help you create apps for Microsoft 365.",
"media": {
"image": "assets/images/help-and-feedback.png",
"image": "assets/images/help-and-feedback.png",
"altText": "help and feedback section"
}
},
Expand All @@ -122,8 +151,8 @@
"title": "Connect with the Microsoft 365 and Power Platform community",
"description": "Stay up to date and connected. \nVisit the [Microsoft 365 and Power Platform community](https://pnp.github.io/) to get more learning materials, guidance, tooling, and engage in community calls. \nJoin the [community Discord server](https://aka.ms/community/discord) to stay connected and help out others. \n Sharing is caring!",
"media": {
"image": "assets/images/parker-pnp.png",
"altText": "PnP Parker"
"image": "assets/images/parker-pnp.png",
"altText": "PnP Parker"
}
}
]
Expand Down
109 changes: 109 additions & 0 deletions src/chat/PromptHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import * as vscode from 'vscode';
import { Commands, promptActionContext, promptCodeContext, promptContext, promptNewContext, promptSetupContext } from '../constants';

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

export class PromptHandlers {

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', 'action'].indexOf(request.command) > -1) ? request.command : '';

const messages = [vscode.LanguageModelChatMessage.User(promptContext)];
messages.push(vscode.LanguageModelChatMessage.User(PromptHandlers.getChatCommandPrompt(chatCommand)));
messages.push(vscode.LanguageModelChatMessage.User(request.prompt));
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
try {
const chatResponse = await model.sendRequest(messages, {}, token);
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
PromptHandlers.getChatCommandButtons(chatCommand).forEach(button => stream.button(button));
return { metadata: { command: chatCommand } };
} catch (err) {
stream.markdown('...It seems that something is not working as expected. Please try again later.');
return { metadata: { command: '' } };
}
}

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':
return [];
case 'action':
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'),
}];
default:
return [];
}
}

private static getChatCommandPrompt(chatCommand: string): string {
switch (chatCommand) {
case 'setup':
return promptSetupContext;
case 'new':
return promptNewContext;
case 'code':
return promptCodeContext;
case 'action':
return promptActionContext;
default:
return '';
}
}

private static getRandomProgressMessage(): string {
const messages = [
'Checking...',
'Let me think about it...',
'Reading the docs...',
'Cracking the code...',
'Unleashing the algorithms...',
'Beaming up the data...',
'Feeding the hamsters...',
'Charging the flux capacitor...',
'Warming up the servers...',
'Consulting with the rubber duck...',
'Asking the magic 8-ball...',
'Counting backwards from infinity...',
'Commencing time travel...',
'Converting coffee to code...',
'Adjusting the reality matrix...',
'Waking up the AI...'
];
return messages[Math.floor(Math.random() * messages.length)];
}
}
3 changes: 2 additions & 1 deletion src/constants/General.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const EXTENSION_NAME = 'spfx-toolkit';
export const EXTENSION_NAME = 'spfx-toolkit';
export const CHAT_PARTICIPANT_NAME = 'spfx-toolkit.pnp';
43 changes: 43 additions & 0 deletions src/constants/Prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export const personality = 'You are a kind and helpful assistant named SPFx Toolkit.';

export const aim = 'You aim to provide support in coding and managing SharePoint Framework (SPFx) solutions.';

export const msLearnLink = 'learn.microsoft.com';
export const msSampleGalleryLink = 'https://adoption.microsoft.com/en-us/sample-solution-gallery/';
export const msLinks = `${msLearnLink}, ${msSampleGalleryLink}`;

export const spfxOverviewLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview';
export const spfxSetupLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-development-environment';
export const spfxWebPartLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts';
export const spfxExtensionLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/extensions/overview-extensions';
export const spfxLibraryLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/library-component-overview';
export const spfxApiLink = 'https://learn.microsoft.com/en-us/javascript/api/overview/sharepoint?view=sp-typescript-latest';
export const spfxGuideLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/enterprise-guidance';
export const spfxVivaLink = 'https://learn.microsoft.com/en-us/sharepoint/dev/spfx/viva/overview-viva-connections';
export const spfxLinks = `${spfxOverviewLink}, ${spfxSetupLink}, ${spfxWebPartLink}, ${spfxExtensionLink}, ${spfxLibraryLink}, ${spfxApiLink}, ${spfxGuideLink}, ${spfxVivaLink}`;

export const pnpCommunityHomePageLink = 'https://pnp.github.io/';
export const pnpSpfxSamplesLink = 'https://pnp.github.io/sp-dev-fx-webparts/';
export const pnpCliM365Link = 'https://pnp.github.io/cli-microsoft365/';
export const pnpPSLink = 'https://pnp.github.io/powershell/';
export const pnpReactControlsLink = 'https://pnp.github.io/sp-dev-fx-controls-react/';
export const pnpPropertyPaneControlsLink = 'https://pnp.github.io/sp-dev-fx-property-controls/';
export const pnpLinks = `${pnpCommunityHomePageLink}, ${pnpSpfxSamplesLink}, ${pnpCliM365Link}, ${pnpPSLink}, ${pnpReactControlsLink}, ${pnpPropertyPaneControlsLink}`;

export const spfxSnippetsLink = 'https://marketplace.visualstudio.com/items?itemName=eliostruyf.spfx-snippets';

export const references = `You will be using the following documentation as references: ${msLinks}, ${spfxLinks}, ${pnpLinks}`;

export const community = `You will promote the Microsoft 365 & Power Platform community: ${pnpCommunityHomePageLink}.`;

export const boundary = 'When returning code you will surround it in a MD code block, not HTML.';

export const promptContext = `${personality}${aim}${community}${references}${boundary}`;

export const promptSetupContext = 'You aim to provide support in setting up your development environment for SharePoint Framework (SPFx) development by suggesting the correct version of Node.js and required dependencies.';

export const promptNewContext = `You aim to provide support in creating a new SharePoint Framework project by suggesting the scaffolding form in SPFx Toolkit VS Code extension or by suggesting one of the samples from the ${pnpSpfxSamplesLink}.`;

export const promptCodeContext = `You aim to provide support in writting code for SharePoint Framework (SPFx) solutions by suggesting the correct coding practices or spfx snippets that may be used from ${spfxSnippetsLink}. You will always provide coding sample for a given prompt.`;

export const promptActionContext = 'You aim to provide support additional help in managing your SharePoint Framework (SPFx) solutions. Providing guidance how to create an upgrade or validate report or how to create a CI/CD pipeline for your project using SPFx Toolkit VS Code features.';
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './FrameworkTypes';
export * from './General';
export * from './NodeVersionManagers';
export * from './ProjectFileContent';
export * from './Prompts';
export * from './WebviewCommand';
export * from './WebViewTypes';
export * from './WorkflowTypes';
12 changes: 9 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { PnPWebview } from './webview/PnPWebview';
import { CommandPanel } from './panels/CommandPanel';
import { workspace, window, ThemeIcon, commands, ExtensionContext } from 'vscode';
import * as vscode from 'vscode';
import { workspace, window, ThemeIcon, commands } from 'vscode';
import { PROJECT_FILE, Scaffolder } from './services/Scaffolder';
import { Extension } from './services/Extension';
import { Dependencies } from './services/Dependencies';
import { unlinkSync, readFileSync } from 'fs';
import { TerminalCommandExecuter } from './services/TerminalCommandExecuter';
import { AuthProvider } from './providers/AuthProvider';
import { CliActions } from './services/CliActions';
import { ProjectFileContent } from './constants';
import { PromptHandlers } from './chat/PromptHandlers';
import { CHAT_PARTICIPANT_NAME, ProjectFileContent } from './constants';


export async function activate(context: ExtensionContext) {
export async function activate(context: vscode.ExtensionContext) {

const chatParticipant = vscode.chat.createChatParticipant(CHAT_PARTICIPANT_NAME, PromptHandlers.handle);
chatParticipant.iconPath = vscode.Uri.joinPath(context.extensionUri, 'assets', 'images', 'parker-pnp.png');

Extension.getInstance(context);

TerminalCommandExecuter.register();
Expand Down
Loading

0 comments on commit 14a9e6b

Please sign in to comment.