Skip to content

Commit

Permalink
Code completions from kernel behind and experiment (#14770)
Browse files Browse the repository at this point in the history
* Code completions from kernel behind and experiment

* Generate telemetry
  • Loading branch information
DonJayamanne authored Nov 24, 2023
1 parent b354341 commit 953a501
Show file tree
Hide file tree
Showing 16 changed files with 1,039 additions and 232 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ module.exports = {
'@typescript-eslint/no-floating-promises': [
'error',
{
ignoreVoid: false
ignoreVoid: true
}
],

Expand Down
80 changes: 75 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,9 @@
"type": "array",
"default": [],
"items": {
"enum": []
"enum": [
"KernelCompletions"
]
},
"markdownDescription": "%jupyter.configuration.jupyter.experiments.optInto.markdownDescription%",
"scope": "application"
Expand All @@ -1506,7 +1508,9 @@
"type": "array",
"default": [],
"items": {
"enum": []
"enum": [
"KernelCompletions"
]
},
"markdownDescription": "%jupyter.configuration.jupyter.experiments.optOutFrom.markdownDescription%",
"scope": "application"
Expand Down Expand Up @@ -1971,8 +1975,7 @@
},
"jupyter.enableKernelCompletions": {
"type": "boolean",
"default": false,
"description": "Enable Non-Python Kernel Completions",
"default": true,
"markdownDescription": "%jupyter.configuration.jupyter.enableKernelCompletions.markdownDescription%",
"scope": "application"
},
Expand Down Expand Up @@ -2036,7 +2039,74 @@
]
},
"markdownDescription": "%jupyter.configuration.jupyter.completionTriggerCharacters.markdownDescription%",
"scope": "machine-overridable"
"scope": "machine-overridable",
"examples": [
{
"bash": [
"$",
"{"
]
},
{
"java": [
".",
"@",
"#"
]
},
{
"javascript": [
".",
"\"",
"'",
"/",
"@"
]
},
{
"julia": [
"."
]
},
{
"python": [
".",
"%",
"'",
"\""
]
},
{
"typescript": [
".",
"\"",
"'",
"/",
"@",
"<"
]
},
{
"rust": [
".",
":"
]
},
{
"scala": [
"."
]
},
{
"xonsh": [
" ",
".",
"%",
"'",
"\""
]
}
]
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@
"comment": ["{Locked='PYTHONNOUSERSITE'}", "{Locked='PYTHONPATH'}"]
},
"jupyter.configuration.jupyter.enableExtendedKernelCompletions.markdownDescription": "Enables Jedi support for extended IntelliSense completions in running Python Jupyter kernels for Python (see this [setting](https://ipython.readthedocs.io/en/stable/config/options/terminal.html?highlight=use_jedi#configtrait-Completer.use_jedi)). This can greatly impact notebook cell execution performance. Use with caution.",
"jupyter.configuration.jupyter.enableKernelCompletions.markdownDescription": "Enables support for auto completion in non-Python Notebooks and Interactive Windows using the associated Jupyter Kernel. \n\nWarning: This can greatly impact cell execution performance. Use with caution.",
"jupyter.configuration.jupyter.enableKernelCompletions.markdownDescription": "Enable code completions in Notebooks and Interactive Windows using results from the associated Jupyter Kernel.",
"DataScience.exportDialogTitle": {
"message": "Export to Jupyter Notebook",
"comment": ["{Locked='Notebook'}"]
Expand Down
55 changes: 53 additions & 2 deletions src/gdpr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,33 @@
"${include}": [
"${F1}"
]
}
*/
//Telemetry.ExecuteCode
/* __GDPR__
"DATASCIENCE.EXECUTE_CODE" : {
"actionSource": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether this was started by Jupyter extension or a 3rd party. Common to most of the events.","owner":"donjayamanne"},
"disableUI": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether the notebook startup UI (progress indicator & the like) was displayed to the user or not. If its not displayed, then its considered an auto start (start in the background, like pre-warming kernel) Common to most of the events.","owner":"donjayamanne"},
"userExecutedCell": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether the user executed a cell. Common to most of the events.","owner":"donjayamanne"},
"resourceHash": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Hash of the resource (notebook.uri or pythonfile.uri associated with this). If we run the same notebook tomorrow, the hash will be the same. Used to check whether a particular notebook fails across time or not. This is also used to map different telemetry events related to this same resource. E.g. we could have an event sent for starting a notebook with this hash, and then later we get yet another event indicating starting a notebook failed. And another event indicating the Python environment used for this notebook is a conda environment or we have some other event indicating some other piece of data for this resource. With the information across multiple resources we can now join the different data points and have a better understanding of what is going on, e.g. why something failed. Common to most of the events.","owner":"donjayamanne"},
"pythonEnvironmentVersion": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Found plenty of issues when starting Conda Python 3.7, Python 3.7 Python 3.9 (in early days when ipykernel was not up to date) Common to most of the events.","owner":"donjayamanne"},
"pythonEnvironmentType": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Found plenty of issues when starting kernels with conda, hence useful to capture this info. Common to most of the events.","owner":"donjayamanne"},
"pythonEnvironmentPath": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"A key, so that rest of the information is tied to this. (hash) Common to most of the events.","owner":"donjayamanne"},
"pythonEnvironmentPackages": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Comma delimited list of hashed packages & their versions. Common to most of the events.","owner":"donjayamanne"},
"kernelSessionId": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Unique identifier for an instance of a notebook session. If we restart or run this notebook tomorrow, this id will be different. Id could be something as simple as a hash of the current Epoch time. Common to most of the events.","owner":"donjayamanne"},
"kernelLanguage": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Language of the kernel connection. Common to most of the events.","owner":"donjayamanne"},
"kernelSpecHash": {"classification":"EndUserPseudonymizedInformation","purpose":"FeatureInsight","comment":"Hash of the kernelspec file (so we do not end up with duplicate telemetry for the same user in same session)","owner":"donjayamanne"},
"kernelId": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Hash of the Kernel Connection id. Common to most of the events.","owner":"donjayamanne"},
"kernelConnectionType": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether kernel was started using kernel spec, interpreter, etc. Common to most of the events.","owner":"donjayamanne"},
"isUsingActiveInterpreter": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether this resource is using the active Python interpreter or not. Common to most of the events.","owner":"donjayamanne"},
"capturedEnvVars": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether we managed to capture the environment variables or not. In the case of conda environments, `false` would be an error condition, as we must have env variables for conda to work. Common to most of the events.","owner":"donjayamanne"},
"newKernelPicker": {"classification":"PublicNonPersonalData","purpose":"PerformanceAndHealth","comment":"Whether using the new kernel picker or not. This will be obsolete once we ship the new kernel picker.","owner":"donjayamanne"},
"resourceType": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Used to determine whether this event is related to a Notebooks or Interactive window. Common to most of the events.","owner":"donjayamanne"},
"extensionId": {"classification":"SystemMetaData","purpose":"FeatureInsight","comment":"Extension Id that's attempting to use the API.","owner":"donjayamanne"},
"${include}": [
"${F1}"
]
}
*/
Expand Down Expand Up @@ -403,8 +430,12 @@
"kernelLanguage": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Language of the kernel spec.","owner":"donjayamanne"},
"kernelId": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Hash of the Kernel Connection id.","owner":"donjayamanne"},
"cancelled": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether the completion request was cancelled or not.","owner":"donjayamanne"},
"resolved": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we resolved the documentation or not.","owner":"donjayamanne"},
"resolveDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Time taken to resolve the documentation.","owner":"donjayamanne","isMeasurement":true},
"completed": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we completed the request.","owner":"donjayamanne"},
"kernelStatusAfterRequest": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Status of the kernel at the time we make a request for the resolve completion information","owner":"donjayamanne"},
"kernelStatusBeforeRequest": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Status of the kernel at the time we make a request for the resolve completion information","owner":"donjayamanne"},
"requestSent": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we send the request to resolve the completion item.","owner":"donjayamanne"},
"requestDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total time taken to complete the request.","owner":"donjayamanne","isMeasurement":true},
"completionItems": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Number of items returned.","owner":"donjayamanne","isMeasurement":true},
"${include}": [
"${F1}"
Expand All @@ -420,6 +451,26 @@
"${include}": [
"${F1}"
]
}
*/
//Telemetry.KernelCodeCompletionResolve
/* __GDPR__
"DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_RESOLVE" : {
"kernelConnectionType": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"What kind of kernel spec did we fail to create.","owner":"donjayamanne"},
"kernelLanguage": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Language of the kernel spec.","owner":"donjayamanne"},
"kernelId": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Hash of the Kernel Connection id.","owner":"donjayamanne"},
"cancelled": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether the completion request was cancelled or not.","owner":"donjayamanne"},
"kernelStatusBeforeRequest": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Status of the kernel at the time we make a request for the resolve completion information","owner":"donjayamanne"},
"kernelStatusAfterRequest": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Status of the kernel at the time we make a request for the resolve completion information","owner":"donjayamanne"},
"requestTimedout": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we timedout waiting for the request to complete.","owner":"donjayamanne"},
"completedWithData": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether the kernel completion resolve request returned any data.","owner":"donjayamanne"},
"requestSent": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we send the request to resolve the completion item.","owner":"donjayamanne"},
"completed": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we resolved the documentation or not.","owner":"donjayamanne"},
"requestDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total time taken to resolve the documentation.","owner":"donjayamanne","isMeasurement":true},
"${include}": [
"${F1}"
]
}
*/
Expand Down
19 changes: 12 additions & 7 deletions src/kernels/kernelExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export class NotebookKernelExecution implements INotebookKernelExecution {
}
public async executeCell(cell: NotebookCell, codeOverride?: string | undefined): Promise<NotebookCellRunState> {
traceCellMessage(cell, `NotebookKernelExecution.executeCell (1), ${getDisplayPath(cell.notebook.uri)}`);
const stopWatch = new StopWatch();
if (cell.kind == NotebookCellKind.Markup) {
return NotebookCellRunState.Success;
}
Expand All @@ -133,7 +134,6 @@ export class NotebookKernelExecution implements INotebookKernelExecution {
this.kernel.resourceUri,
this.kernel.kernelConnectionMetadata
);
sendKernelTelemetryEvent(this.kernel.resourceUri, Telemetry.ExecuteCell);
const sessionPromise = this.kernel.start(new DisplayOptions(false));

// If we're restarting, wait for it to finish
Expand All @@ -149,6 +149,7 @@ export class NotebookKernelExecution implements INotebookKernelExecution {
`NotebookKernelExecution.executeCell completed (3), ${getDisplayPath(cell.notebook.uri)}`
);
traceVerbose(`Cell ${cell.index} executed with state ${result[0]}`);
sendKernelTelemetryEvent(this.kernel.resourceUri, Telemetry.ExecuteCell, { duration: stopWatch.elapsedTime });

return result[0];
}
Expand All @@ -158,7 +159,6 @@ export class NotebookKernelExecution implements INotebookKernelExecution {
this.kernel.resourceUri,
this.kernel.kernelConnectionMetadata
);
sendKernelTelemetryEvent(this.kernel.resourceUri, Telemetry.ExecuteCell);
const sessionPromise = this.kernel.start(new DisplayOptions(false));

