1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
+ import { log , TargetProfile } from "qsharp-lang" ;
4
5
import * as vscode from "vscode" ;
5
- import { log } from "qsharp-lang" ;
6
-
6
+ import { qsharpExtensionId } from "../common" ;
7
+ import { FullProgramConfig , getActiveProgram } from "../programConfig" ;
8
+ import { getQirForProgram , QirGenerationError } from "../qirGeneration" ;
9
+ import {
10
+ EventType ,
11
+ getActiveDocumentType ,
12
+ QsharpDocumentType ,
13
+ sendTelemetryEvent ,
14
+ UserFlowStatus ,
15
+ UserTaskInvocationType ,
16
+ } from "../telemetry" ;
17
+ import { getRandomGuid } from "../utils" ;
18
+ import { sendMessageToPanel } from "../webviewPanel" ;
19
+ import { getTokenForWorkspace } from "./auth" ;
20
+ import { QuantumUris } from "./networkRequests" ;
21
+ import {
22
+ getPreferredTargetProfile ,
23
+ targetSupportQir ,
24
+ } from "./providerProperties" ;
25
+ import { startRefreshCycle } from "./treeRefresher" ;
7
26
import {
8
27
Job ,
9
28
Target ,
@@ -18,16 +37,6 @@ import {
18
37
queryWorkspaces ,
19
38
submitJob ,
20
39
} from "./workspaceActions" ;
21
- import { QuantumUris } from "./networkRequests" ;
22
- import { getQirForActiveWindow } from "../qirGeneration" ;
23
- import {
24
- getPreferredTargetProfile ,
25
- targetSupportQir ,
26
- } from "./providerProperties" ;
27
- import { startRefreshCycle } from "./treeRefresher" ;
28
- import { getTokenForWorkspace } from "./auth" ;
29
- import { qsharpExtensionId } from "../common" ;
30
- import { sendMessageToPanel } from "../webviewPanel" ;
31
40
32
41
const workspacesSecret = `${ qsharpExtensionId } .workspaces` ;
33
42
@@ -112,42 +121,33 @@ export async function initAzureWorkspaces(context: vscode.ExtensionContext) {
112
121
113
122
const target = treeItem . itemData as Target ;
114
123
115
- let qir = "" ;
116
124
try {
117
- qir = await getQirForActiveWindow (
118
- getPreferredTargetProfile ( target . id ) ,
119
- ) ;
120
- } catch ( e : any ) {
121
- if ( e ?. name === "QirGenerationError" ) {
122
- vscode . window . showErrorMessage ( e . message , { modal : true } ) ;
123
- return ;
124
- }
125
- }
126
- if ( ! qir ) return ;
125
+ const preferredTargetProfile = getPreferredTargetProfile ( target . id ) ;
126
+ const program = await getActiveProgram ( {
127
+ showModalError : true ,
128
+ targetProfileFallback : preferredTargetProfile ,
129
+ } ) ;
127
130
128
- const quantumUris = new QuantumUris (
129
- treeItem . workspace . endpointUri ,
130
- treeItem . workspace . id ,
131
- ) ;
131
+ if ( ! program . success ) {
132
+ throw new QirGenerationError ( program . errorMsg ) ;
133
+ }
132
134
133
- try {
134
- const token = await getTokenForWorkspace ( treeItem . workspace ) ;
135
- if ( ! token ) return ;
136
-
137
- const jobId = await submitJob (
138
- token ,
139
- quantumUris ,
140
- qir ,
141
- target . providerId ,
142
- target . id ,
135
+ await compileAndSubmit (
136
+ program . programConfig ,
137
+ preferredTargetProfile ,
138
+ getActiveDocumentType ( ) ,
139
+ UserTaskInvocationType . Command ,
140
+ workspaceTreeProvider ,
141
+ treeItem . workspace ,
142
+ target ,
143
143
) ;
144
- if ( jobId ) {
145
- // The job submitted fine. Refresh the workspace until it shows up
146
- // and all jobs are finished. Don't await on this, just let it run
147
- startRefreshCycle ( workspaceTreeProvider , treeItem . workspace , jobId ) ;
144
+ } catch ( e : unknown ) {
145
+ log . warn ( "Failed to submit job. " , e ) ;
146
+
147
+ if ( e instanceof QirGenerationError ) {
148
+ vscode . window . showErrorMessage ( e . message , { modal : true } ) ;
149
+ return ;
148
150
}
149
- } catch ( e : any ) {
150
- log . error ( "Failed to submit job. " , e ) ;
151
151
152
152
vscode . window . showErrorMessage ( "Failed to submit the job to Azure." , {
153
153
modal : true ,
@@ -401,3 +401,104 @@ function getHistogramBucketsFromData(
401
401
}
402
402
return undefined ;
403
403
}
404
+
405
+ export async function compileAndSubmit (
406
+ program : FullProgramConfig ,
407
+ targetProfile : TargetProfile ,
408
+ telemetryDocumentType : QsharpDocumentType ,
409
+ telemetryInvocationType : UserTaskInvocationType ,
410
+ workspaceTreeProvider : WorkspaceTreeProvider ,
411
+ workspace : WorkspaceConnection ,
412
+ target : Target ,
413
+ parameters : { jobName : string ; shots : number } | undefined = undefined ,
414
+ ) {
415
+ const associationId = getRandomGuid ( ) ;
416
+ const start = performance . now ( ) ;
417
+ sendTelemetryEvent (
418
+ EventType . SubmitToAzureStart ,
419
+ { associationId, invocationType : telemetryInvocationType } ,
420
+ { } ,
421
+ ) ;
422
+
423
+ const qir = await getQirForProgram (
424
+ program ,
425
+ targetProfile ,
426
+ telemetryDocumentType ,
427
+ ) ;
428
+
429
+ if ( ! parameters ) {
430
+ const result = await promptForJobParameters ( ) ;
431
+ if ( ! result ) {
432
+ sendTelemetryEvent (
433
+ EventType . SubmitToAzureEnd ,
434
+ {
435
+ associationId,
436
+ reason : "user cancelled parameter input" ,
437
+ flowStatus : UserFlowStatus . Aborted ,
438
+ } ,
439
+ { timeToCompleteMs : performance . now ( ) - start } ,
440
+ ) ;
441
+ return ;
442
+ }
443
+ parameters = { jobName : result . jobName , shots : result . numberOfShots } ;
444
+ }
445
+
446
+ const { jobId } = await submitJob (
447
+ workspace ,
448
+ associationId ,
449
+ qir ,
450
+ target . providerId ,
451
+ target . id ,
452
+ parameters . jobName ,
453
+ parameters . shots ,
454
+ ) ;
455
+
456
+ sendTelemetryEvent (
457
+ EventType . SubmitToAzureEnd ,
458
+ {
459
+ associationId,
460
+ reason : "job submitted" ,
461
+ flowStatus : UserFlowStatus . Succeeded ,
462
+ } ,
463
+ { timeToCompleteMs : performance . now ( ) - start } ,
464
+ ) ;
465
+
466
+ // The job submitted fine. Refresh the workspace until it shows up
467
+ // and all jobs are finished. Don't await on this, just let it run
468
+ startRefreshCycle ( workspaceTreeProvider , workspace , jobId ) ;
469
+
470
+ return jobId ;
471
+ }
472
+
473
+ async function promptForJobParameters ( ) : Promise <
474
+ { jobName : string ; numberOfShots : number } | undefined
475
+ > {
476
+ const jobName = await vscode . window . showInputBox ( {
477
+ prompt : "Job name" ,
478
+ value : new Date ( ) . toISOString ( ) ,
479
+ } ) ;
480
+ if ( ! jobName ) return ;
481
+
482
+ // validator for the user-provided number of shots input
483
+ const validateShotsInput = ( input : string ) => {
484
+ const result = parseFloat ( input ) ;
485
+ if ( isNaN ( result ) || Math . floor ( result ) !== result ) {
486
+ return "Number of shots must be an integer" ;
487
+ }
488
+ } ;
489
+
490
+ // prompt the user for the number of shots
491
+ const numberOfShotsPrompted = await vscode . window . showInputBox ( {
492
+ value : "100" ,
493
+ prompt : "Number of shots" ,
494
+ validateInput : validateShotsInput ,
495
+ } ) ;
496
+
497
+ // abort if the user hits <Esc> during shots entry
498
+ if ( numberOfShotsPrompted === undefined ) {
499
+ return ;
500
+ }
501
+
502
+ const numberOfShots = parseInt ( numberOfShotsPrompted ) ;
503
+ return { jobName, numberOfShots } ;
504
+ }
0 commit comments