@azure/ai-language-text
Version:
An isomorphic client library for the text analysis features in the Azure Cognitive Language Service.
383 lines • 17.3 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { __rest } from "tslib";
import { extractErrorPointerIndex, parseAssessmentIndex, parseHealthcareEntityIndex, sortResponseIdObjects, } from "./util";
import { RestError } from "@azure/core-rest-pipeline";
/**
* Helper function for converting nested service error to the unified
* TextAnalysisError
*/
function toTextAnalysisError(errorModel) {
// Return the deepest error.
if (errorModel.innererror !== undefined) {
return toTextAnalysisError(errorModel.innererror);
}
return Object.assign({}, errorModel);
}
function makeTextAnalysisErrorResult(id, error) {
return {
id,
error: toTextAnalysisError(error),
};
}
/**
* combines successful and erroneous results into a single array of results and
* sort them so that the IDs order match that of the input documents array.
* @param ids - the array of input document IDs.
* @param response - the response received from the service.
* @param options - an options bag that includes functions to process the results.
*/
function transformDocumentResults(ids, response, options) {
const { processError = makeTextAnalysisErrorResult, processSuccess } = options || {};
const successResults = processSuccess
? response.documents.map(processSuccess)
: response.documents;
const unsortedResults = successResults.concat(response.errors.map((error) => processError(error.id, error.error)));
return sortResponseIdObjects(ids, unsortedResults);
}
function toLanguageDetectionResult(docIds, results) {
return transformDocumentResults(docIds, results, {
processSuccess: (_a) => {
var { detectedLanguage } = _a, rest = __rest(_a, ["detectedLanguage"]);
return (Object.assign({ primaryLanguage: detectedLanguage }, rest));
},
});
}
function toPiiEntityRecognitionResult(docIds, results) {
return transformDocumentResults(docIds, results);
}
function toSentimentAnalysisResult(docIds, results) {
return transformDocumentResults(docIds, results, {
processSuccess: (_a) => {
var { sentences } = _a, rest = __rest(_a, ["sentences"]);
return (Object.assign(Object.assign({}, rest), { sentences: sentences.map((sentence) => convertGeneratedSentenceSentiment(sentence, sentences)) }));
},
});
}
/**
* Converts a sentence sentiment object returned by the service to another that
* is user-friendly.
*
* @param sentence - The sentence sentiment object to be converted.
* @param response - The entire response returned by the service.
* @returns The user-friendly sentence sentiment object.
* @internal
*/
function convertGeneratedSentenceSentiment(_a, sentences) {
var _b;
var { targets, assessments: _ } = _a, rest = __rest(_a, ["targets", "assessments"]);
return Object.assign(Object.assign({}, rest), { opinions: (_b = targets === null || targets === void 0 ? void 0 : targets.map(
// eslint-disable-next-line @typescript-eslint/no-shadow
(_a) => {
var { relations } = _a, rest = __rest(_a, ["relations"]);
return ({
target: rest,
assessments: relations
.filter((relation) => relation.relationType === "assessment")
.map((relation) => convertTargetRelationToAssessmentSentiment(relation, sentences)),
});
})) !== null && _b !== void 0 ? _b : [] });
}
/**
* Converts a target relation object returned by the service to an assessment
* sentiment object where JSON pointers in the former are realized in the
* latter.
*
* @param targetRelation - The target relation object to be converted.
* @param response - The entire response returned by the service.
* @returns The user-friendly assessment sentiment object.
* @internal
*/
function convertTargetRelationToAssessmentSentiment(targetRelation, sentences) {
var _a;
const assessmentPtr = targetRelation.ref;
const assessmentIndex = parseAssessmentIndex(assessmentPtr);
const assessment = (_a = sentences === null || sentences === void 0 ? void 0 : sentences[assessmentIndex.sentence].assessments) === null || _a === void 0 ? void 0 : _a[assessmentIndex.assessment];
if (assessment !== undefined) {
return assessment;
}
else {
throw new Error(`Pointer "${assessmentPtr}" is not a valid Assessment pointer`);
}
}
function toEntityLinkingResult(docIds, results) {
return transformDocumentResults(docIds, results);
}
function toKeyPhraseExtractionResult(docIds, results) {
return transformDocumentResults(docIds, results);
}
function toEntityRecognitionResult(docIds, results) {
return transformDocumentResults(docIds, results);
}
/**
* @internal
*/
export function transformActionResult(actionName, docIds, response) {
switch (response.kind) {
case "EntityLinkingResults": {
return toEntityLinkingResult(docIds, response.results);
}
case "EntityRecognitionResults": {
return toEntityRecognitionResult(docIds, response.results);
}
case "KeyPhraseExtractionResults": {
return toKeyPhraseExtractionResult(docIds, response.results);
}
case "PiiEntityRecognitionResults": {
return toPiiEntityRecognitionResult(docIds, response.results);
}
case "SentimentAnalysisResults": {
return toSentimentAnalysisResult(docIds, response.results);
}
case "LanguageDetectionResults": {
return toLanguageDetectionResult(docIds, response.results);
}
default: {
const __exhaust = response;
throw new Error(`Unsupported results kind: ${__exhaust} for an action of type ${actionName}`);
}
}
}
function appendReadableErrorMessage(currentMessage, innerMessage) {
let message = currentMessage;
if (message.slice(-1) !== ".") {
message = message + ".";
}
return message + " " + innerMessage;
}
/**
* @internal
* parses incoming errors from the service/
* @param error - the incoming error
*/
function transformError(errorResponse) {
var _a;
const strongErrorResponse = errorResponse;
if (!strongErrorResponse.response) {
throw errorResponse;
}
const topLevelError = (_a = strongErrorResponse.response.parsedBody) === null || _a === void 0 ? void 0 : _a.error;
if (!topLevelError)
return errorResponse;
let errorMessage = topLevelError.message;
let code = topLevelError.code;
function unwrap(error) {
const innerError = error.innererror;
if (innerError) {
if (innerError.message) {
errorMessage = appendReadableErrorMessage(errorMessage, innerError.message);
}
if (innerError.code) {
code = innerError.code;
}
return unwrap(innerError);
}
return error;
}
unwrap(topLevelError);
return new RestError(errorMessage, {
code,
statusCode: strongErrorResponse.statusCode,
});
}
export async function throwError(p) {
try {
return await p;
}
catch (e) {
throw transformError(e);
}
}
function toHealthcareResult(docIds, results) {
function makeHealthcareEntity(entity) {
const { dataSources } = entity, rest = __rest(entity, ["dataSources"]);
return Object.assign({ dataSources: dataSources !== null && dataSources !== void 0 ? dataSources : [] }, rest);
}
function makeHealthcareRelation(entities) {
return ({ entities: generatedEntities, relationType, confidenceScore, }) => ({
relationType: relationType,
confidenceScore,
roles: generatedEntities.map((role) => ({
entity: entities[parseHealthcareEntityIndex(role.ref)],
name: role.role,
})),
});
}
return transformDocumentResults(docIds, results, {
processSuccess: (_a) => {
var { entities, relations } = _a, rest = __rest(_a, ["entities", "relations"]);
const newEntities = entities.map(makeHealthcareEntity);
return Object.assign({ entities: newEntities, entityRelations: relations.map(makeHealthcareRelation(newEntities)) }, rest);
},
});
}
/**
* @internal
*/
export function transformAnalyzeBatchResults(docIds, response = [], errors = []) {
const errorMap = toIndexErrorMap(errors);
return response.map((actionData, idx) => {
const { lastUpdateDateTime: completedOn, actionName, kind: resultKind } = actionData;
const error = errorMap.get(idx);
switch (resultKind) {
case "SentimentAnalysisLROResults": {
const kind = "SentimentAnalysis";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: toSentimentAnalysisResult(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "EntityRecognitionLROResults": {
const kind = "EntityRecognition";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind: "EntityRecognition", results: toEntityRecognitionResult(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "PiiEntityRecognitionLROResults": {
const kind = "PiiEntityRecognition";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: toPiiEntityRecognitionResult(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "KeyPhraseExtractionLROResults": {
const kind = "KeyPhraseExtraction";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: toKeyPhraseExtractionResult(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "EntityLinkingLROResults": {
const kind = "EntityLinking";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: toEntityLinkingResult(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "HealthcareLROResults": {
const kind = "Healthcare";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: toHealthcareResult(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "CustomEntityRecognitionLROResults": {
const kind = "CustomEntityRecognition";
if (actionData.status === "failed") {
return returnErrorCustomTask(kind, error, completedOn);
}
const { results } = actionData;
const { deploymentName, projectName, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: transformDocumentResults(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { deploymentName,
projectName });
}
case "CustomSingleLabelClassificationLROResults": {
const kind = "CustomSingleLabelClassification";
if (actionData.status === "failed") {
return returnErrorCustomTask(kind, error, completedOn);
}
const { results } = actionData;
const { deploymentName, projectName, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: transformDocumentResults(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { deploymentName,
projectName });
}
case "CustomMultiLabelClassificationLROResults": {
const kind = "CustomMultiLabelClassification";
if (actionData.status === "failed") {
return returnErrorCustomTask(kind, error, completedOn);
}
const { results } = actionData;
const { deploymentName, projectName, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind, results: transformDocumentResults(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { deploymentName,
projectName });
}
case "ExtractiveSummarizationLROResults": {
const kind = "ExtractiveSummarization";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind: "ExtractiveSummarization", results: transformDocumentResults(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
case "AbstractiveSummarizationLROResults": {
const kind = "AbstractiveSummarization";
if (actionData.status === "failed") {
return returnErrorTask(kind, error, completedOn);
}
const { results } = actionData;
const { modelVersion, statistics } = results;
return Object.assign(Object.assign(Object.assign({ kind: "AbstractiveSummarization", results: transformDocumentResults(docIds, results), completedOn }, (actionName ? { actionName } : {})), (statistics ? { statistics } : {})), { modelVersion });
}
default: {
throw new Error(`Unsupported results kind: ${resultKind}`);
}
}
});
}
/**
* @internal
* Transform a list of error into index and error Map
*/
function toIndexErrorMap(errors) {
const errorMap = new Map();
for (const error of errors) {
const position = extractErrorPointerIndex(error);
const { target } = error, errorWithoutTarget = __rest(error, ["target"]);
errorMap.set(position, toTextAnalysisError(errorWithoutTarget));
}
return errorMap;
}
/**
* Return the error for non-custom task
*
* @param kind - non custom task kind
* @param error - error returned from the service
* @param failedOn - the LastUpdateDateTime from the service
* @returns - AnalyzeBatchResult with error
*/
function returnErrorTask(kind, error, failedOn) {
if (!error) {
throw new Error("Unexpected response from service - no errors for missing action results.");
}
return {
kind,
modelVersion: "",
failedOn,
error,
};
}
/**
* Return the error for non-custom task
*
* @param kind - non custom task kind
* @param error - error returned from the service
* @param failedOn - the LastUpdateDateTime from the service
* @returns AnalyzeBatchResult for custom task with error
*/
function returnErrorCustomTask(kind, error, failedOn) {
if (!error) {
throw new Error("Unexpected response from service - no errors for missing action results.");
}
return {
kind,
projectName: "",
deploymentName: "",
failedOn,
error,
};
}
//# sourceMappingURL=transforms.js.map