Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(participant): export to a playground VSCODE-574 #832

Merged
merged 16 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@
"dark": "images/dark/play.svg"
}
},
{
"command": "mdb.exportCodeToPlayground",
"title": "Export Code to Playground"
},
{
"command": "mdb.exportToPython",
"title": "MongoDB: Export To Python 3"
Expand Down Expand Up @@ -747,6 +751,17 @@
"when": "mdb.isPlayground == true"
}
],
"mdb.copilot": [
{
"command": "mdb.exportCodeToPlayground"
}
],
"editor/context": [
{
"submenu": "mdb.copilot",
"group": "1_main@2"
}
],
"commandPalette": [
{
"command": "mdb.selectDatabaseWithParticipant",
Expand Down Expand Up @@ -948,6 +963,10 @@
"command": "mdb.runPlayground",
"when": "false"
},
{
"command": "mdb.exportCodeToPlayground",
"when": "false"
},
{
"command": "mdb.createIndexFromTreeView",
"when": "false"
Expand Down Expand Up @@ -994,6 +1013,12 @@
}
]
},
"submenus": [
{
"id": "mdb.copilot",
"label": "MongoDB Copilot"
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
}
],
"keybindings": [
{
"command": "mdb.runSelectedPlaygroundBlocks",
Expand Down
1 change: 1 addition & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum EXTENSION_COMMANDS {
MDB_RUN_SELECTED_PLAYGROUND_BLOCKS = 'mdb.runSelectedPlaygroundBlocks',
MDB_RUN_ALL_PLAYGROUND_BLOCKS = 'mdb.runAllPlaygroundBlocks',
MDB_RUN_ALL_OR_SELECTED_PLAYGROUND_BLOCKS = 'mdb.runPlayground',
MDB_EXPORT_CODE_TO_PLAYGROUND = 'mdb.exportCodeToPlayground',

MDB_FIX_THIS_INVALID_INTERACTIVE_SYNTAX = 'mdb.fixThisInvalidInteractiveSyntax',
MDB_FIX_ALL_INVALID_INTERACTIVE_SYNTAX = 'mdb.fixAllInvalidInteractiveSyntax',
Expand Down
26 changes: 12 additions & 14 deletions src/editors/playgroundController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
// TODO: this function was copied from the compass-export-to-language module
// https://github.com/mongodb-js/compass/blob/7c4bc0789a7b66c01bb7ba63955b3b11ed40c094/packages/compass-export-to-language/src/modules/count-aggregation-stages-in-string.js
// and should be updated as well when the better solution for the problem will be found.
const countAggregationStagesInString = (str: string) => {

Check warning on line 65 in src/editors/playgroundController.ts

View workflow job for this annotation

GitHub Actions / Test and Build (ubuntu-latest)

Missing return type on function

Check warning on line 65 in src/editors/playgroundController.ts

View workflow job for this annotation

GitHub Actions / Test and Build (macos-latest)

Missing return type on function
if (!dummySandbox) {
dummySandbox = vm.createContext(Object.create(null), {
codeGeneration: { strings: false, wasm: false },
Expand Down Expand Up @@ -492,33 +492,31 @@
}

try {
const progressResult = await vscode.window.withProgress(
return await vscode.window.withProgress(
{
location: ProgressLocation.Notification,
title: 'Running MongoDB playground...',
cancellable: true,
},
async (progress, token) => {
async (progress, token): Promise<ShellEvaluateResult> => {
token.onCancellationRequested(() => {
// If a user clicked the cancel button terminate all playground scripts.
// If the user clicks the cancel button,
// terminate all processes running on the language server.
this._languageServerController.cancelAll();

return { result: undefined };
});

// Run all playground scripts.
const result: ShellEvaluateResult = await this._evaluate(
codeToEvaluate
);

return result;
return Promise.race([
this._evaluate(codeToEvaluate),
new Promise<{ result: undefined }>((resolve) =>
token.onCancellationRequested(() => {
resolve({ result: undefined });
})
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
),
]);
}
);

return progressResult;
} catch (error) {
log.error('Evaluating playground with cancel modal failed', error);

return { result: undefined };
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/mdbExtensionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ export default class MDBExtensionController implements vscode.Disposable {
EXTENSION_COMMANDS.MDB_RUN_ALL_OR_SELECTED_PLAYGROUND_BLOCKS,
() => this._playgroundController.runAllOrSelectedPlaygroundBlocks()
);
this.registerCommand(EXTENSION_COMMANDS.MDB_EXPORT_CODE_TO_PLAYGROUND, () =>
this._participantController.exportCodeToPlayground()
);

this.registerCommand(
EXTENSION_COMMANDS.MDB_FIX_THIS_INVALID_INTERACTIVE_SYNTAX,
Expand Down
82 changes: 78 additions & 4 deletions src/participant/participant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import EXTENSION_COMMANDS from '../commands';
import type { StorageController } from '../storage';
import { StorageVariables } from '../storage';
import { GenericPrompt, isPromptEmpty } from './prompts/generic';
import { ExportToPlaygroundPrompt } from './prompts/exportToPlayground';
import type { ChatResult } from './constants';
import {
askToConnectChatResult,
Expand Down Expand Up @@ -42,6 +43,7 @@ import {
} from '../telemetry/telemetryService';
import { DocsChatbotAIService } from './docsChatbotAIService';
import type TelemetryService from '../telemetry/telemetryService';
import formatError from '../utils/formatError';

const log = createLogger('participant');

Expand Down Expand Up @@ -132,7 +134,7 @@ export default class ParticipantController {
return this._participant;
}

handleError(err: any, command: string): never {
handleError(err: any, command: string): void {
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
let errorCode: string | undefined;
let errorName: ParticipantErrorTypes;
// Making the chat request might fail because
Expand Down Expand Up @@ -168,9 +170,6 @@ export default class ParticipantController {
error_name: errorName,
}
);

// Re-throw other errors so they show up in the UI.
throw err;
}

/**
Expand Down Expand Up @@ -1201,6 +1200,79 @@ export default class ParticipantController {
});
}

async exportCodeToPlayground(): Promise<boolean> {
const activeTextEditor = vscode.window.activeTextEditor;
if (!activeTextEditor) {
await vscode.window.showErrorMessage('Active editor not found.');
return false;
}

const sortedSelections = Array.from(activeTextEditor.selections).sort(
(a, b) => a.start.compareTo(b.start)
);
const selectedText = sortedSelections
.map((selection) => activeTextEditor.document.getText(selection))
.join('\n');
const code =
selectedText || activeTextEditor.document.getText().trim() || '';
try {
const progressResult = await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: 'Exporting code to a playground...',
cancellable: true,
},
async (progress, token): Promise<string | undefined> => {
token.onCancellationRequested(async () => {
await vscode.window.showInformationMessage(
'The running export to a playground operation was canceled.'
);
});

const messages = ExportToPlaygroundPrompt.buildMessages(code);
return Promise.race([
this.getChatResponseContent({
messages,
token,
}),
new Promise<undefined>((resolve) =>
token.onCancellationRequested(() => {
resolve(undefined);
})
Comment on lines +1492 to +1497
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're already passing the cancellation token to getChatResponseContent - why do we need the extra promise here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem that the token passed to the await model.sendRequest(messages, {}, token) terminates the request to the model and the model returns a result anyway. I want to investigate this more separately and maybe file the ticket to vscode.

),
]);
}
);

if (progressResult?.includes("Sorry, I can't assist with that.")) {
void vscode.window.showErrorMessage("Sorry, I can't assist with that.");
return Promise.resolve(false);
Anemy marked this conversation as resolved.
Show resolved Hide resolved
}

if (progressResult) {
const runnableContent = getRunnableContentFromString(progressResult);
if (runnableContent) {
await vscode.commands.executeCommand(
EXTENSION_COMMANDS.OPEN_PARTICIPANT_QUERY_IN_PLAYGROUND,
{
runnableContent,
}
);
}
}

return Promise.resolve(true);
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
this.handleError(error, 'exportToPlayground');
await vscode.window.showErrorMessage(
`An error occurred exporting to a playground: ${
formatError(error).message
}`
);
return Promise.resolve(false);
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
}
}

async chatHandler(
...args: [
vscode.ChatRequest,
Expand Down Expand Up @@ -1249,6 +1321,8 @@ Please see our [FAQ](https://www.mongodb.com/docs/generative-ai-faq/) for more i
}
} catch (e) {
this.handleError(e, request.command || 'generic');
// Re-throw other errors so they show up in the UI.
throw e;
}
}

Expand Down
31 changes: 31 additions & 0 deletions src/participant/prompts/exportToPlayground.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as vscode from 'vscode';

export class ExportToPlaygroundPrompt {
static getAssistantPrompt(): vscode.LanguageModelChatMessage {
const prompt = `You are a MongoDB expert.
Your task is to help the user build MongoDB queries and aggregation pipelines that perform their task.
You convert user's code written in any programming language to the MongoDB Shell syntax.
Take a user prompt as an input string and translate it to the MongoDB Shell language.
Keep your response concise.
You should suggest queries that are performant and correct.
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
Respond with markdown, suggest code in a Markdown code block that begins with \`\`\`javascript and ends with \`\`\`.
Anemy marked this conversation as resolved.
Show resolved Hide resolved
Respond in MongoDB shell syntax using the \`\`\`javascript code block syntax.`;

// eslint-disable-next-line new-cap
return vscode.LanguageModelChatMessage.Assistant(prompt);
}

static getUserPrompt(prompt: string): vscode.LanguageModelChatMessage {
// eslint-disable-next-line new-cap
return vscode.LanguageModelChatMessage.User(prompt);
Anemy marked this conversation as resolved.
Show resolved Hide resolved
}

static buildMessages(prompt: string): vscode.LanguageModelChatMessage[] {
const messages = [
ExportToPlaygroundPrompt.getAssistantPrompt(),
ExportToPlaygroundPrompt.getUserPrompt(prompt),
];

return messages;
alenakhineika marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading