Path: blob/main/extensions/copilot/test/prompts/newNotebookCell.stest.ts
13389 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import assert from 'assert';5import { newNotebookCodeCell } from '../../src/extension/intents/node/newNotebookIntent';6import { IChatMLFetcher } from '../../src/platform/chat/common/chatMLFetcher';7import { IEndpointProvider } from '../../src/platform/endpoint/common/endpointProvider';8import { ITestingServicesAccessor } from '../../src/platform/test/node/services';9import { INotebookSection } from '../../src/util/common/notebooks';10import { CancellationToken } from '../../src/util/vs/base/common/cancellation';11import { URI } from '../../src/util/vs/base/common/uri';12import { IInstantiationService } from '../../src/util/vs/platform/instantiation/common/instantiation';13import { ssuite, stest } from '../base/stest';14import { fetchConversationOptions } from '../e2e/scenarioTest';15import { isValidPythonFile } from '../simulation/diagnosticProviders/python';1617ssuite({ title: 'newNotebook', subtitle: 'prompt', location: 'panel' }, () => {18stest({ description: 'generate code cell', language: 'python' }, async (testingServiceCollection) => {19const accessor = testingServiceCollection.createTestingAccessor();20const endpoint = await accessor.get(IEndpointProvider).getChatEndpoint('copilot-base');21const topic = 'Creating Random Arrays with Numpy';22const sections: INotebookSection[] = [23{24'title': 'Import Required Libraries',25'content': 'Import the necessary libraries, including NumPy.'26},27{28'title': 'Create Random Arrays',29'content': 'Use NumPy to create random arrays of various shapes and sizes, including 1D, 2D, and 3D arrays.'30},31{32'title': 'Seed the Random Number Generator',33'content': 'Use the seed() function to seed the random number generator for reproducibility.'34},35{36'title': 'Generate Random Integers',37'content': 'Use the randint() function to generate random integers within a specified range.'38}39];4041const cells: string[] = [];42const firstCell = await newNotebookCodeCell(accessor.get(IInstantiationService), accessor.get(IChatMLFetcher), endpoint, fetchConversationOptions(), undefined, topic, sections[0], '', 'python', URI.file('sample.ipynb'), CancellationToken.None);43assert.ok(firstCell !== undefined, 'code should not be empty');44assert.ok(firstCell.includes('import numpy as np'), 'code should include import numpy as np');4546const firstCellIsValid = await validatePythonCode(accessor, firstCell!);47assert.ok(firstCellIsValid, 'first cell code should be valid python code');48cells.push(firstCell!);4950const secondCell = await newNotebookCodeCell(accessor.get(IInstantiationService), accessor.get(IChatMLFetcher), endpoint, fetchConversationOptions(), undefined, topic, sections[1], cells.join('\n'), 'python', URI.file('sample.ipynb'), CancellationToken.None);51assert.ok(secondCell !== undefined, 'code should not be empty');52assert.ok(!secondCell.includes('import numpy'), 'code should not include import numpy again');53// const secondCellIsValid = await validatePythonCode(accessor, secondCell!);54// assert.ok(secondCellIsValid, 'second cell code should be valid python code');55});5657stest({ description: 'Generate code cell (numpy)', language: 'python' }, async (testingServiceCollection) => {58const accessor = testingServiceCollection.createTestingAccessor();59const endpoint = await accessor.get(IEndpointProvider).getChatEndpoint('copilot-base');60const topic = 'A Jupyter notebook that creates a structured array with NumPy.';61const sections: INotebookSection[] = [62{63title: 'Import Required Libraries', content: 'Import the necessary libraries, including NumPy.'64},65{66title: 'Create Structured Array', content: 'Use NumPy to create a structured array with named fields and data types.'67},68{69title: 'Accessing Structured Array Elements', content: 'Access individual elements of the structured array using the field names.'70},71{72title: 'Iterating over Structured Arrays', content: 'Iterate over the structured array using a fo…nd access the field values for each element.'73}74];7576const cells: string[] = [];77const firstCell = await newNotebookCodeCell(accessor.get(IInstantiationService), accessor.get(IChatMLFetcher), endpoint, fetchConversationOptions(), undefined, topic, sections[0], '', 'python', URI.file('sample.ipynb'), CancellationToken.None);78assert.ok(firstCell !== undefined, 'code should not be empty');79assert.ok(firstCell.includes('import numpy as np'), 'code should include import numpy as np');8081const firstCellIsValid = await validatePythonCode(accessor, firstCell!);82assert.ok(firstCellIsValid, 'code should be valid python code');83cells.push(firstCell!);8485for (let i = 1; i < sections.length; i++) {86const cell = await newNotebookCodeCell(accessor.get(IInstantiationService), accessor.get(IChatMLFetcher), endpoint, fetchConversationOptions(), undefined, topic, sections[i], cells.join('\n'), 'python', URI.file('sample.ipynb'), CancellationToken.None);87assert.ok(cell !== undefined, 'code should not be empty');88assert.ok(!cell.includes('import numpy'), 'code should not include import numpy again');89const cellIsValid = await validatePythonCode(accessor, cell!);90assert.ok(cellIsValid, 'code should be valid python code');91cells.push(cell!);92}93});9495stest({ description: 'Generate code cell (seaborn + pandas)', language: 'python' }, async (testingServiceCollection) => {96const accessor = testingServiceCollection.createTestingAccessor();97const endpoint = await accessor.get(IEndpointProvider).getChatEndpoint('copilot-base');98const topic = 'A Jupyter notebook that loads planets data from Seaborn and performs aggregation in Pandas.';99const sections: INotebookSection[] = [100{ title: 'Import Required Libraries', content: 'Import the necessary libraries, including Pandas and Seaborn.' },101102{ title: 'Load Planets Data', content: 'Load the planets data from Seaborn into a Pandas DataFrame.' },103104{ title: 'Data Cleaning', content: 'Clean the data by removing missing values and converting data types if necessary.' },105106{ title: 'Aggregation with GroupBy', content: 'Use the groupby() function to group the data…erform aggregation operations on the groups.' },107108{ title: 'Aggregation with Pivot Tables', content: 'Use the pivot_table() function to create a p… summarizes the data by one or more columns.' },109];110111const cells: string[] = [];112const firstCell = await newNotebookCodeCell(accessor.get(IInstantiationService), accessor.get(IChatMLFetcher), endpoint, fetchConversationOptions(), undefined, topic, sections[0], '', 'python', URI.file('sample.ipynb'), CancellationToken.None);113assert.ok(firstCell !== undefined, 'code should not be empty');114assert.ok(firstCell.includes('import seaborn'), 'code should include import seaborn');115assert.ok(firstCell.includes('import pandas'), 'code should include import pandas');116117const firstCellIsValid = await validatePythonCode(accessor, firstCell!);118assert.ok(firstCellIsValid, 'code should be valid python code');119cells.push(firstCell!);120121for (let i = 1; i < sections.length; i++) {122const cell = await newNotebookCodeCell(accessor.get(IInstantiationService), accessor.get(IChatMLFetcher), endpoint, fetchConversationOptions(), undefined, topic, sections[i], cells.join('\n'), 'python', URI.file('sample.ipynb'), CancellationToken.None);123assert.ok(cell !== undefined, 'code should not be empty');124assert.ok(!cell.includes('import seaborn'), 'code should not include import seaborn again');125assert.ok(!cell.includes('import pandas'), 'code should not include import pandas again');126const cellIsValid = await validatePythonCode(accessor, cell!);127assert.ok(cellIsValid, 'code should be valid python code');128cells.push(cell!);129}130});131});132133// async function validatePythonCode(pythonCode: string): Promise<boolean> {134// const validateCode = `135// import codeop136// import re137138// def validate_python_code(code):139// # Split the code into separate statements140// statements = re.split(r"\\n(?=\\w)", code)141// for statement in statements:142// codeop.compile_command(statement)143144// validate_python_code("""${pythonCode}""")145// `;146// if (pythonCode.startsWith('```')) {147// return false;148// }149150// return new Promise((resolve) => {151// exec(`python3 -c "${validateCode.replace(/["\\]/g, '\\$&')}"`, (error, stdout, stderr) => {152// if (error) {153// return resolve(false);154// } else if (stderr && stderr.length > 0) {155// return resolve(false);156// }157158// resolve(true);159// });160// });161// }162163async function validatePythonCode(accessor: ITestingServicesAccessor, pythonCode: string): Promise<boolean> {164const escapedPythonCode = pythonCode.replace(/`/g, 'BACKTICK_PLACEHOLDER');165const validateCode = `166import codeop167import re168169def validate_python_code(code):170# Replace placeholder string with actual backtick171code = code.replace('BACKTICK_PLACEHOLDER', chr(96))172# Split the code into separate statements173statements = re.split(r"\\n(?=\\w)", code)174for statement in statements:175codeop.compile_command(statement)176177validate_python_code("""${escapedPythonCode}""")178`;179return isValidPythonFile(accessor, validateCode);180}181182183