mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 01:25:39 -05:00
ML - dashboard icons and links (#10153)
* ML - dashboard icons and links
This commit is contained in:
62
extensions/machine-learning/src/modelManagement/artifacts.ts
Normal file
62
extensions/machine-learning/src/modelManagement/artifacts.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as msRest from '@azure/ms-rest-js';
|
||||
import * as Models from './interfaces';
|
||||
import * as Mappers from './mappers';
|
||||
import * as Parameters from './parameters';
|
||||
import { AzureMachineLearningWorkspacesContext } from '@azure/arm-machinelearningservices';
|
||||
|
||||
export class Artifacts {
|
||||
private readonly client: AzureMachineLearningWorkspacesContext;
|
||||
|
||||
constructor(client: AzureMachineLearningWorkspacesContext) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
getArtifactContentInformation2(subscriptionId: string, resourceGroupName: string, workspaceName: string, origin: string, container: string, options?: Models.ArtifactAPIGetArtifactContentInformation2OptionalParams): Promise<Models.GetArtifactContentInformation2Response>;
|
||||
getArtifactContentInformation2(subscriptionId: string, resourceGroupName: string, workspaceName: string, origin: string, container: string, callback: msRest.ServiceCallback<Models.ArtifactContentInformationDto>): void;
|
||||
getArtifactContentInformation2(subscriptionId: string, resourceGroupName: string, workspaceName: string, origin: string, container: string, options: Models.ArtifactAPIGetArtifactContentInformation2OptionalParams, callback: msRest.ServiceCallback<Models.ArtifactContentInformationDto>): void;
|
||||
getArtifactContentInformation2(subscriptionId: string, resourceGroupName: string, workspaceName: string, origin: string, container: string, options?: Models.ArtifactAPIGetArtifactContentInformation2OptionalParams | msRest.ServiceCallback<Models.ArtifactContentInformationDto>, callback?: msRest.ServiceCallback<Models.ArtifactContentInformationDto>): Promise<Models.GetArtifactContentInformation2Response> {
|
||||
return this.client.sendOperationRequest(
|
||||
{
|
||||
subscriptionId,
|
||||
resourceGroupName,
|
||||
workspaceName,
|
||||
origin,
|
||||
container,
|
||||
options
|
||||
},
|
||||
getArtifactContentInformation2OperationSpec,
|
||||
callback) as Promise<Models.GetArtifactContentInformation2Response>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const serializer = new msRest.Serializer(Mappers);
|
||||
const getArtifactContentInformation2OperationSpec: msRest.OperationSpec = {
|
||||
httpMethod: 'GET',
|
||||
path: 'artifact/v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.MachineLearningServices/workspaces/{workspaceName}/artifacts/contentinfo/{origin}/{container}',
|
||||
urlParameters: [
|
||||
Parameters.subscriptionId,
|
||||
Parameters.resourceGroupName,
|
||||
Parameters.workspaceName,
|
||||
Parameters.origin,
|
||||
Parameters.container,
|
||||
Parameters.apiVersion
|
||||
],
|
||||
queryParameters: [
|
||||
Parameters.projectName0,
|
||||
Parameters.path1,
|
||||
Parameters.accountName
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
bodyMapper: Mappers.ArtifactContentInformationDto
|
||||
},
|
||||
default: {}
|
||||
},
|
||||
serializer
|
||||
};
|
||||
78
extensions/machine-learning/src/modelManagement/assets.ts
Normal file
78
extensions/machine-learning/src/modelManagement/assets.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as msRest from '@azure/ms-rest-js';
|
||||
import * as Models from './interfaces';
|
||||
import * as Mappers from './mappers';
|
||||
import * as Parameters from './parameters';
|
||||
import { AzureMachineLearningWorkspacesContext } from '@azure/arm-machinelearningservices';
|
||||
|
||||
export class Assets {
|
||||
private readonly client: AzureMachineLearningWorkspacesContext;
|
||||
|
||||
constructor(client: AzureMachineLearningWorkspacesContext) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
queryById(
|
||||
subscriptionId: string,
|
||||
resourceGroup: string,
|
||||
workspace: string,
|
||||
id: string,
|
||||
options?: msRest.RequestOptionsBase
|
||||
): Promise<Models.AssetsQueryByIdResponse>;
|
||||
queryById(
|
||||
subscriptionId: string,
|
||||
resourceGroup: string,
|
||||
workspace: string,
|
||||
id: string,
|
||||
callback: msRest.ServiceCallback<Models.Asset>
|
||||
): void;
|
||||
queryById(
|
||||
subscriptionId: string,
|
||||
resourceGroup: string,
|
||||
workspace: string,
|
||||
id: string,
|
||||
options: msRest.RequestOptionsBase,
|
||||
callback: msRest.ServiceCallback<Models.Asset>
|
||||
): void;
|
||||
queryById(
|
||||
subscriptionId: string,
|
||||
resourceGroup: string,
|
||||
workspace: string,
|
||||
id: string,
|
||||
options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Asset>,
|
||||
callback?: msRest.ServiceCallback<Models.Asset>
|
||||
): Promise<Models.AssetsQueryByIdResponse> {
|
||||
return this.client.sendOperationRequest(
|
||||
{
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
workspace,
|
||||
id,
|
||||
options
|
||||
},
|
||||
queryByIdOperationSpec,
|
||||
callback
|
||||
) as Promise<Models.AssetsQueryByIdResponse>;
|
||||
}
|
||||
}
|
||||
|
||||
const serializer = new msRest.Serializer(Mappers);
|
||||
const queryByIdOperationSpec: msRest.OperationSpec = {
|
||||
httpMethod: 'GET',
|
||||
path:
|
||||
'modelmanagement/v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.MachineLearningServices/workspaces/{workspace}/assets/{id}',
|
||||
urlParameters: [Parameters.subscriptionId, Parameters.resourceGroup, Parameters.workspace, Parameters.id],
|
||||
responses: {
|
||||
200: {
|
||||
bodyMapper: Mappers.Asset
|
||||
},
|
||||
default: {
|
||||
bodyMapper: Mappers.ModelErrorResponse
|
||||
}
|
||||
},
|
||||
serializer
|
||||
};
|
||||
@@ -0,0 +1,329 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import * as constants from '../common/constants';
|
||||
import { azureResource } from '../typings/azure-resource';
|
||||
import { AzureMachineLearningWorkspaces } from '@azure/arm-machinelearningservices';
|
||||
import { TokenCredentials } from '@azure/ms-rest-js';
|
||||
import { WorkspaceModels } from './workspacesModels';
|
||||
import { AzureMachineLearningWorkspacesOptions, Workspace } from '@azure/arm-machinelearningservices/esm/models';
|
||||
import { WorkspaceModel, Asset, IArtifactParts } from './interfaces';
|
||||
import { Config } from '../configurations/config';
|
||||
import { Assets } from './assets';
|
||||
import * as polly from 'polly-js';
|
||||
import { Artifacts } from './artifacts';
|
||||
import { HttpClient } from '../common/httpClient';
|
||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as utils from '../common/utils';
|
||||
|
||||
/**
|
||||
* Azure Model Service
|
||||
*/
|
||||
export class AzureModelRegistryService {
|
||||
|
||||
private _amlClient: AzureMachineLearningWorkspaces | undefined;
|
||||
private _modelClient: WorkspaceModels | undefined;
|
||||
/**
|
||||
* Creates new service
|
||||
*/
|
||||
constructor(
|
||||
private _apiWrapper: ApiWrapper,
|
||||
private _config: Config,
|
||||
private _httpClient: HttpClient,
|
||||
private _outputChannel: vscode.OutputChannel) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of azure accounts
|
||||
*/
|
||||
public async getAccounts(): Promise<azdata.Account[]> {
|
||||
return await this._apiWrapper.getAllAccounts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of azure subscriptions
|
||||
* @param account azure account
|
||||
*/
|
||||
public async getSubscriptions(account: azdata.Account | undefined): Promise<azureResource.AzureResourceSubscription[] | undefined> {
|
||||
const data = <azureResource.GetSubscriptionsResult>await this._apiWrapper.executeCommand(constants.azureSubscriptionsCommand, account, true);
|
||||
return data?.subscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of azure groups
|
||||
* @param account azure account
|
||||
* @param subscription azure subscription
|
||||
*/
|
||||
public async getGroups(
|
||||
account: azdata.Account | undefined,
|
||||
subscription: azureResource.AzureResourceSubscription | undefined): Promise<azureResource.AzureResource[] | undefined> {
|
||||
const data = <azureResource.GetResourceGroupsResult>await this._apiWrapper.executeCommand(constants.azureResourceGroupsCommand, account, subscription, true);
|
||||
return data?.resourceGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of workspaces
|
||||
* @param account azure account
|
||||
* @param subscription azure subscription
|
||||
* @param resourceGroup azure resource group
|
||||
*/
|
||||
public async getWorkspaces(
|
||||
account: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
resourceGroup: azureResource.AzureResource | undefined): Promise<Workspace[]> {
|
||||
return await this.fetchWorkspaces(account, subscription, resourceGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of models
|
||||
* @param account azure account
|
||||
* @param subscription azure subscription
|
||||
* @param resourceGroup azure resource group
|
||||
* @param workspace azure workspace
|
||||
*/
|
||||
public async getModels(
|
||||
account: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
resourceGroup: azureResource.AzureResource,
|
||||
workspace: Workspace): Promise<WorkspaceModel[] | undefined> {
|
||||
return await this.fetchModels(account, subscription, resourceGroup, workspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download an azure model to a temporary location
|
||||
* @param account azure account
|
||||
* @param subscription azure subscription
|
||||
* @param resourceGroup azure resource group
|
||||
* @param workspace azure workspace
|
||||
* @param model azure model
|
||||
*/
|
||||
public async downloadModel(
|
||||
account: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
resourceGroup: azureResource.AzureResource,
|
||||
workspace: Workspace,
|
||||
model: WorkspaceModel): Promise<string> {
|
||||
let downloadedFilePath: string = '';
|
||||
|
||||
for (const tenant of account.properties.tenants) {
|
||||
try {
|
||||
const downloadUrls = await this.getAssetArtifactsDownloadLinks(account, subscription, resourceGroup, workspace, model, tenant);
|
||||
if (downloadUrls && downloadUrls.length > 0) {
|
||||
downloadedFilePath = await this.execDownloadArtifactTask(downloadUrls[0]);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
return downloadedFilePath;
|
||||
}
|
||||
|
||||
public set AzureMachineLearningClient(value: AzureMachineLearningWorkspaces) {
|
||||
this._amlClient = value;
|
||||
}
|
||||
|
||||
public set ModelClient(value: WorkspaceModels) {
|
||||
this._modelClient = value;
|
||||
}
|
||||
|
||||
public async signInToAzure(): Promise<void> {
|
||||
await this._apiWrapper.executeCommand(constants.signInToAzureCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the background task to download the artifact
|
||||
*/
|
||||
private async execDownloadArtifactTask(downloadUrl: string): Promise<string> {
|
||||
let results = await utils.executeTasks(this._apiWrapper, constants.downloadModelMsgTaskName, [this.downloadArtifact(downloadUrl)], true);
|
||||
return results && results.length > 0 ? results[0] : constants.noResultError;
|
||||
}
|
||||
|
||||
private async downloadArtifact(downloadUrl: string): Promise<string> {
|
||||
let tempFilePath = path.join(os.tmpdir(), `ads_ml_temp_${UUID.generateUuid()}`);
|
||||
await this._httpClient.download(downloadUrl, tempFilePath, this._outputChannel);
|
||||
return tempFilePath;
|
||||
}
|
||||
|
||||
private async fetchWorkspaces(account: azdata.Account, subscription: azureResource.AzureResourceSubscription, resourceGroup: azureResource.AzureResource | undefined): Promise<Workspace[]> {
|
||||
let resources: Workspace[] = [];
|
||||
|
||||
try {
|
||||
for (const tenant of account.properties.tenants) {
|
||||
const client = await this.getAmlClient(account, subscription, tenant);
|
||||
let result = resourceGroup ? await client.workspaces.listByResourceGroup(resourceGroup.name) : await client.workspaces.listBySubscription();
|
||||
if (result) {
|
||||
resources.push(...result);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
private async fetchModels(
|
||||
account: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
resourceGroup: azureResource.AzureResource,
|
||||
workspace: Workspace): Promise<WorkspaceModel[]> {
|
||||
let resources: WorkspaceModel[] = [];
|
||||
|
||||
for (const tenant of account.properties.tenants) {
|
||||
try {
|
||||
let options: AzureMachineLearningWorkspacesOptions = {
|
||||
baseUri: this.getBaseUrl(workspace, this._config.amlModelManagementUrl)
|
||||
};
|
||||
const client = await this.getAmlClient(account, subscription, tenant, options, this._config.amlApiVersion);
|
||||
let modelsClient = this.getModelClient(client);
|
||||
resources = resources.concat(await modelsClient.listModels(resourceGroup.name, workspace.name || ''));
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
private async fetchModelAsset(
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
resourceGroup: azureResource.AzureResource,
|
||||
workspace: Workspace,
|
||||
model: WorkspaceModel,
|
||||
client: AzureMachineLearningWorkspaces): Promise<Asset> {
|
||||
|
||||
const modelId = this.getModelId(model);
|
||||
if (modelId) {
|
||||
let modelsClient = new Assets(client);
|
||||
return await modelsClient.queryById(subscription.id, resourceGroup.name, workspace.name || '', modelId);
|
||||
} else {
|
||||
throw Error(constants.invalidModelIdError(model.url));
|
||||
}
|
||||
}
|
||||
|
||||
private async getAssetArtifactsDownloadLinks(
|
||||
account: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
resourceGroup: azureResource.AzureResource,
|
||||
workspace: Workspace,
|
||||
model: WorkspaceModel,
|
||||
tenant: any): Promise<string[]> {
|
||||
let options: AzureMachineLearningWorkspacesOptions = {
|
||||
baseUri: this.getBaseUrl(workspace, this._config.amlModelManagementUrl)
|
||||
};
|
||||
const modelManagementClient = await this.getAmlClient(account, subscription, tenant, options, this._config.amlApiVersion);
|
||||
const asset = await this.fetchModelAsset(subscription, resourceGroup, workspace, model, modelManagementClient);
|
||||
options.baseUri = this.getBaseUrl(workspace, this._config.amlExperienceUrl);
|
||||
const experienceClient = await this.getAmlClient(account, subscription, tenant, options, this._config.amlApiVersion);
|
||||
const artifactClient = new Artifacts(experienceClient);
|
||||
let downloadLinks: string[] = [];
|
||||
if (asset && asset.artifacts) {
|
||||
const downloadLinkPromises: Array<Promise<string>> = [];
|
||||
for (const artifact of asset.artifacts) {
|
||||
const parts = artifact.id
|
||||
? this.getPartsFromAssetIdOrPrefix(artifact.id)
|
||||
: this.getPartsFromAssetIdOrPrefix(artifact.prefix);
|
||||
|
||||
if (parts) {
|
||||
const promise = polly()
|
||||
.waitAndRetry(3)
|
||||
.executeForPromise(
|
||||
async (): Promise<string> => {
|
||||
const artifact = await artifactClient.getArtifactContentInformation2(
|
||||
experienceClient.subscriptionId,
|
||||
resourceGroup.name,
|
||||
workspace.name || '',
|
||||
parts.origin,
|
||||
parts.container,
|
||||
{ path: parts.path }
|
||||
);
|
||||
if (artifact) {
|
||||
return artifact.contentUri || '';
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
);
|
||||
downloadLinkPromises.push(promise);
|
||||
}
|
||||
}
|
||||
try {
|
||||
downloadLinks = await Promise.all(downloadLinkPromises);
|
||||
} catch (rejectedPromiseError) {
|
||||
return rejectedPromiseError;
|
||||
}
|
||||
return downloadLinks;
|
||||
|
||||
} else {
|
||||
throw Error(constants.noArtifactError(model.url));
|
||||
}
|
||||
}
|
||||
|
||||
private getPartsFromAssetIdOrPrefix(idOrPrefix: string | undefined): IArtifactParts | undefined {
|
||||
const artifactRegex = /^(.+?)\/(.+?)\/(.+?)$/;
|
||||
if (idOrPrefix) {
|
||||
const parts = artifactRegex.exec(idOrPrefix);
|
||||
if (parts && parts.length === 4) {
|
||||
return {
|
||||
origin: parts[1],
|
||||
container: parts[2],
|
||||
path: parts[3]
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getBaseUrl(workspace: Workspace, server: string): string {
|
||||
let baseUri = `https://${workspace.location}.${server}`;
|
||||
if (workspace.location === 'chinaeast2') {
|
||||
baseUri = `https://${workspace.location}.${server}`;
|
||||
}
|
||||
return baseUri;
|
||||
}
|
||||
|
||||
private getModelClient(amlClient: AzureMachineLearningWorkspaces) {
|
||||
return this._modelClient ?? new WorkspaceModels(amlClient);
|
||||
}
|
||||
|
||||
private async getAmlClient(
|
||||
account: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
tenant: any,
|
||||
options: AzureMachineLearningWorkspacesOptions | undefined = undefined,
|
||||
apiVersion: string | undefined = undefined): Promise<AzureMachineLearningWorkspaces> {
|
||||
if (this._amlClient) {
|
||||
return this._amlClient;
|
||||
} else {
|
||||
const tokens = await this._apiWrapper.getSecurityToken(account, azdata.AzureResource.ResourceManagement);
|
||||
let token: string = '';
|
||||
let tokenType: string | undefined = undefined;
|
||||
if (tokens && tenant.id in tokens) {
|
||||
const tokenForId = tokens[tenant.id];
|
||||
if (tokenForId) {
|
||||
token = tokenForId.token;
|
||||
tokenType = tokenForId.tokenType;
|
||||
}
|
||||
}
|
||||
const client = new AzureMachineLearningWorkspaces(new TokenCredentials(token, tokenType), subscription.id, options);
|
||||
if (apiVersion) {
|
||||
client.apiVersion = apiVersion;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
private getModelId(model: WorkspaceModel): string {
|
||||
const amlAssetRegex = /^aml:\/\/asset\/(.+)$/;
|
||||
const id = model ? amlAssetRegex.exec(model.url || '') : undefined;
|
||||
return id && id.length === 2 ? id[1] : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import * as utils from '../common/utils';
|
||||
import { Config } from '../configurations/config';
|
||||
import { QueryRunner } from '../common/queryRunner';
|
||||
import { ImportedModel, ImportedModelDetails, ModelParameters } from './interfaces';
|
||||
import { ModelPythonClient } from './modelPythonClient';
|
||||
import * as constants from '../common/constants';
|
||||
import * as queries from './queries';
|
||||
import { DatabaseTable } from '../prediction/interfaces';
|
||||
import { ModelConfigRecent } from './modelConfigRecent';
|
||||
|
||||
/**
|
||||
* Service to deployed models
|
||||
*/
|
||||
export class DeployedModelService {
|
||||
|
||||
/**
|
||||
* Creates new instance
|
||||
*/
|
||||
constructor(
|
||||
private _apiWrapper: ApiWrapper,
|
||||
private _config: Config,
|
||||
private _queryRunner: QueryRunner,
|
||||
private _modelClient: ModelPythonClient,
|
||||
private _recentModelService: ModelConfigRecent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns deployed models
|
||||
*/
|
||||
public async getDeployedModels(table: DatabaseTable): Promise<ImportedModel[]> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
let list: ImportedModel[] = [];
|
||||
if (!table.databaseName || !table.tableName || !table.schema) {
|
||||
return [];
|
||||
}
|
||||
if (connection) {
|
||||
const query = queries.getDeployedModelsQuery(table);
|
||||
let result = await this._queryRunner.safeRunQuery(connection, query);
|
||||
if (result && result.rows && result.rows.length > 0) {
|
||||
result.rows.forEach(row => {
|
||||
list.push(this.loadModelData(row, table));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw Error(constants.noConnectionError);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads model
|
||||
* @param model model object
|
||||
*/
|
||||
public async downloadModel(model: ImportedModel): Promise<string> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection) {
|
||||
const query = queries.getModelContentQuery(model);
|
||||
let result = await this._queryRunner.safeRunQuery(connection, query);
|
||||
if (result && result.rows && result.rows.length > 0) {
|
||||
const content = result.rows[0][0].displayValue;
|
||||
return await utils.writeFileFromHex(content);
|
||||
} else {
|
||||
throw Error(constants.invalidModelToSelectError);
|
||||
}
|
||||
} else {
|
||||
throw Error(constants.noConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads model parameters
|
||||
*/
|
||||
public async loadModelParameters(filePath: string): Promise<ModelParameters> {
|
||||
return await this._modelClient.loadModelParameters(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploys local model
|
||||
* @param filePath model file path
|
||||
* @param details model details
|
||||
*/
|
||||
public async deployLocalModel(filePath: string, details: ImportedModelDetails | undefined, table: DatabaseTable) {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection && table.databaseName) {
|
||||
|
||||
await this.configureImport(connection, table);
|
||||
let currentModels = await this.getDeployedModels(table);
|
||||
const content = await utils.readFileInHex(filePath);
|
||||
let modelToAdd: ImportedModel = Object.assign({}, {
|
||||
id: 0,
|
||||
content: content,
|
||||
table: table
|
||||
}, details);
|
||||
await this._queryRunner.runWithDatabaseChange(connection, queries.getInsertModelQuery(modelToAdd, table), table.databaseName);
|
||||
|
||||
let updatedModels = await this.getDeployedModels(table);
|
||||
if (updatedModels.length < currentModels.length + 1) {
|
||||
throw Error(constants.importModelFailedError(details?.modelName, filePath));
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Error(constants.noConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a model
|
||||
*/
|
||||
public async updateModel(model: ImportedModel) {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection && model && model.table && model.table.databaseName) {
|
||||
await this._queryRunner.runWithDatabaseChange(connection, queries.getUpdateModelQuery(model), model.table.databaseName);
|
||||
} else {
|
||||
throw new Error(constants.noConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a model
|
||||
*/
|
||||
public async deleteModel(model: ImportedModel) {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection && model && model.table && model.table.databaseName) {
|
||||
await this._queryRunner.runWithDatabaseChange(connection, queries.getDeleteModelQuery(model), model.table.databaseName);
|
||||
} else {
|
||||
throw new Error(constants.noConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
public async configureImport(connection: azdata.connection.ConnectionProfile, table: DatabaseTable) {
|
||||
if (connection && table.databaseName) {
|
||||
let query = queries.getDatabaseConfigureQuery(table);
|
||||
await this._queryRunner.safeRunQuery(connection, query);
|
||||
|
||||
query = queries.getConfigureTableQuery(table);
|
||||
await this._queryRunner.runWithDatabaseChange(connection, query, table.databaseName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the given table name is valid to be used as import table. If table doesn't exist returns true to create new table
|
||||
* Otherwise verifies the schema and returns true if the schema is supported
|
||||
* @param connection database connection
|
||||
* @param table config table name
|
||||
*/
|
||||
public async verifyConfigTable(table: DatabaseTable): Promise<boolean> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection && table.databaseName) {
|
||||
let databases = await this._apiWrapper.listDatabases(connection.connectionId);
|
||||
|
||||
// If database exist verify the table schema
|
||||
//
|
||||
if ((await databases).find(x => x === table.databaseName)) {
|
||||
const query = queries.getConfigTableVerificationQuery(table);
|
||||
const result = await this._queryRunner.runWithDatabaseChange(connection, query, table.databaseName);
|
||||
return result !== undefined && result.rows.length > 0 && result.rows[0][0].displayValue === '1';
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
throw new Error(constants.noConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
public async getRecentImportTable(): Promise<DatabaseTable> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
let table: DatabaseTable | undefined;
|
||||
if (connection) {
|
||||
table = this._recentModelService.getModelTable(connection);
|
||||
if (!table) {
|
||||
table = {
|
||||
databaseName: connection.databaseName ?? 'master',
|
||||
tableName: this._config.registeredModelTableName,
|
||||
schema: this._config.registeredModelTableSchemaName
|
||||
};
|
||||
}
|
||||
} else {
|
||||
throw new Error(constants.noConnectionError);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
public async storeRecentImportTable(importTable: DatabaseTable): Promise<void> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection) {
|
||||
this._recentModelService.storeModelTable(connection, importTable);
|
||||
} else {
|
||||
throw new Error(constants.noConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
private loadModelData(row: azdata.DbCellValue[], table: DatabaseTable): ImportedModel {
|
||||
return {
|
||||
id: +row[0].displayValue,
|
||||
modelName: row[1].displayValue,
|
||||
description: row[2].displayValue,
|
||||
version: row[3].displayValue,
|
||||
created: row[4].displayValue,
|
||||
framework: row[5].displayValue,
|
||||
frameworkVersion: row[6].displayValue,
|
||||
deploymentTime: row[7].displayValue,
|
||||
deployedBy: row[8].displayValue,
|
||||
runId: row[9].displayValue,
|
||||
table: table
|
||||
};
|
||||
}
|
||||
|
||||
private async getCurrentConnection(): Promise<azdata.connection.ConnectionProfile> {
|
||||
return await this._apiWrapper.getCurrentConnection();
|
||||
}
|
||||
}
|
||||
241
extensions/machine-learning/src/modelManagement/interfaces.ts
Normal file
241
extensions/machine-learning/src/modelManagement/interfaces.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as msRest from '@azure/ms-rest-js';
|
||||
import { Resource } from '@azure/arm-machinelearningservices/esm/models';
|
||||
import { DatabaseTable } from '../prediction/interfaces';
|
||||
|
||||
/**
|
||||
* An interface representing ListWorkspaceModelResult.
|
||||
*/
|
||||
export interface ListWorkspaceModelsResult extends Array<WorkspaceModel> {
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface representing Workspace model
|
||||
*/
|
||||
export interface WorkspaceModel extends Resource {
|
||||
framework?: string;
|
||||
frameworkVersion?: string;
|
||||
createdBy?: string;
|
||||
createdTime?: string;
|
||||
experimentName?: string;
|
||||
outputsSchema?: Array<string>;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface representing Workspace model list response
|
||||
*/
|
||||
export type WorkspacesModelsResponse = ListWorkspaceModelsResult & {
|
||||
/**
|
||||
* The underlying HTTP response.
|
||||
*/
|
||||
_response: msRest.HttpResponse & {
|
||||
/**
|
||||
* The response body as text (string format)
|
||||
*/
|
||||
bodyAsText: string;
|
||||
|
||||
/**
|
||||
* The response body as parsed JSON or XML
|
||||
*/
|
||||
parsedBody: ListWorkspaceModelsResult;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface representing imported model
|
||||
*/
|
||||
export interface ImportedModel extends ImportedModelDetails {
|
||||
id: number;
|
||||
content?: string;
|
||||
table: DatabaseTable;
|
||||
}
|
||||
|
||||
export interface ModelParameter {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ModelParameters {
|
||||
inputs: ModelParameter[],
|
||||
outputs: ModelParameter[]
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface representing imported model
|
||||
*/
|
||||
export interface ImportedModelDetails {
|
||||
modelName: string;
|
||||
created?: string;
|
||||
deploymentTime?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
fileName?: string;
|
||||
framework?: string;
|
||||
frameworkVersion?: string;
|
||||
runId?: string;
|
||||
deployedBy?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Artifact definition.
|
||||
*/
|
||||
export interface ArtifactDetails {
|
||||
/**
|
||||
* The Artifact Id.
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* The Artifact prefix.
|
||||
*/
|
||||
prefix?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* An interface representing Asset.
|
||||
* The Asset definition.
|
||||
*
|
||||
*/
|
||||
export interface Asset {
|
||||
/**
|
||||
* @member {string} [id] The Asset Id.
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* @member {string} [name] The name of the Asset.
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @member {string} [description] The Asset description.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* @member {ArtifactDetails[]} [artifacts] A list of child artifacts.
|
||||
*/
|
||||
artifacts?: ArtifactDetails[];
|
||||
/**
|
||||
* @member {string[]} [tags] The Asset tag list.
|
||||
*/
|
||||
tags?: string[];
|
||||
/**
|
||||
* @member {{ [propertyName: string]: string }} [kvTags] The Asset tag
|
||||
* dictionary. Tags are mutable.
|
||||
*/
|
||||
kvTags?: { [propertyName: string]: string };
|
||||
/**
|
||||
* @member {{ [propertyName: string]: string }} [properties] The Asset
|
||||
* property dictionary. Properties are immutable.
|
||||
*/
|
||||
properties?: { [propertyName: string]: string };
|
||||
/**
|
||||
* @member {string} [runid] The RunId associated with this Asset.
|
||||
*/
|
||||
runid?: string;
|
||||
/**
|
||||
* @member {string} [projectid] The project Id.
|
||||
*/
|
||||
projectid?: string;
|
||||
/**
|
||||
* @member {{ [propertyName: string]: string }} [meta] A dictionary
|
||||
* containing metadata about the Asset.
|
||||
*/
|
||||
meta?: { [propertyName: string]: string };
|
||||
/**
|
||||
* @member {Date} [createdTime] The time the Asset was created in UTC.
|
||||
*/
|
||||
createdTime?: Date;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contains response data for the queryById operation.
|
||||
*/
|
||||
export type AssetsQueryByIdResponse = Asset & {
|
||||
/**
|
||||
* The underlying HTTP response.
|
||||
*/
|
||||
_response: msRest.HttpResponse & {
|
||||
/**
|
||||
* The response body as text (string format)
|
||||
*/
|
||||
bodyAsText: string;
|
||||
/**
|
||||
* The response body as parsed JSON or XML
|
||||
*/
|
||||
parsedBody: Asset;
|
||||
};
|
||||
};
|
||||
|
||||
export interface IArtifactParts {
|
||||
origin: string;
|
||||
container: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* An interface representing ArtifactContentInformationDto.
|
||||
*/
|
||||
export interface ArtifactContentInformationDto {
|
||||
/**
|
||||
* @member {string} [contentUri]
|
||||
*/
|
||||
contentUri?: string;
|
||||
/**
|
||||
* @member {string} [origin]
|
||||
*/
|
||||
origin?: string;
|
||||
/**
|
||||
* @member {string} [container]
|
||||
*/
|
||||
container?: string;
|
||||
/**
|
||||
* @member {string} [path]
|
||||
*/
|
||||
path?: string;
|
||||
}
|
||||
/**
|
||||
* Contains response data for the getArtifactContentInformation2 operation.
|
||||
*/
|
||||
export type GetArtifactContentInformation2Response = ArtifactContentInformationDto & {
|
||||
/**
|
||||
* The underlying HTTP response.
|
||||
*/
|
||||
_response: msRest.HttpResponse & {
|
||||
/**
|
||||
* The response body as text (string format)
|
||||
*/
|
||||
bodyAsText: string;
|
||||
/**
|
||||
* The response body as parsed JSON or XML
|
||||
*/
|
||||
parsedBody: ArtifactContentInformationDto;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @interface
|
||||
* An interface representing ArtifactAPIGetArtifactContentInformation2OptionalParams.
|
||||
* Optional Parameters.
|
||||
*
|
||||
* @extends RequestOptionsBase
|
||||
*/
|
||||
export interface ArtifactAPIGetArtifactContentInformation2OptionalParams extends msRest.RequestOptionsBase {
|
||||
/**
|
||||
* @member {string} [projectName]
|
||||
*/
|
||||
projectName?: string;
|
||||
/**
|
||||
* @member {string} [path]
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* @member {string} [accountName]
|
||||
*/
|
||||
accountName?: string;
|
||||
}
|
||||
|
||||
320
extensions/machine-learning/src/modelManagement/mappers.ts
Normal file
320
extensions/machine-learning/src/modelManagement/mappers.ts
Normal file
@@ -0,0 +1,320 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as msRest from '@azure/ms-rest-js';
|
||||
|
||||
export const Resource: msRest.CompositeMapper = {
|
||||
serializedName: 'Resource',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'Resource',
|
||||
modelProperties: {
|
||||
id: {
|
||||
readOnly: true,
|
||||
serializedName: 'id',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
name: {
|
||||
readOnly: true,
|
||||
serializedName: 'name',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
identity: {
|
||||
readOnly: true,
|
||||
serializedName: 'identity',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'Identity'
|
||||
}
|
||||
},
|
||||
location: {
|
||||
serializedName: 'location',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
type: {
|
||||
readOnly: true,
|
||||
serializedName: 'type',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
tags: {
|
||||
serializedName: 'tags',
|
||||
type: {
|
||||
name: 'Dictionary',
|
||||
value: {
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const ListWorkspaceModelsResult: msRest.CompositeMapper = {
|
||||
serializedName: 'ListWorkspaceModelsResult',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ListWorkspaceModelsResult',
|
||||
modelProperties: {
|
||||
value: {
|
||||
serializedName: '',
|
||||
type: {
|
||||
name: 'Sequence',
|
||||
element: {
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'WorkspaceModel'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
nextLink: {
|
||||
serializedName: 'nextLink',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const WorkspaceModel: msRest.CompositeMapper = {
|
||||
serializedName: 'WorkspaceModel',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'WorkspaceModel',
|
||||
modelProperties: {
|
||||
...Resource.type.modelProperties,
|
||||
framework: {
|
||||
readOnly: true,
|
||||
serializedName: 'framework',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const MachineLearningServiceError: msRest.CompositeMapper = {
|
||||
serializedName: 'MachineLearningServiceError',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'MachineLearningServiceError',
|
||||
modelProperties: {
|
||||
error: {
|
||||
readOnly: true,
|
||||
serializedName: 'error',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ErrorResponse'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const ModelErrorResponse: msRest.CompositeMapper = {
|
||||
serializedName: 'ModelErrorResponse',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ModelErrorResponse',
|
||||
modelProperties: {
|
||||
code: {
|
||||
serializedName: 'code',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
statusCode: {
|
||||
serializedName: 'statusCode',
|
||||
type: {
|
||||
name: 'Number'
|
||||
}
|
||||
},
|
||||
message: {
|
||||
serializedName: 'message',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
details: {
|
||||
serializedName: 'details',
|
||||
type: {
|
||||
name: 'Sequence',
|
||||
element: {
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ErrorDetails'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const ArtifactDetails: msRest.CompositeMapper = {
|
||||
serializedName: 'ArtifactDetails',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ArtifactDetails',
|
||||
modelProperties: {
|
||||
id: {
|
||||
serializedName: 'id',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
prefix: {
|
||||
serializedName: 'prefix',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const Asset: msRest.CompositeMapper = {
|
||||
serializedName: 'Asset',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'Asset',
|
||||
modelProperties: {
|
||||
id: {
|
||||
serializedName: 'id',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
name: {
|
||||
serializedName: 'name',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
description: {
|
||||
serializedName: 'description',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
artifacts: {
|
||||
serializedName: 'artifacts',
|
||||
type: {
|
||||
name: 'Sequence',
|
||||
element: {
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ArtifactDetails'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tags: {
|
||||
serializedName: 'tags',
|
||||
type: {
|
||||
name: 'Sequence',
|
||||
element: {
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
kvTags: {
|
||||
serializedName: 'kvTags',
|
||||
type: {
|
||||
name: 'Dictionary',
|
||||
value: {
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
serializedName: 'properties',
|
||||
type: {
|
||||
name: 'Dictionary',
|
||||
value: {
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
runid: {
|
||||
serializedName: 'runid',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
projectid: {
|
||||
serializedName: 'projectid',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
serializedName: 'meta',
|
||||
type: {
|
||||
name: 'Dictionary',
|
||||
value: {
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
createdTime: {
|
||||
serializedName: 'createdTime',
|
||||
type: {
|
||||
name: 'DateTime'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const ArtifactContentInformationDto: msRest.CompositeMapper = {
|
||||
serializedName: 'ArtifactContentInformationDto',
|
||||
type: {
|
||||
name: 'Composite',
|
||||
className: 'ArtifactContentInformationDto',
|
||||
modelProperties: {
|
||||
contentUri: {
|
||||
serializedName: 'contentUri',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
origin: {
|
||||
serializedName: 'origin',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
container: {
|
||||
serializedName: 'container',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
},
|
||||
path: {
|
||||
serializedName: 'path',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import { DatabaseTable } from '../prediction/interfaces';
|
||||
|
||||
const TableConfigName = 'MLS_ModelTableConfigName';
|
||||
|
||||
export class ModelConfigRecent {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(private _memento: vscode.Memento) {
|
||||
}
|
||||
|
||||
public getModelTable(connection: azdata.connection.ConnectionProfile): DatabaseTable | undefined {
|
||||
return this._memento.get<DatabaseTable>(this.getKey(connection));
|
||||
}
|
||||
|
||||
public storeModelTable(connection: azdata.connection.ConnectionProfile, table: DatabaseTable): void {
|
||||
this._memento.update(this.getKey(connection), table);
|
||||
}
|
||||
|
||||
private getKey(connection: azdata.connection.ConnectionProfile): string {
|
||||
return `${TableConfigName}_${connection.serverName}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ProcessService } from '../common/processService';
|
||||
import { Config } from '../configurations/config';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||
import * as utils from '../common/utils';
|
||||
import { PackageManager } from '../packageManagement/packageManager';
|
||||
import * as constants from '../common/constants';
|
||||
import * as os from 'os';
|
||||
import { ModelParameters } from './interfaces';
|
||||
|
||||
/**
|
||||
* Python client for ONNX models
|
||||
*/
|
||||
export class ModelPythonClient {
|
||||
|
||||
/**
|
||||
* Creates new instance
|
||||
*/
|
||||
constructor(private _outputChannel: vscode.OutputChannel, private _apiWrapper: ApiWrapper, private _processService: ProcessService, private _config: Config, private _packageManager: PackageManager) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploys models in the SQL database using mlflow
|
||||
* @param connection
|
||||
* @param modelPath
|
||||
*/
|
||||
public async deployModel(connection: azdata.connection.ConnectionProfile, modelPath: string): Promise<void> {
|
||||
await this.installDependencies();
|
||||
await this.executeDeployScripts(connection, modelPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs dependencies for python client
|
||||
*/
|
||||
private async installDependencies(): Promise<void> {
|
||||
await utils.executeTasks(this._apiWrapper, constants.installModelMngDependenciesMsgTaskName, [
|
||||
this._packageManager.installRequiredPythonPackages(this._config.modelsRequiredPythonPackages)], true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param modelPath Loads model parameters
|
||||
*/
|
||||
public async loadModelParameters(modelPath: string): Promise<ModelParameters> {
|
||||
await this.installDependencies();
|
||||
return await this.executeModelParametersScripts(modelPath);
|
||||
}
|
||||
|
||||
private async executeModelParametersScripts(modelFolderPath: string): Promise<ModelParameters> {
|
||||
modelFolderPath = utils.makeLinuxPath(modelFolderPath);
|
||||
|
||||
let scripts: string[] = [
|
||||
'import onnx',
|
||||
'import json',
|
||||
`onnx_model_path = '${modelFolderPath}'`,
|
||||
`onnx_model = onnx.load_model(onnx_model_path)`,
|
||||
`type_map = {
|
||||
onnx.TensorProto.DataType.FLOAT: 'real',
|
||||
onnx.TensorProto.DataType.UINT8: 'tinyint',
|
||||
onnx.TensorProto.DataType.INT16: 'smallint',
|
||||
onnx.TensorProto.DataType.INT32: 'int',
|
||||
onnx.TensorProto.DataType.INT64: 'bigint',
|
||||
onnx.TensorProto.DataType.STRING: 'varchar(MAX)',
|
||||
onnx.TensorProto.DataType.DOUBLE: 'float'}`,
|
||||
`parameters = {
|
||||
"inputs": [],
|
||||
"outputs": []
|
||||
}`,
|
||||
`def addParameters(list, paramType):
|
||||
for id, p in enumerate(list):
|
||||
p_type = ''
|
||||
|
||||
if p.type.tensor_type.elem_type in type_map:
|
||||
p_type = type_map[p.type.tensor_type.elem_type]
|
||||
|
||||
parameters[paramType].append({
|
||||
'name': p.name,
|
||||
'type': p_type
|
||||
})`,
|
||||
|
||||
'addParameters(onnx_model.graph.input, "inputs")',
|
||||
'addParameters(onnx_model.graph.output, "outputs")',
|
||||
'print(json.dumps(parameters))'
|
||||
];
|
||||
let pythonExecutable = this._config.pythonExecutable;
|
||||
let output = await this._processService.execScripts(pythonExecutable, scripts, [], undefined);
|
||||
let parametersJson = JSON.parse(output);
|
||||
return Object.assign({}, parametersJson);
|
||||
}
|
||||
|
||||
private async executeDeployScripts(connection: azdata.connection.ConnectionProfile, modelFolderPath: string): Promise<void> {
|
||||
let home = utils.makeLinuxPath(os.homedir());
|
||||
modelFolderPath = utils.makeLinuxPath(modelFolderPath);
|
||||
|
||||
let credentials = await this._apiWrapper.getCredentials(connection.connectionId);
|
||||
|
||||
if (connection) {
|
||||
let server = connection.serverName;
|
||||
|
||||
const experimentId = `ads_ml_experiment_${UUID.generateUuid()}`;
|
||||
const credential = connection.userName ? `${connection.userName}:${credentials[azdata.ConnectionOptionSpecialType.password]}@` : '';
|
||||
let scripts: string[] = [
|
||||
'import mlflow.onnx',
|
||||
`tracking_uri = "file://${home}/mlruns"`,
|
||||
'print(tracking_uri)',
|
||||
'import onnx',
|
||||
'from mlflow.tracking.client import MlflowClient',
|
||||
`onx = onnx.load("${modelFolderPath}")`,
|
||||
`mlflow.set_tracking_uri(tracking_uri)`,
|
||||
'client = MlflowClient()',
|
||||
`exp_name = "${experimentId}"`,
|
||||
`db_uri_artifact = "mssql+pyodbc://${credential}${server}/MlFlowDB?driver=ODBC+Driver+17+for+SQL+Server&"`,
|
||||
'client.create_experiment(exp_name, artifact_location=db_uri_artifact)',
|
||||
'mlflow.set_experiment(exp_name)',
|
||||
'mlflow.onnx.log_model(onx, "pipeline_vectorize")'
|
||||
];
|
||||
let pythonExecutable = this._config.pythonExecutable;
|
||||
await this._processService.execScripts(pythonExecutable, scripts, [], this._outputChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
143
extensions/machine-learning/src/modelManagement/parameters.ts
Normal file
143
extensions/machine-learning/src/modelManagement/parameters.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as msRest from '@azure/ms-rest-js';
|
||||
|
||||
export const subscriptionId: msRest.OperationURLParameter = {
|
||||
parameterPath: 'subscriptionId',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'subscriptionId',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const resourceGroupName: msRest.OperationURLParameter = {
|
||||
parameterPath: 'resourceGroupName',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'resourceGroupName',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const workspaceName: msRest.OperationURLParameter = {
|
||||
parameterPath: 'workspaceName',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'workspaceName',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const workspace: msRest.OperationURLParameter = {
|
||||
parameterPath: 'workspace',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'workspace',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const resourceGroup: msRest.OperationURLParameter = {
|
||||
parameterPath: 'resourceGroup',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'resourceGroup',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const id: msRest.OperationURLParameter = {
|
||||
parameterPath: 'id',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'id',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const acceptLanguage: msRest.OperationParameter = {
|
||||
parameterPath: 'acceptLanguage',
|
||||
mapper: {
|
||||
serializedName: 'accept-language',
|
||||
defaultValue: 'en-US',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const apiVersion: msRest.OperationQueryParameter = {
|
||||
parameterPath: 'apiVersion',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'api-version',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const origin: msRest.OperationURLParameter = {
|
||||
parameterPath: 'origin',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'origin',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const container: msRest.OperationURLParameter = {
|
||||
parameterPath: 'container',
|
||||
mapper: {
|
||||
required: true,
|
||||
serializedName: 'container',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const projectName0: msRest.OperationQueryParameter = {
|
||||
parameterPath: [
|
||||
'options',
|
||||
'projectName'
|
||||
],
|
||||
mapper: {
|
||||
serializedName: 'projectName',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const path1: msRest.OperationQueryParameter = {
|
||||
parameterPath: [
|
||||
'options',
|
||||
'path'
|
||||
],
|
||||
mapper: {
|
||||
serializedName: 'path',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
export const accountName: msRest.OperationQueryParameter = {
|
||||
parameterPath: [
|
||||
'options',
|
||||
'accountName'
|
||||
],
|
||||
mapper: {
|
||||
serializedName: 'accountName',
|
||||
type: {
|
||||
name: 'String'
|
||||
}
|
||||
}
|
||||
};
|
||||
195
extensions/machine-learning/src/modelManagement/queries.ts
Normal file
195
extensions/machine-learning/src/modelManagement/queries.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as utils from '../common/utils';
|
||||
import { DatabaseTable } from '../prediction/interfaces';
|
||||
import { ImportedModel } from './interfaces';
|
||||
|
||||
export function getDatabaseConfigureQuery(configTable: DatabaseTable): string {
|
||||
return `
|
||||
IF NOT EXISTS (
|
||||
SELECT name
|
||||
FROM sys.databases
|
||||
WHERE name = N'${utils.doubleEscapeSingleQuotes(configTable.databaseName)}'
|
||||
)
|
||||
CREATE DATABASE [${utils.doubleEscapeSingleBrackets(configTable.databaseName)}]
|
||||
`;
|
||||
}
|
||||
|
||||
export function getDeployedModelsQuery(table: DatabaseTable): string {
|
||||
return `
|
||||
${selectQuery}
|
||||
FROM ${utils.getRegisteredModelsThreePartsName(table.databaseName || '', table.tableName || '', table.schema || '')}
|
||||
WHERE model_name not like 'MLmodel' and model_name not like 'conda.yaml'
|
||||
ORDER BY model_id
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies config table has the expected schema
|
||||
* @param databaseName
|
||||
* @param tableName
|
||||
*/
|
||||
export function getConfigTableVerificationQuery(table: DatabaseTable): string {
|
||||
let tableName = table.tableName;
|
||||
let schemaName = table.schema;
|
||||
const twoPartTableName = utils.getRegisteredModelsTwoPartsName(table.tableName || '', table.schema || '');
|
||||
|
||||
return `
|
||||
IF NOT EXISTS (
|
||||
SELECT name
|
||||
FROM sys.databases
|
||||
WHERE name = N'${utils.doubleEscapeSingleQuotes(table.databaseName)}'
|
||||
)
|
||||
BEGIN
|
||||
SELECT 1
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
USE [${utils.doubleEscapeSingleBrackets(table.databaseName)}]
|
||||
IF EXISTS
|
||||
( SELECT t.name, s.name
|
||||
FROM sys.tables t join sys.schemas s on t.schema_id=t.schema_id
|
||||
WHERE t.name = '${utils.doubleEscapeSingleQuotes(tableName)}'
|
||||
AND s.name = '${utils.doubleEscapeSingleQuotes(schemaName)}'
|
||||
)
|
||||
BEGIN
|
||||
IF EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_name')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_id')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_description')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_framework')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_framework_version')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_version')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_creation_time')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='model_deployment_time')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='deployed_by')
|
||||
AND EXISTS (SELECT * FROM syscolumns WHERE ID=OBJECT_ID('${twoPartTableName}') AND NAME='run_id')
|
||||
BEGIN
|
||||
SELECT 1
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SELECT 0
|
||||
END
|
||||
END
|
||||
ELSE
|
||||
SELECT 1
|
||||
END
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the import table if doesn't exist
|
||||
*/
|
||||
export function getConfigureTableQuery(table: DatabaseTable): string {
|
||||
let tableName = table.tableName;
|
||||
let schemaName = table.schema;
|
||||
const twoPartTableName = utils.getRegisteredModelsTwoPartsName(table.tableName || '', table.schema || '');
|
||||
|
||||
return `
|
||||
IF NOT EXISTS
|
||||
( SELECT t.name, s.name
|
||||
FROM sys.tables t join sys.schemas s on t.schema_id=t.schema_id
|
||||
WHERE t.name = '${utils.doubleEscapeSingleQuotes(tableName)}'
|
||||
AND s.name = '${utils.doubleEscapeSingleQuotes(schemaName)}'
|
||||
)
|
||||
BEGIN
|
||||
CREATE TABLE ${twoPartTableName}(
|
||||
[model_id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[model_name] [varchar](256) NOT NULL,
|
||||
[model_framework] [varchar](256) NULL,
|
||||
[model_framework_version] [varchar](256) NULL,
|
||||
[model] [varbinary](max) NOT NULL,
|
||||
[model_version] [varchar](256) NULL,
|
||||
[model_creation_time] [datetime2] NULL,
|
||||
[model_deployment_time] [datetime2] NULL,
|
||||
[deployed_by] [int] NULL,
|
||||
[model_description] [varchar](256) NULL,
|
||||
[run_id] [varchar](256) NULL,
|
||||
CONSTRAINT [${utils.doubleEscapeSingleBrackets(tableName)}_models_pk] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[model_id] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
ALTER TABLE ${twoPartTableName} ADD CONSTRAINT [${utils.doubleEscapeSingleBrackets(tableName)}_deployment_time] DEFAULT (getdate()) FOR [model_deployment_time]
|
||||
END
|
||||
`;
|
||||
}
|
||||
|
||||
export function getInsertModelQuery(model: ImportedModel, table: DatabaseTable): string {
|
||||
const twoPartTableName = utils.getRegisteredModelsTwoPartsName(table.tableName || '', table.schema || '');
|
||||
const threePartTableName = utils.getRegisteredModelsThreePartsName(table.databaseName || '', table.tableName || '', table.schema || '');
|
||||
let updateScript = `
|
||||
INSERT INTO ${twoPartTableName}
|
||||
(model_name, model, model_version, model_description, model_creation_time, model_framework, model_framework_version, run_id)
|
||||
VALUES (
|
||||
'${utils.doubleEscapeSingleQuotes(model.modelName || '')}',
|
||||
${utils.doubleEscapeSingleQuotes(model.content || '')},
|
||||
'${utils.doubleEscapeSingleQuotes(model.version || '')}',
|
||||
'${utils.doubleEscapeSingleQuotes(model.description || '')}',
|
||||
'${utils.doubleEscapeSingleQuotes(model.created || '')}',
|
||||
'${utils.doubleEscapeSingleQuotes(model.framework || '')}',
|
||||
'${utils.doubleEscapeSingleQuotes(model.frameworkVersion || '')}',
|
||||
'${utils.doubleEscapeSingleQuotes(model.runId || '')}')
|
||||
`;
|
||||
|
||||
return `
|
||||
${updateScript}
|
||||
${selectQuery}
|
||||
FROM ${threePartTableName}
|
||||
WHERE model_id = SCOPE_IDENTITY();
|
||||
`;
|
||||
}
|
||||
|
||||
export function getModelContentQuery(model: ImportedModel): string {
|
||||
const threePartTableName = utils.getRegisteredModelsThreePartsName(model.table.databaseName || '', model.table.tableName || '', model.table.schema || '');
|
||||
return `
|
||||
SELECT model
|
||||
FROM ${threePartTableName}
|
||||
WHERE model_id = ${model.id};
|
||||
`;
|
||||
}
|
||||
|
||||
export function getUpdateModelQuery(model: ImportedModel): string {
|
||||
const twoPartTableName = utils.getRegisteredModelsTwoPartsName(model.table.tableName || '', model.table.schema || '');
|
||||
const threePartTableName = utils.getRegisteredModelsThreePartsName(model.table.databaseName || '', model.table.tableName || '', model.table.schema || '');
|
||||
let updateScript = `
|
||||
UPDATE ${twoPartTableName}
|
||||
SET
|
||||
model_name = '${utils.doubleEscapeSingleQuotes(model.modelName || '')}',
|
||||
model_version = '${utils.doubleEscapeSingleQuotes(model.version || '')}',
|
||||
model_description = '${utils.doubleEscapeSingleQuotes(model.description || '')}',
|
||||
model_creation_time = '${utils.doubleEscapeSingleQuotes(model.created || '')}',
|
||||
model_framework = '${utils.doubleEscapeSingleQuotes(model.frameworkVersion || '')}',
|
||||
model_framework_version = '${utils.doubleEscapeSingleQuotes(model.frameworkVersion || '')}',
|
||||
run_id = '${utils.doubleEscapeSingleQuotes(model.runId || '')}'
|
||||
WHERE model_id = ${model.id}`;
|
||||
|
||||
return `
|
||||
${updateScript}
|
||||
${selectQuery}
|
||||
FROM ${threePartTableName}
|
||||
WHERE model_id = ${model.id};
|
||||
`;
|
||||
}
|
||||
|
||||
export function getDeleteModelQuery(model: ImportedModel): string {
|
||||
const twoPartTableName = utils.getRegisteredModelsTwoPartsName(model.table.tableName || '', model.table.schema || '');
|
||||
const threePartTableName = utils.getRegisteredModelsThreePartsName(model.table.databaseName || '', model.table.tableName || '', model.table.schema || '');
|
||||
let updateScript = `
|
||||
Delete from ${twoPartTableName}
|
||||
WHERE model_id = ${model.id}`;
|
||||
|
||||
return `
|
||||
${updateScript}
|
||||
${selectQuery}
|
||||
FROM ${threePartTableName}
|
||||
`;
|
||||
}
|
||||
|
||||
export const selectQuery = 'SELECT model_id, model_name, model_description, model_version, model_creation_time, model_framework, model_framework_version, model_deployment_time, deployed_by, run_id';
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as msRest from '@azure/ms-rest-js';
|
||||
import { AzureMachineLearningWorkspacesContext } from '@azure/arm-machinelearningservices';
|
||||
import * as Models from './interfaces';
|
||||
import * as Mappers from './mappers';
|
||||
import * as Parameters from './parameters';
|
||||
|
||||
/**
|
||||
* Workspace models client
|
||||
*/
|
||||
export class WorkspaceModels {
|
||||
private readonly client: AzureMachineLearningWorkspacesContext;
|
||||
|
||||
constructor(client: AzureMachineLearningWorkspacesContext) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
listModels(resourceGroupName: string, workspaceName: string, options?: msRest.RequestOptionsBase): Promise<Models.ListWorkspaceModelsResult>;
|
||||
listModels(resourceGroupName: string, workspaceName: string, callback: msRest.ServiceCallback<Models.ListWorkspaceModelsResult>): void;
|
||||
listModels(resourceGroupName: string, workspaceName: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.ListWorkspaceModelsResult>): void;
|
||||
listModels(resourceGroupName: string, workspaceName: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.ListWorkspaceModelsResult>, callback?: msRest.ServiceCallback<Models.ListWorkspaceModelsResult>): Promise<Models.WorkspacesModelsResponse> {
|
||||
return this.client.sendOperationRequest(
|
||||
{
|
||||
resourceGroupName,
|
||||
workspaceName,
|
||||
options
|
||||
},
|
||||
listModelsOperationSpec,
|
||||
callback) as Promise<Models.WorkspacesModelsResponse>;
|
||||
}
|
||||
}
|
||||
|
||||
const serializer = new msRest.Serializer(Mappers);
|
||||
const listModelsOperationSpec: msRest.OperationSpec = {
|
||||
httpMethod: 'GET',
|
||||
path:
|
||||
'modelmanagement/v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.MachineLearningServices/workspaces/{workspaceName}/models',
|
||||
urlParameters: [
|
||||
Parameters.subscriptionId,
|
||||
Parameters.resourceGroupName,
|
||||
Parameters.workspaceName
|
||||
],
|
||||
queryParameters: [
|
||||
Parameters.apiVersion
|
||||
],
|
||||
headerParameters: [
|
||||
Parameters.acceptLanguage
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
bodyMapper: Mappers.ListWorkspaceModelsResult
|
||||
},
|
||||
default: {
|
||||
bodyMapper: Mappers.MachineLearningServiceError
|
||||
}
|
||||
},
|
||||
serializer
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user