UNPKG

15.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const CLDebug_1 = require("./CLDebug");
5const Request = require("request");
6const constants = require("./constants");
7const requestMethodMap = new Map([
8 ['GET', Request.get],
9 ['PUT', Request.put],
10 ['POST', Request.post],
11 ['DELETE', Request.delete]
12]);
13class CLClient {
14 constructor(options) {
15 this.options = options;
16 if (options.APIM_SUBSCRIPTION_KEY === undefined) {
17 options.APIM_SUBSCRIPTION_KEY = options.LUIS_AUTHORING_KEY;
18 }
19 }
20 ValidationError() {
21 if (typeof this.options.CONVERSATION_LEARNER_SERVICE_URI !== 'string' || this.options.CONVERSATION_LEARNER_SERVICE_URI.length === 0) {
22 return `CONVERSATION_LEARNER_SERVICE_URI must be a non-empty string. You passed: ${this.options.CONVERSATION_LEARNER_SERVICE_URI}`;
23 }
24 if (typeof this.options.LUIS_AUTHORING_KEY !== 'string' || this.options.LUIS_AUTHORING_KEY.length === 0) {
25 return `LUIS_AUTHORING_KEY must be a non-empty string. You passed: ${this.options.LUIS_AUTHORING_KEY}`;
26 }
27 return null;
28 }
29 LuisAuthoringKey() {
30 return this.options.LUIS_AUTHORING_KEY;
31 }
32 BuildURL(baseUri, apiPath, query) {
33 let uri = baseUri + (!baseUri.endsWith('/') ? '/' : '') + apiPath;
34 if (query) {
35 uri += `?${query}`;
36 }
37 return uri;
38 }
39 MakeURL(apiPath, query) {
40 return this.BuildURL(this.options.CONVERSATION_LEARNER_SERVICE_URI, apiPath, query);
41 }
42 MakeSessionURL(apiPath, query) {
43 // check if request is bypassing cognitive services APIM
44 if (!this.options.CONVERSATION_LEARNER_SERVICE_URI.includes('api.cognitive.microsoft.com')) {
45 // In this case we are not chaning the serviceUrl and it stays the same,
46 // for example: https://localhost:37936/api/v1/ -> https://localhost:37936/api/v1/
47 return this.MakeURL(apiPath, query);
48 }
49 // The base uri for session API in cognitive services APIM is in the form of '<service url>/conversationlearner/session/v1.0/'
50 // Session API are the following api:
51 // 1) POST /app/<appId>/session
52 // 2) PUT /app/<appId>/session/extract
53 // 3) PUT /app/<appId>/session/score
54 // 4) DELETE /app/<appId>/session
55 let baseUri = this.options.CONVERSATION_LEARNER_SERVICE_URI.endsWith('/') ?
56 this.options.CONVERSATION_LEARNER_SERVICE_URI :
57 `${this.options.CONVERSATION_LEARNER_SERVICE_URI}/`;
58 const apimVersionSuffix = '/v1.0/';
59 if (baseUri.endsWith(apimVersionSuffix)) {
60 // In this case, serviceurl has api version information in it; "session" will be inserted before /v1.0
61 // this means that https://westus.api.cognitive.microsoft.com/conversationlearner/v1.0/ becomes
62 // https://westus.api.cognitive.microsoft.com/conversationlearner/session/v1.0/
63 baseUri = `${baseUri.substring(0, baseUri.lastIndexOf(apimVersionSuffix))}/session${apimVersionSuffix}`;
64 }
65 else {
66 // When api version information is not part of the serviceUrl, we simply add /session/ to end of the api
67 // example: https://westus.api.cognitive.microsoft.com/conversationlearner/ -> https://westus.api.cognitive.microsoft.com/conversationlearner/session/
68 baseUri += 'session/';
69 }
70 return this.BuildURL(baseUri, apiPath, query);
71 }
72 send(method, url, body) {
73 return new Promise((resolve, reject) => {
74 const requestData = {
75 url,
76 headers: {
77 [constants.luisAuthoringKeyHeader]: this.options.LUIS_AUTHORING_KEY,
78 [constants.luisSubscriptionKeyHeader]: this.options.LUIS_SUBSCRIPTION_KEY,
79 // This is only used when directly targeting service. In future APIM will provide user/subscription id associated from LUIS key
80 [constants.apimSubscriptionIdHeader]: this.options.LUIS_AUTHORING_KEY,
81 [constants.apimSubscriptionKeyHeader]: this.options.APIM_SUBSCRIPTION_KEY
82 },
83 json: true,
84 body
85 };
86 CLDebug_1.CLDebug.LogRequest(method, url, requestData);
87 const requestMethod = requestMethodMap.get(method);
88 if (!requestMethod) {
89 throw new Error(`Request method not found for http verb: ${method}`);
90 }
91 requestMethod(requestData, (error, response, responseBody) => {
92 if (error) {
93 reject(error);
94 }
95 else if (response.statusCode && response.statusCode >= 300) {
96 reject(response);
97 }
98 else {
99 resolve(responseBody);
100 }
101 });
102 });
103 }
104 //==============================================================================
105 // App
106 //=============================================================================
107 /**
108 * Retrieve information about a specific application
109 * If the app ID isn't found in the set of (non-archived) apps,
110 * returns 404 error ("not found")
111 */
112 GetApp(appId) {
113 let apiPath = `app/${appId}`;
114 return this.send('GET', this.MakeURL(apiPath));
115 }
116 GetAppSource(appId, packageId) {
117 let apiPath = `app/${appId}/source?package=${packageId}`;
118 return this.send('GET', this.MakeURL(apiPath));
119 }
120 PostAppSource(appId, appDefinition) {
121 return tslib_1.__awaiter(this, void 0, void 0, function* () {
122 let apiPath = `app/${appId}/source`;
123 yield this.send('POST', this.MakeURL(apiPath), appDefinition);
124 });
125 }
126 /** Retrieve a list of (active) applications */
127 GetApps(query) {
128 let apiPath = `apps`;
129 return this.send('GET', this.MakeURL(apiPath, query));
130 }
131 /** Create a new application */
132 CopyApps(srcUserId, destUserId, appId, luisSubscriptionKey) {
133 const apiPath = `apps/copy?srcUserId=${srcUserId}&destUserId=${destUserId}&appId=${appId}&luisSubscriptionKey=${luisSubscriptionKey}`;
134 return this.send('POST', this.MakeURL(apiPath));
135 }
136 /**
137 * Archive an existing application
138 * Note: "deleting" an application doesn't destroy it, but rather archives
139 * it for a period (eg 30 days). During the archive period, the application
140 * can be restored with the next API call. At the end of the archive period,
141 * the application is destroyed.
142 */
143 ArchiveApp(appId) {
144 let apiPath = `app/${appId}`;
145 return this.send('DELETE', this.MakeURL(apiPath));
146 }
147 /**
148 * Create a new application
149 */
150 // TODO: Fix API to return full object
151 AddApp(app, query) {
152 return tslib_1.__awaiter(this, void 0, void 0, function* () {
153 const apiPath = `app`;
154 // Note: This isn't an actual AppBase, but just { appId, packageId }
155 const appResponse = yield this.send('POST', this.MakeURL(apiPath, query), app);
156 return appResponse.appId;
157 });
158 }
159 /** Creates a new package tag */
160 PublishApp(appId, tagName) {
161 let apiPath = `app/${appId}/publish?version=${tagName}`;
162 return this.send('PUT', this.MakeURL(apiPath));
163 }
164 /** Sets a package tags as the live version */
165 PublishProdPackage(appId, packageId) {
166 let apiPath = `app/${appId}/publish/${packageId}`;
167 return this.send('POST', this.MakeURL(apiPath));
168 }
169 //==============================================================================
170 // Entity
171 //=============================================================================
172 /**
173 * Retrieves definitions of ALL entities in the latest package
174 * (or the specified package, if provided). To retrieve just the IDs
175 * of all entities, see the GetEntityIds method
176 */
177 GetEntities(appId, query) {
178 let apiPath = `app/${appId}/entities`;
179 return this.send('GET', this.MakeURL(apiPath, query));
180 }
181 //=============================================================================
182 // Log Dialogs
183 //=============================================================================
184 /**
185 * Retrieves the contents of many/all logDialogs.
186 * To retrieve just a list of IDs of all logDialogs,
187 * see the GET GetLogDialogIds method.
188 */
189 GetLogDialogs(appId, packageIds) {
190 const packages = packageIds.map(p => `package=${p}`).join("&");
191 const apiPath = `app/${appId}/logdialogs?includeDefinitions=false&${packages}`;
192 return this.send('GET', this.MakeURL(apiPath));
193 }
194 /** Runs entity extraction (prediction). */
195 LogDialogExtract(appId, logDialogId, turnIndex, userInput) {
196 let apiPath = `app/${appId}/logdialog/${logDialogId}/extractor/${turnIndex}`;
197 // Always retrieve entity list
198 let query = 'includeDefinitions=true';
199 return this.send('PUT', this.MakeURL(apiPath, query), userInput);
200 }
201 //=============================================================================
202 // Train Dialogs
203 //=============================================================================
204 /**
205 * Retrieves information about a specific trainDialog in the current package
206 * (or the specified package, if provided)
207 */
208 GetTrainDialog(appId, trainDialogId, includeDefinitions = false) {
209 let query = `includeDefinitions=${includeDefinitions}`;
210 let apiPath = `app/${appId}/traindialog/${trainDialogId}`;
211 return this.send('GET', this.MakeURL(apiPath, query));
212 }
213 /** Runs entity extraction (prediction). */
214 TrainDialogExtract(appId, trainDialogId, turnIndex, userInput) {
215 let apiPath = `app/${appId}/traindialog/${trainDialogId}/extractor/${turnIndex}`;
216 // Always retrieve entity list
217 let query = 'includeDefinitions=true';
218 return this.send('PUT', this.MakeURL(apiPath, query), userInput);
219 }
220 /**
221 * Returns a 409 if text variation conflicts with existing labels, otherwise 200
222 * filteredDialog is dialog to ignore when checking for conflicts
223 */
224 TrainDialogValidateTextVariation(appId, trainDialogId, textVariation, excludeConflictCheckId) {
225 let apiPath = `app/${appId}/traindialog/${trainDialogId}/extractor/textvariation`;
226 // Note: service can take a list of filteredDialogs, but we just use one for now
227 let query = excludeConflictCheckId ? `filteredDialogs=${excludeConflictCheckId}` : undefined;
228 return this.send('POST', this.MakeURL(apiPath, query), textVariation);
229 }
230 //=============================================================================
231 // Session
232 //=============================================================================
233 /** Creates a new session and a corresponding logDialog */
234 StartSession(appId, sessionCreateParams) {
235 let apiPath = `app/${appId}/session`;
236 return this.send('POST', this.MakeSessionURL(apiPath), sessionCreateParams);
237 }
238 /** Gets information about a session */
239 // TODO: move this to session API path next time that the API definition gets updated
240 GetSession(appId, sessionId) {
241 let apiPath = `app/${appId}/session/${sessionId}`;
242 return this.send('GET', this.MakeURL(apiPath));
243 }
244 /** Runs entity extraction (prediction). */
245 SessionExtract(appId, sessionId, userInput) {
246 let apiPath = `app/${appId}/session/${sessionId}/extractor`;
247 // Always retrieve entity list
248 let query = 'includeDefinitions=true';
249 return this.send('PUT', this.MakeSessionURL(apiPath, query), userInput);
250 }
251 /** Take a turn and returns chosen action */
252 SessionScore(appId, sessionId, scorerInput) {
253 let apiPath = `app/${appId}/session/${sessionId}/scorer`;
254 return this.send('PUT', this.MakeSessionURL(apiPath), scorerInput);
255 }
256 SessionLogicResult(appId, sessionId, actionId, actionResult) {
257 let apiPath = `app/${appId}/session/${sessionId}/scorerSteps/action/${actionId}/logicResult`;
258 return this.send('PUT', this.MakeSessionURL(apiPath), { logicResult: actionResult.logicResult });
259 }
260 /** End a session. */
261 EndSession(appId, sessionId) {
262 let apiPath = `app/${appId}/session/${sessionId}`;
263 //TODO: remove this when redundant query parameter is removed
264 let query = 'saveDialog=false';
265 return this.send('DELETE', this.MakeSessionURL(apiPath, query));
266 }
267 //=============================================================================
268 // Teach
269 //=============================================================================
270 /** Creates a new teaching session and a corresponding trainDialog */
271 StartTeach(appId, createTeachParams) {
272 let apiPath = `app/${appId}/teach`;
273 return this.send('POST', this.MakeURL(apiPath), createTeachParams);
274 }
275 /**
276 * Runs entity extraction (prediction).
277 * If a more recent version of the package is available on
278 * the server, the session will first migrate to that newer version. This
279 * doesn't affect the trainDialog maintained.
280 */
281 TeachExtract(appId, teachId, userInput, excludeConflictCheckId) {
282 let apiPath = `app/${appId}/teach/${teachId}/extractor`;
283 // Note: service can take a list of filteredDialogs, but we just use one for now
284 let query = `includeDefinitions=true`;
285 if (excludeConflictCheckId) {
286 query += `&filteredDialogs=${excludeConflictCheckId}`;
287 }
288 return this.send('PUT', this.MakeURL(apiPath, query), { text: userInput.text });
289 }
290 /**
291 * Uploads a labeled entity extraction instance
292 * ie "commits" an entity extraction label, appending it to the teach session's
293 * trainDialog, and advancing the dialog. This may yield produce a new package.
294 */
295 TeachExtractFeedback(appId, teachId, extractorStep) {
296 let apiPath = `app/${appId}/teach/${teachId}/extractor`;
297 return this.send('POST', this.MakeURL(apiPath), extractorStep);
298 }
299 /**
300 * Takes a turn and return distribution over actions.
301 * If a more recent version of the package is
302 * available on the server, the session will first migrate to that newer version.
303 * This doesn't affect the trainDialog maintained by the teaching session.
304 */
305 TeachScore(appId, teachId, scorerInput) {
306 let apiPath = `app/${appId}/teach/${teachId}/scorer`;
307 return this.send('PUT', this.MakeURL(apiPath), scorerInput);
308 }
309 /**
310 * Uploads a labeled scorer step instance
311 * – ie "commits" a scorer label, appending it to the teach session's
312 * trainDialog, and advancing the dialog. This may yield produce a new package.
313 */
314 TeachScoreFeedback(appId, teachId, scorerResponse) {
315 let apiPath = `app/${appId}/teach/${teachId}/scorer`;
316 return this.send('POST', this.MakeURL(apiPath), scorerResponse);
317 }
318 /**
319 * Ends a teach.
320 * For Teach sessions, does NOT delete the associated trainDialog.
321 * To delete the associated trainDialog, call DELETE on the trainDialog.
322 */
323 EndTeach(appId, teachId, save) {
324 const query = `saveDialog=${save}`;
325 let apiPath = `app/${appId}/teach/${teachId}`;
326 return this.send('DELETE', this.MakeURL(apiPath, query));
327 }
328}
329exports.CLClient = CLClient;
330//# sourceMappingURL=CLClient.js.map
\No newline at end of file