// If we're restarting, wait for it to finish
Expand All @@ -168,11 +168,16 @@ export class NotebookKernelExecution implements INotebookKernelExecution {
const result = executionQueue.queueCode(code, extensionId, token);
traceVerbose(`Queue code ${result.executionId} from ${extensionId} after ${stopWatch.elapsedTime}ms:\n${code}`);
result.result
.finally(
() =>
!token.isCancellationRequested &&
traceInfo(`Execution of code ${result.executionId} completed in ${stopWatch.elapsedTime}ms`)
)
.finally(() => {
!token.isCancellationRequested &&
traceInfo(`Execution of code ${result.executionId} completed in ${stopWatch.elapsedTime}ms`);
sendKernelTelemetryEvent(
this.kernel.resourceUri,
Telemetry.ExecuteCode,
{ duration: stopWatch.elapsedTime },
{ extensionId }
);
})
.catch(noop);
return result;
}
Expand Down
2 changes: 2 additions & 0 deletions src/platform/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ export enum Telemetry {
CellOutputMimeType = 'DS_INTERNAL.CELL_OUTPUT_MIME_TYPE',
JupyterApiUsage = 'DATASCIENCE.JUPYTER_API_USAGE',
KernelCodeCompletion = 'DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION',
KernelCodeCompletionResolve = 'DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_RESOLVE',
KernelCodeCompletionCannotResolve = 'DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_CANNOT_RESOLVE',
JupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_KERNEL_API_USAGE',
NewJupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_NEW_KERNEL_API_USAGE',
Expand Down Expand Up @@ -446,6 +447,7 @@ export enum Telemetry {
SwitchKernel = 'DS_INTERNAL.SWITCH_KERNEL',
KernelCount = 'DS_INTERNAL.KERNEL_COUNT',
ExecuteCell = 'DATASCIENCE.EXECUTE_CELL',
ExecuteCode = 'DATASCIENCE.EXECUTE_CODE',
ResumeCellExecution = 'DATASCIENCE.RESUME_EXECUTE_CELL',
/**
* Sent when a command we register is executed.
Expand Down
3 changes: 2 additions & 1 deletion src/platform/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ export interface IAsyncDisposableRegistry extends IAsyncDisposable {
}

export enum Experiments {
DataViewerContribution = 'DataViewerContribution'
DataViewerContribution = 'DataViewerContribution',
KernelCompletions = 'KernelCompletions'
}

/**
Expand Down
14 changes: 14 additions & 0 deletions src/platform/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ export function sendTelemetryEvent<P extends IEventNamePropertyMapping, E extend
);
}

export type TelemetryProperties<
E extends keyof P,
P extends IEventNamePropertyMapping = IEventNamePropertyMapping
> = P[E] extends TelemetryEventInfo<infer R>
? ExcludeType<R, number> extends never | undefined
? undefined
: ExcludeType<R, number>
: undefined | undefined;

export type TelemetryMeasures<
E extends keyof P,
P extends IEventNamePropertyMapping = IEventNamePropertyMapping
> = P[E] extends TelemetryEventInfo<infer R> ? PickType<UnionToIntersection<R>, number> : undefined;

function sendTelemetryEventInternal<P extends IEventNamePropertyMapping, E extends keyof P>(
eventName: E,
measures?: Record<string, number>,
Expand Down
Loading

0 comments on commit 953a501

Please sign in to comment.