@aws-amplify/predictions 5.5.6-api-v6-models.b3abc9b.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/lib/Predictions.d.ts +11 -45
- package/lib/Predictions.js +24 -132
- package/lib/errors/PredictionsError.d.ts +4 -0
- package/lib/errors/PredictionsError.js +15 -0
- package/lib/errors/types/validation.d.ts +14 -0
- package/lib/errors/types/validation.js +50 -0
- package/lib/errors/utils/assertValidationError.d.ts +2 -0
- package/lib/errors/utils/assertValidationError.js +14 -0
- package/lib/index.d.ts +2 -4
- package/lib/index.js +2 -9
- package/{lib-esm/Providers → lib/providers}/AmazonAIConvertPredictionsProvider.d.ts +6 -7
- package/lib/providers/AmazonAIConvertPredictionsProvider.js +307 -0
- package/lib/{Providers → providers}/AmazonAIIdentifyPredictionsProvider.d.ts +5 -6
- package/lib/providers/AmazonAIIdentifyPredictionsProvider.js +334 -0
- package/lib/{Providers → providers}/AmazonAIInterpretPredictionsProvider.d.ts +3 -4
- package/lib/providers/AmazonAIInterpretPredictionsProvider.js +208 -0
- package/lib/providers/IdentifyTextUtils.d.ts +16 -0
- package/lib/{Providers → providers}/IdentifyTextUtils.js +97 -148
- package/lib/{Providers → providers}/Utils.d.ts +2 -2
- package/lib/{Providers → providers}/Utils.js +11 -12
- package/lib/{Providers → providers}/index.d.ts +1 -2
- package/lib/providers/index.js +11 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types/AWSTypes.d.ts +8 -9
- package/lib/types/AWSTypes.js +0 -1
- package/lib/types/Predictions.d.ts +63 -59
- package/lib/types/Predictions.js +41 -28
- package/lib/types/index.js +1 -2
- package/lib-esm/Predictions.d.ts +11 -45
- package/lib-esm/Predictions.js +23 -133
- package/lib-esm/errors/PredictionsError.d.ts +4 -0
- package/lib-esm/errors/PredictionsError.js +11 -0
- package/lib-esm/errors/types/validation.d.ts +14 -0
- package/lib-esm/errors/types/validation.js +47 -0
- package/lib-esm/errors/utils/assertValidationError.d.ts +2 -0
- package/lib-esm/errors/utils/assertValidationError.js +10 -0
- package/lib-esm/index.d.ts +2 -4
- package/lib-esm/index.js +1 -5
- package/{lib/Providers → lib-esm/providers}/AmazonAIConvertPredictionsProvider.d.ts +6 -7
- package/lib-esm/providers/AmazonAIConvertPredictionsProvider.js +303 -0
- package/lib-esm/{Providers → providers}/AmazonAIIdentifyPredictionsProvider.d.ts +5 -6
- package/lib-esm/providers/AmazonAIIdentifyPredictionsProvider.js +330 -0
- package/lib-esm/{Providers → providers}/AmazonAIInterpretPredictionsProvider.d.ts +3 -4
- package/lib-esm/providers/AmazonAIInterpretPredictionsProvider.js +204 -0
- package/lib-esm/providers/IdentifyTextUtils.d.ts +16 -0
- package/lib-esm/providers/IdentifyTextUtils.js +264 -0
- package/lib-esm/{Providers → providers}/Utils.d.ts +2 -2
- package/lib-esm/{Providers → providers}/Utils.js +10 -12
- package/lib-esm/{Providers → providers}/index.d.ts +1 -2
- package/lib-esm/{Providers → providers}/index.js +1 -3
- package/lib-esm/tsconfig.tsbuildinfo +1 -0
- package/lib-esm/types/AWSTypes.d.ts +8 -9
- package/lib-esm/types/AWSTypes.js +1 -1
- package/lib-esm/types/Predictions.d.ts +63 -59
- package/lib-esm/types/Predictions.js +34 -27
- package/lib-esm/types/index.js +0 -1
- package/package.json +39 -48
- package/src/Predictions.ts +33 -220
- package/src/errors/PredictionsError.ts +16 -0
- package/src/errors/types/validation.ts +51 -0
- package/src/errors/utils/assertValidationError.ts +18 -0
- package/src/index.ts +16 -16
- package/src/{Providers → providers}/AmazonAIConvertPredictionsProvider.ts +210 -171
- package/src/{Providers → providers}/AmazonAIIdentifyPredictionsProvider.ts +244 -246
- package/src/providers/AmazonAIInterpretPredictionsProvider.ts +293 -0
- package/src/{Providers → providers}/IdentifyTextUtils.ts +69 -52
- package/src/{Providers → providers}/Utils.ts +2 -2
- package/src/{Providers → providers}/index.ts +0 -2
- package/src/types/AWSTypes.ts +5 -6
- package/src/types/Predictions.ts +107 -62
- package/lib/.tsbuildinfo +0 -3
- package/lib/Predictions.js.map +0 -1
- package/lib/Providers/AmazonAIConvertPredictionsProvider.js +0 -377
- package/lib/Providers/AmazonAIConvertPredictionsProvider.js.map +0 -1
- package/lib/Providers/AmazonAIIdentifyPredictionsProvider.js +0 -441
- package/lib/Providers/AmazonAIIdentifyPredictionsProvider.js.map +0 -1
- package/lib/Providers/AmazonAIInterpretPredictionsProvider.js +0 -321
- package/lib/Providers/AmazonAIInterpretPredictionsProvider.js.map +0 -1
- package/lib/Providers/AmazonAIPredictionsProvider.d.ts +0 -14
- package/lib/Providers/AmazonAIPredictionsProvider.js +0 -43
- package/lib/Providers/AmazonAIPredictionsProvider.js.map +0 -1
- package/lib/Providers/IdentifyTextUtils.d.ts +0 -40
- package/lib/Providers/IdentifyTextUtils.js.map +0 -1
- package/lib/Providers/Utils.js.map +0 -1
- package/lib/Providers/index.js +0 -13
- package/lib/Providers/index.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types/AWSTypes.js.map +0 -1
- package/lib/types/Predictions.js.map +0 -1
- package/lib/types/Providers/AbstractConvertPredictionsProvider.d.ts +0 -9
- package/lib/types/Providers/AbstractConvertPredictionsProvider.js +0 -44
- package/lib/types/Providers/AbstractConvertPredictionsProvider.js.map +0 -1
- package/lib/types/Providers/AbstractIdentifyPredictionsProvider.d.ts +0 -9
- package/lib/types/Providers/AbstractIdentifyPredictionsProvider.js +0 -44
- package/lib/types/Providers/AbstractIdentifyPredictionsProvider.js.map +0 -1
- package/lib/types/Providers/AbstractInterpretPredictionsProvider.d.ts +0 -7
- package/lib/types/Providers/AbstractInterpretPredictionsProvider.js +0 -27
- package/lib/types/Providers/AbstractInterpretPredictionsProvider.js.map +0 -1
- package/lib/types/Providers/AbstractPredictionsProvider.d.ts +0 -7
- package/lib/types/Providers/AbstractPredictionsProvider.js +0 -16
- package/lib/types/Providers/AbstractPredictionsProvider.js.map +0 -1
- package/lib/types/Providers/index.d.ts +0 -4
- package/lib/types/Providers/index.js +0 -10
- package/lib/types/Providers/index.js.map +0 -1
- package/lib/types/index.js.map +0 -1
- package/lib-esm/.tsbuildinfo +0 -3
- package/lib-esm/Predictions.js.map +0 -1
- package/lib-esm/Providers/AmazonAIConvertPredictionsProvider.js +0 -375
- package/lib-esm/Providers/AmazonAIConvertPredictionsProvider.js.map +0 -1
- package/lib-esm/Providers/AmazonAIIdentifyPredictionsProvider.js +0 -439
- package/lib-esm/Providers/AmazonAIIdentifyPredictionsProvider.js.map +0 -1
- package/lib-esm/Providers/AmazonAIInterpretPredictionsProvider.js +0 -319
- package/lib-esm/Providers/AmazonAIInterpretPredictionsProvider.js.map +0 -1
- package/lib-esm/Providers/AmazonAIPredictionsProvider.d.ts +0 -14
- package/lib-esm/Providers/AmazonAIPredictionsProvider.js +0 -41
- package/lib-esm/Providers/AmazonAIPredictionsProvider.js.map +0 -1
- package/lib-esm/Providers/IdentifyTextUtils.d.ts +0 -40
- package/lib-esm/Providers/IdentifyTextUtils.js +0 -313
- package/lib-esm/Providers/IdentifyTextUtils.js.map +0 -1
- package/lib-esm/Providers/Utils.js.map +0 -1
- package/lib-esm/Providers/index.js.map +0 -1
- package/lib-esm/index.js.map +0 -1
- package/lib-esm/types/AWSTypes.js.map +0 -1
- package/lib-esm/types/Predictions.js.map +0 -1
- package/lib-esm/types/Providers/AbstractConvertPredictionsProvider.d.ts +0 -9
- package/lib-esm/types/Providers/AbstractConvertPredictionsProvider.js +0 -42
- package/lib-esm/types/Providers/AbstractConvertPredictionsProvider.js.map +0 -1
- package/lib-esm/types/Providers/AbstractIdentifyPredictionsProvider.d.ts +0 -9
- package/lib-esm/types/Providers/AbstractIdentifyPredictionsProvider.js +0 -42
- package/lib-esm/types/Providers/AbstractIdentifyPredictionsProvider.js.map +0 -1
- package/lib-esm/types/Providers/AbstractInterpretPredictionsProvider.d.ts +0 -7
- package/lib-esm/types/Providers/AbstractInterpretPredictionsProvider.js +0 -25
- package/lib-esm/types/Providers/AbstractInterpretPredictionsProvider.js.map +0 -1
- package/lib-esm/types/Providers/AbstractPredictionsProvider.d.ts +0 -7
- package/lib-esm/types/Providers/AbstractPredictionsProvider.js +0 -14
- package/lib-esm/types/Providers/AbstractPredictionsProvider.js.map +0 -1
- package/lib-esm/types/Providers/index.d.ts +0 -4
- package/lib-esm/types/Providers/index.js +0 -7
- package/lib-esm/types/Providers/index.js.map +0 -1
- package/lib-esm/types/index.js.map +0 -1
- package/src/Providers/AmazonAIInterpretPredictionsProvider.ts +0 -281
- package/src/Providers/AmazonAIPredictionsProvider.ts +0 -69
- package/src/types/Providers/AbstractConvertPredictionsProvider.ts +0 -55
- package/src/types/Providers/AbstractIdentifyPredictionsProvider.ts +0 -57
- package/src/types/Providers/AbstractInterpretPredictionsProvider.ts +0 -26
- package/src/types/Providers/AbstractPredictionsProvider.ts +0 -20
- package/src/types/Providers/index.ts +0 -6
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Amplify, ConsoleLogger as Logger, fetchAuthSession, } from '@aws-amplify/core';
|
|
4
|
+
import { Category, PredictionsAction, getAmplifyUserAgentObject, } from '@aws-amplify/core/internals/utils';
|
|
5
|
+
import { getUrl } from '@aws-amplify/storage';
|
|
6
|
+
import { DetectFacesCommand, DetectLabelsCommand, DetectModerationLabelsCommand, DetectTextCommand, RecognizeCelebritiesCommand, RekognitionClient, SearchFacesByImageCommand, } from '@aws-sdk/client-rekognition';
|
|
7
|
+
import { AnalyzeDocumentCommand, DetectDocumentTextCommand, TextractClient, } from '@aws-sdk/client-textract';
|
|
8
|
+
import { PredictionsValidationErrorCode } from '../errors/types/validation';
|
|
9
|
+
import { assertValidationError } from '../errors/utils/assertValidationError';
|
|
10
|
+
import { isFileSource, isIdentifyBytesSource, isIdentifyCelebrities, isIdentifyFromCollection, isIdentifyLabelsInput, isIdentifyTextInput, isStorageSource, isValidIdentifyInput, } from '../types';
|
|
11
|
+
import { categorizeRekognitionBlocks, categorizeTextractBlocks, } from './IdentifyTextUtils';
|
|
12
|
+
import { blobToArrayBuffer, makeCamelCase, makeCamelCaseArray } from './Utils';
|
|
13
|
+
const logger = new Logger('AmazonAIIdentifyPredictionsProvider');
|
|
14
|
+
export class AmazonAIIdentifyPredictionsProvider {
|
|
15
|
+
getProviderName() {
|
|
16
|
+
return 'AmazonAIIdentifyPredictionsProvider';
|
|
17
|
+
}
|
|
18
|
+
identify(input) {
|
|
19
|
+
assertValidationError(isValidIdentifyInput(input), PredictionsValidationErrorCode.InvalidInput);
|
|
20
|
+
if (isIdentifyTextInput(input)) {
|
|
21
|
+
logger.debug('identifyText');
|
|
22
|
+
return this.identifyText(input);
|
|
23
|
+
}
|
|
24
|
+
else if (isIdentifyLabelsInput(input)) {
|
|
25
|
+
logger.debug('identifyLabels');
|
|
26
|
+
return this.identifyLabels(input);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
logger.debug('identifyEntities');
|
|
30
|
+
return this.identifyEntities(input);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Verify user input source and converts it into source object readable by Rekognition and Textract.
|
|
35
|
+
* Note that Rekognition and Textract use the same source interface, so we need not worry about types.
|
|
36
|
+
* @param {IdentifySource} source - User input source that directs to the object user wants
|
|
37
|
+
* to identify (storage, file, or bytes).
|
|
38
|
+
* @return {Promise<Image>} - Promise resolving to the converted source object.
|
|
39
|
+
*/
|
|
40
|
+
configureSource(source) {
|
|
41
|
+
return new Promise((res, rej) => {
|
|
42
|
+
if (isStorageSource(source)) {
|
|
43
|
+
const storageConfig = {
|
|
44
|
+
accessLevel: source.level,
|
|
45
|
+
targetIdentityId: source.identityId,
|
|
46
|
+
};
|
|
47
|
+
getUrl({ key: source.key, options: storageConfig })
|
|
48
|
+
.then(value => {
|
|
49
|
+
const parser = /https:\/\/([a-zA-Z0-9%\-_.]+)\.s3\.[A-Za-z0-9%\-._~]+\/([a-zA-Z0-9%\-._~/]+)\?/;
|
|
50
|
+
const parsedURL = value.url.toString().match(parser) ?? '';
|
|
51
|
+
if (parsedURL.length < 3)
|
|
52
|
+
rej('Invalid S3 key was given.');
|
|
53
|
+
res({
|
|
54
|
+
S3Object: {
|
|
55
|
+
Bucket: parsedURL[1],
|
|
56
|
+
Name: decodeURIComponent(parsedURL[2]),
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
})
|
|
60
|
+
.catch(err => rej(err));
|
|
61
|
+
}
|
|
62
|
+
else if (isFileSource(source)) {
|
|
63
|
+
blobToArrayBuffer(source.file)
|
|
64
|
+
.then(buffer => {
|
|
65
|
+
res({ Bytes: new Uint8Array(buffer) });
|
|
66
|
+
})
|
|
67
|
+
.catch(err => rej(err));
|
|
68
|
+
}
|
|
69
|
+
else if (isIdentifyBytesSource(source)) {
|
|
70
|
+
const bytes = source.bytes;
|
|
71
|
+
if (bytes instanceof Blob) {
|
|
72
|
+
blobToArrayBuffer(bytes)
|
|
73
|
+
.then(buffer => {
|
|
74
|
+
res({ Bytes: new Uint8Array(buffer) });
|
|
75
|
+
})
|
|
76
|
+
.catch(err => rej(err));
|
|
77
|
+
}
|
|
78
|
+
if (bytes instanceof ArrayBuffer || bytes instanceof Buffer) {
|
|
79
|
+
res({ Bytes: new Uint8Array(bytes) });
|
|
80
|
+
}
|
|
81
|
+
// everything else can be directly passed to Rekognition / Textract.
|
|
82
|
+
res({ Bytes: bytes });
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
rej('Input source is not configured correctly.');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Recognize text from real-world images and documents (plain text, forms and tables). Detects text in the input
|
|
91
|
+
* image and converts it into machine-readable text.
|
|
92
|
+
* @param {IdentifySource} source - Object containing the source image and feature types to analyze.
|
|
93
|
+
* @return {Promise<IdentifyTextOutput>} - Promise resolving to object containing identified texts.
|
|
94
|
+
*/
|
|
95
|
+
async identifyText(input) {
|
|
96
|
+
const { credentials } = await fetchAuthSession();
|
|
97
|
+
assertValidationError(!!credentials, PredictionsValidationErrorCode.NoCredentials);
|
|
98
|
+
const { identifyText = {} } = Amplify.getConfig().Predictions?.identify ?? {};
|
|
99
|
+
const { region = '', defaults = {} } = identifyText;
|
|
100
|
+
const { format: configFormat = 'PLAIN' } = defaults;
|
|
101
|
+
this.rekognitionClient = new RekognitionClient({
|
|
102
|
+
region,
|
|
103
|
+
credentials,
|
|
104
|
+
customUserAgent: _getPredictionsIdentifyAmplifyUserAgent(),
|
|
105
|
+
});
|
|
106
|
+
this.textractClient = new TextractClient({
|
|
107
|
+
region,
|
|
108
|
+
credentials,
|
|
109
|
+
customUserAgent: _getPredictionsIdentifyAmplifyUserAgent(),
|
|
110
|
+
});
|
|
111
|
+
let inputDocument;
|
|
112
|
+
inputDocument = await this.configureSource(input.text?.source);
|
|
113
|
+
// get default value if format isn't specified in the input.
|
|
114
|
+
const format = input.text?.format ?? configFormat;
|
|
115
|
+
const featureTypes = []; // structures we want to analyze (e.g. [TABLES, FORMS]).
|
|
116
|
+
if (format === 'FORM' || format === 'ALL')
|
|
117
|
+
featureTypes.push('FORMS');
|
|
118
|
+
if (format === 'TABLE' || format === 'ALL')
|
|
119
|
+
featureTypes.push('TABLES');
|
|
120
|
+
if (featureTypes.length === 0) {
|
|
121
|
+
/**
|
|
122
|
+
* Empty featureTypes indicates that we will identify plain text. We will use rekognition (suitable
|
|
123
|
+
* for everyday images but has 50 word limit) first and see if reaches its word limit. If it does, then
|
|
124
|
+
* we call textract and use the data that identify more words.
|
|
125
|
+
*/
|
|
126
|
+
const textractParam = {
|
|
127
|
+
Document: inputDocument,
|
|
128
|
+
};
|
|
129
|
+
const rekognitionParam = {
|
|
130
|
+
Image: inputDocument,
|
|
131
|
+
};
|
|
132
|
+
const detectTextCommand = new DetectTextCommand(rekognitionParam);
|
|
133
|
+
const rekognitionData = await this.rekognitionClient.send(detectTextCommand);
|
|
134
|
+
const rekognitionResponse = categorizeRekognitionBlocks(rekognitionData.TextDetections);
|
|
135
|
+
if (rekognitionResponse.text.words.length < 50) {
|
|
136
|
+
// did not hit the word limit, return the data
|
|
137
|
+
return rekognitionResponse;
|
|
138
|
+
}
|
|
139
|
+
const detectDocumentTextCommand = new DetectDocumentTextCommand(textractParam);
|
|
140
|
+
const { Blocks } = await this.textractClient.send(detectDocumentTextCommand);
|
|
141
|
+
if ((rekognitionData.TextDetections?.length ?? 0) > (Blocks?.length ?? 0)) {
|
|
142
|
+
return rekognitionResponse;
|
|
143
|
+
}
|
|
144
|
+
return categorizeTextractBlocks(Blocks);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const param = {
|
|
148
|
+
Document: inputDocument,
|
|
149
|
+
FeatureTypes: featureTypes,
|
|
150
|
+
};
|
|
151
|
+
const analyzeDocumentCommand = new AnalyzeDocumentCommand(param);
|
|
152
|
+
const { Blocks } = await this.textractClient.send(analyzeDocumentCommand);
|
|
153
|
+
return categorizeTextractBlocks(Blocks);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Identify instances of real world entities from an image and if it contains unsafe content.
|
|
158
|
+
* @param {IdentifyLabelsInput} input - Object containing the source image and entity type to identify.
|
|
159
|
+
* @return {Promise<IdentifyLabelsOutput>} - Promise resolving to an array of identified entities.
|
|
160
|
+
*/
|
|
161
|
+
async identifyLabels(input) {
|
|
162
|
+
const { credentials } = await fetchAuthSession();
|
|
163
|
+
assertValidationError(!!credentials, PredictionsValidationErrorCode.NoCredentials);
|
|
164
|
+
const { identifyLabels = {} } = Amplify.getConfig().Predictions?.identify ?? {};
|
|
165
|
+
const { region = '', defaults = {} } = identifyLabels;
|
|
166
|
+
const { type = 'LABELS' } = defaults;
|
|
167
|
+
this.rekognitionClient = new RekognitionClient({
|
|
168
|
+
region,
|
|
169
|
+
credentials,
|
|
170
|
+
customUserAgent: _getPredictionsIdentifyAmplifyUserAgent(),
|
|
171
|
+
});
|
|
172
|
+
const inputImage = await this.configureSource(input.labels?.source);
|
|
173
|
+
const param = { Image: inputImage };
|
|
174
|
+
const servicePromises = [];
|
|
175
|
+
// get default argument
|
|
176
|
+
const entityType = input.labels?.type ?? type;
|
|
177
|
+
if (entityType === 'LABELS' || entityType === 'ALL') {
|
|
178
|
+
servicePromises.push(this.detectLabels(param));
|
|
179
|
+
}
|
|
180
|
+
if (entityType === 'UNSAFE' || entityType === 'ALL') {
|
|
181
|
+
servicePromises.push(this.detectModerationLabels(param));
|
|
182
|
+
}
|
|
183
|
+
return Promise.all(servicePromises).then(data => {
|
|
184
|
+
let identifyResult = {};
|
|
185
|
+
// concatenate resolved promises to a single object
|
|
186
|
+
data.forEach(val => {
|
|
187
|
+
identifyResult = { ...identifyResult, ...val };
|
|
188
|
+
});
|
|
189
|
+
return identifyResult;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Calls Rekognition.detectLabels and organizes the returned data.
|
|
194
|
+
* @param {DetectLabelsInput} param - parameter to be passed onto Rekognition
|
|
195
|
+
* @return {Promise<IdentifyLabelsOutput>} - Promise resolving to organized detectLabels response.
|
|
196
|
+
*/
|
|
197
|
+
async detectLabels(param) {
|
|
198
|
+
const detectLabelsCommand = new DetectLabelsCommand(param);
|
|
199
|
+
const data = await this.rekognitionClient.send(detectLabelsCommand);
|
|
200
|
+
if (!data.Labels)
|
|
201
|
+
return {}; // no image was detected
|
|
202
|
+
const detectLabelData = data.Labels.map(label => {
|
|
203
|
+
const boxes = label.Instances?.map(instance => makeCamelCase(instance.BoundingBox)) || [];
|
|
204
|
+
return {
|
|
205
|
+
name: label.Name,
|
|
206
|
+
boundingBoxes: boxes,
|
|
207
|
+
metadata: {
|
|
208
|
+
confidence: label.Confidence,
|
|
209
|
+
parents: makeCamelCaseArray(label.Parents),
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
return { labels: detectLabelData };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Calls Rekognition.detectModerationLabels and organizes the returned data.
|
|
217
|
+
* @param {Rekognition.DetectLabelsRequest} param - Parameter to be passed onto Rekognition
|
|
218
|
+
* @return {Promise<IdentifyLabelsOutput>} - Promise resolving to organized detectModerationLabels response.
|
|
219
|
+
*/
|
|
220
|
+
async detectModerationLabels(param) {
|
|
221
|
+
const detectModerationLabelsCommand = new DetectModerationLabelsCommand(param);
|
|
222
|
+
const data = await this.rekognitionClient.send(detectModerationLabelsCommand);
|
|
223
|
+
if (data.ModerationLabels?.length !== 0) {
|
|
224
|
+
return { unsafe: 'YES' };
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return { unsafe: 'NO' };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Identify faces within an image that is provided as input, and match faces from a collection
|
|
232
|
+
* or identify celebrities.
|
|
233
|
+
* @param {IdentifyEntityInput} input - object containing the source image and face match options.
|
|
234
|
+
* @return {Promise<IdentifyEntityOutput>} Promise resolving to identify results.
|
|
235
|
+
*/
|
|
236
|
+
async identifyEntities(input) {
|
|
237
|
+
const { credentials } = await fetchAuthSession();
|
|
238
|
+
assertValidationError(!!credentials, PredictionsValidationErrorCode.NoCredentials);
|
|
239
|
+
const { identifyEntities = {} } = Amplify.getConfig().Predictions?.identify ?? {};
|
|
240
|
+
const { region = '', celebrityDetectionEnabled = false, defaults = {}, } = identifyEntities;
|
|
241
|
+
const { collectionId: collectionIdConfig = '', maxEntities: maxFacesConfig = 50, } = defaults;
|
|
242
|
+
// default arguments
|
|
243
|
+
this.rekognitionClient = new RekognitionClient({
|
|
244
|
+
region,
|
|
245
|
+
credentials,
|
|
246
|
+
customUserAgent: _getPredictionsIdentifyAmplifyUserAgent(),
|
|
247
|
+
});
|
|
248
|
+
const inputImage = await this.configureSource(input.entities?.source);
|
|
249
|
+
const param = { Attributes: ['ALL'], Image: inputImage };
|
|
250
|
+
if (isIdentifyCelebrities(input.entities) &&
|
|
251
|
+
input.entities.celebrityDetection) {
|
|
252
|
+
assertValidationError(celebrityDetectionEnabled, PredictionsValidationErrorCode.CelebrityDetectionNotEnabled);
|
|
253
|
+
const recognizeCelebritiesCommand = new RecognizeCelebritiesCommand(param);
|
|
254
|
+
const data = await this.rekognitionClient.send(recognizeCelebritiesCommand);
|
|
255
|
+
const faces = data.CelebrityFaces?.map(celebrity => ({
|
|
256
|
+
boundingBox: makeCamelCase(celebrity.Face?.BoundingBox),
|
|
257
|
+
landmarks: makeCamelCaseArray(celebrity.Face?.Landmarks),
|
|
258
|
+
metadata: {
|
|
259
|
+
...makeCamelCase(celebrity, ['Id', 'Name', 'Urls']),
|
|
260
|
+
pose: makeCamelCase(celebrity.Face?.Pose),
|
|
261
|
+
},
|
|
262
|
+
})) ?? [];
|
|
263
|
+
return { entities: faces };
|
|
264
|
+
}
|
|
265
|
+
else if (isIdentifyFromCollection(input.entities) &&
|
|
266
|
+
input.entities.collection) {
|
|
267
|
+
const { collectionId = collectionIdConfig, maxEntities: maxFaces = maxFacesConfig, } = input.entities;
|
|
268
|
+
// Concatenate additional parameters
|
|
269
|
+
const updatedParam = {
|
|
270
|
+
...param,
|
|
271
|
+
CollectionId: collectionId,
|
|
272
|
+
MaxFaces: maxFaces,
|
|
273
|
+
};
|
|
274
|
+
const searchFacesByImageCommand = new SearchFacesByImageCommand(updatedParam);
|
|
275
|
+
const data = await this.rekognitionClient.send(searchFacesByImageCommand);
|
|
276
|
+
const faces = data.FaceMatches?.map(match => {
|
|
277
|
+
const externalImageId = match.Face?.ExternalImageId
|
|
278
|
+
? this.decodeExternalImageId(match.Face.ExternalImageId)
|
|
279
|
+
: undefined;
|
|
280
|
+
return {
|
|
281
|
+
boundingBox: makeCamelCase(match.Face?.BoundingBox),
|
|
282
|
+
metadata: {
|
|
283
|
+
externalImageId,
|
|
284
|
+
similarity: match.Similarity,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}) ?? [];
|
|
288
|
+
return { entities: faces };
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
const detectFacesCommand = new DetectFacesCommand(param);
|
|
292
|
+
const data = await this.rekognitionClient.send(detectFacesCommand);
|
|
293
|
+
const faces = data.FaceDetails?.map(detail => {
|
|
294
|
+
// face attributes keys we want to extract from Rekognition's response
|
|
295
|
+
const attributeKeys = [
|
|
296
|
+
'Smile',
|
|
297
|
+
'Eyeglasses',
|
|
298
|
+
'Sunglasses',
|
|
299
|
+
'Gender',
|
|
300
|
+
'Beard',
|
|
301
|
+
'Mustache',
|
|
302
|
+
'EyesOpen',
|
|
303
|
+
'MouthOpen',
|
|
304
|
+
];
|
|
305
|
+
const faceAttributes = makeCamelCase(detail, attributeKeys);
|
|
306
|
+
faceAttributes.emotions = detail.Emotions?.map(emotion => emotion.Type);
|
|
307
|
+
return {
|
|
308
|
+
boundingBox: makeCamelCase(detail.BoundingBox),
|
|
309
|
+
landmarks: makeCamelCaseArray(detail.Landmarks),
|
|
310
|
+
ageRange: makeCamelCase(detail.AgeRange),
|
|
311
|
+
attributes: faceAttributes,
|
|
312
|
+
metadata: {
|
|
313
|
+
confidence: detail.Confidence,
|
|
314
|
+
pose: makeCamelCase(detail.Pose),
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}) ?? [];
|
|
318
|
+
return { entities: faces };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
decodeExternalImageId(externalImageId) {
|
|
322
|
+
return ('' + externalImageId).replace(/::/g, '/');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function _getPredictionsIdentifyAmplifyUserAgent() {
|
|
326
|
+
return getAmplifyUserAgentObject({
|
|
327
|
+
category: Category.Predictions,
|
|
328
|
+
action: PredictionsAction.Identify,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { AbstractInterpretPredictionsProvider } from '../types/Providers';
|
|
2
1
|
import { InterpretTextInput, InterpretTextOutput } from '../types';
|
|
3
|
-
export declare class AmazonAIInterpretPredictionsProvider
|
|
4
|
-
private comprehendClient
|
|
5
|
-
constructor();
|
|
2
|
+
export declare class AmazonAIInterpretPredictionsProvider {
|
|
3
|
+
private comprehendClient?;
|
|
6
4
|
getProviderName(): string;
|
|
5
|
+
interpret(input: InterpretTextInput): Promise<InterpretTextOutput>;
|
|
7
6
|
interpretText(input: InterpretTextInput): Promise<InterpretTextOutput>;
|
|
8
7
|
private detectKeyPhrases;
|
|
9
8
|
private detectSyntax;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Amplify, fetchAuthSession } from '@aws-amplify/core';
|
|
4
|
+
import { Category, PredictionsAction, getAmplifyUserAgentObject, } from '@aws-amplify/core/internals/utils';
|
|
5
|
+
import { ComprehendClient, DetectDominantLanguageCommand, DetectEntitiesCommand, DetectKeyPhrasesCommand, DetectSentimentCommand, DetectSyntaxCommand, } from '@aws-sdk/client-comprehend';
|
|
6
|
+
import { PredictionsValidationErrorCode } from '../errors/types/validation';
|
|
7
|
+
import { assertValidationError } from '../errors/utils/assertValidationError';
|
|
8
|
+
import { isInterpretTextOthers, isValidInterpretInput, } from '../types';
|
|
9
|
+
export class AmazonAIInterpretPredictionsProvider {
|
|
10
|
+
getProviderName() {
|
|
11
|
+
return 'AmazonAIInterpretPredictionsProvider';
|
|
12
|
+
}
|
|
13
|
+
interpret(input) {
|
|
14
|
+
assertValidationError(isValidInterpretInput(input), PredictionsValidationErrorCode.InvalidInput);
|
|
15
|
+
return this.interpretText(input);
|
|
16
|
+
}
|
|
17
|
+
async interpretText(input) {
|
|
18
|
+
const { credentials } = await fetchAuthSession();
|
|
19
|
+
assertValidationError(!!credentials, PredictionsValidationErrorCode.NoCredentials);
|
|
20
|
+
const { interpretText = {} } = Amplify.getConfig().Predictions?.interpret ?? {};
|
|
21
|
+
const { region = '', defaults = {} } = interpretText;
|
|
22
|
+
const { type: defaultType = '' } = defaults;
|
|
23
|
+
const { text: textSource } = input;
|
|
24
|
+
const { source, type = defaultType } = textSource;
|
|
25
|
+
const { text } = source;
|
|
26
|
+
let language;
|
|
27
|
+
if (isInterpretTextOthers(textSource)) {
|
|
28
|
+
language = textSource.source.language;
|
|
29
|
+
}
|
|
30
|
+
this.comprehendClient = new ComprehendClient({
|
|
31
|
+
credentials,
|
|
32
|
+
region,
|
|
33
|
+
customUserAgent: getAmplifyUserAgentObject({
|
|
34
|
+
category: Category.Predictions,
|
|
35
|
+
action: PredictionsAction.Interpret,
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
const doAll = type === 'all';
|
|
39
|
+
let languageCode = language;
|
|
40
|
+
if (doAll || type === 'language') {
|
|
41
|
+
const languageDetectionParams = {
|
|
42
|
+
Text: text,
|
|
43
|
+
};
|
|
44
|
+
languageCode = await this.detectLanguage(languageDetectionParams);
|
|
45
|
+
}
|
|
46
|
+
let entitiesPromise;
|
|
47
|
+
if (doAll || type === 'entities') {
|
|
48
|
+
assertValidationError(!!languageCode, PredictionsValidationErrorCode.NoLanguage);
|
|
49
|
+
const entitiesDetectionParams = {
|
|
50
|
+
Text: text,
|
|
51
|
+
LanguageCode: languageCode,
|
|
52
|
+
};
|
|
53
|
+
entitiesPromise = this.detectEntities(entitiesDetectionParams);
|
|
54
|
+
}
|
|
55
|
+
let sentimentPromise;
|
|
56
|
+
if (doAll || type === 'sentiment') {
|
|
57
|
+
assertValidationError(!!languageCode, PredictionsValidationErrorCode.NoLanguage);
|
|
58
|
+
const sentimentParams = {
|
|
59
|
+
Text: text,
|
|
60
|
+
LanguageCode: languageCode,
|
|
61
|
+
};
|
|
62
|
+
sentimentPromise = this.detectSentiment(sentimentParams);
|
|
63
|
+
}
|
|
64
|
+
let syntaxPromise;
|
|
65
|
+
if (doAll || type === 'syntax') {
|
|
66
|
+
assertValidationError(!!languageCode, PredictionsValidationErrorCode.NoLanguage);
|
|
67
|
+
const syntaxParams = {
|
|
68
|
+
Text: text,
|
|
69
|
+
LanguageCode: languageCode,
|
|
70
|
+
};
|
|
71
|
+
syntaxPromise = this.detectSyntax(syntaxParams);
|
|
72
|
+
}
|
|
73
|
+
let keyPhrasesPromise;
|
|
74
|
+
if (doAll || type === 'keyPhrases') {
|
|
75
|
+
assertValidationError(!!languageCode, PredictionsValidationErrorCode.NoLanguage);
|
|
76
|
+
const keyPhrasesParams = {
|
|
77
|
+
Text: text,
|
|
78
|
+
LanguageCode: languageCode,
|
|
79
|
+
};
|
|
80
|
+
keyPhrasesPromise = this.detectKeyPhrases(keyPhrasesParams);
|
|
81
|
+
}
|
|
82
|
+
const [textEntities, sentiment, syntax, keyPhrases] = await Promise.all([
|
|
83
|
+
entitiesPromise,
|
|
84
|
+
sentimentPromise,
|
|
85
|
+
syntaxPromise,
|
|
86
|
+
keyPhrasesPromise,
|
|
87
|
+
]);
|
|
88
|
+
return {
|
|
89
|
+
textInterpretation: {
|
|
90
|
+
keyPhrases,
|
|
91
|
+
language: languageCode,
|
|
92
|
+
sentiment,
|
|
93
|
+
syntax,
|
|
94
|
+
textEntities,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async detectKeyPhrases(params) {
|
|
99
|
+
try {
|
|
100
|
+
const detectKeyPhrasesCommand = new DetectKeyPhrasesCommand(params);
|
|
101
|
+
const data = await this.comprehendClient.send(detectKeyPhrasesCommand);
|
|
102
|
+
const { KeyPhrases = [] } = data || {};
|
|
103
|
+
return KeyPhrases.map(({ Text: text }) => {
|
|
104
|
+
return { text };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
if (err.code === 'AccessDeniedException') {
|
|
109
|
+
throw new Error('Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' +
|
|
110
|
+
'amplify predictions add');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async detectSyntax(params) {
|
|
118
|
+
try {
|
|
119
|
+
const detectSyntaxCommand = new DetectSyntaxCommand(params);
|
|
120
|
+
const data = await this.comprehendClient.send(detectSyntaxCommand);
|
|
121
|
+
const { SyntaxTokens = [] } = data || {};
|
|
122
|
+
return this.serializeSyntaxFromComprehend(SyntaxTokens);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (err.code === 'AccessDeniedException') {
|
|
126
|
+
throw new Error('Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' +
|
|
127
|
+
'amplify predictions add');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
serializeSyntaxFromComprehend(tokens) {
|
|
135
|
+
let response = [];
|
|
136
|
+
if (tokens && Array.isArray(tokens)) {
|
|
137
|
+
response = tokens.map(({ Text: text = '', PartOfSpeech: { Tag: syntax = '' } = {} }) => {
|
|
138
|
+
return { text, syntax };
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return response;
|
|
142
|
+
}
|
|
143
|
+
async detectSentiment(params) {
|
|
144
|
+
try {
|
|
145
|
+
const detectSentimentCommand = new DetectSentimentCommand(params);
|
|
146
|
+
const data = await this.comprehendClient.send(detectSentimentCommand);
|
|
147
|
+
const { Sentiment: predominant = '', SentimentScore: { Positive: positive = 0, Negative: negative = 0, Neutral: neutral = 0, Mixed: mixed = 0, } = {}, } = ({} = data);
|
|
148
|
+
return { predominant, positive, negative, neutral, mixed };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (err.code === 'AccessDeniedException') {
|
|
152
|
+
throw new Error('Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' +
|
|
153
|
+
'amplify predictions add');
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
throw err;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async detectEntities(params) {
|
|
161
|
+
try {
|
|
162
|
+
const detectEntitiesCommand = new DetectEntitiesCommand(params);
|
|
163
|
+
const data = await this.comprehendClient.send(detectEntitiesCommand);
|
|
164
|
+
const { Entities = [] } = data || {};
|
|
165
|
+
return this.serializeEntitiesFromComprehend(Entities);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
if (err.code === 'AccessDeniedException') {
|
|
169
|
+
throw new Error('Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' +
|
|
170
|
+
'amplify predictions add');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
serializeEntitiesFromComprehend(data) {
|
|
178
|
+
let response = [];
|
|
179
|
+
if (data && Array.isArray(data)) {
|
|
180
|
+
response = data.map(({ Type: type, Text: text }) => {
|
|
181
|
+
return { type, text };
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return response;
|
|
185
|
+
}
|
|
186
|
+
async detectLanguage(params) {
|
|
187
|
+
try {
|
|
188
|
+
const detectDominantLanguageCommand = new DetectDominantLanguageCommand(params);
|
|
189
|
+
const data = await this.comprehendClient.send(detectDominantLanguageCommand);
|
|
190
|
+
const { Languages: [{ LanguageCode }] = [{}] } = ({} = data || {});
|
|
191
|
+
assertValidationError(!!LanguageCode, PredictionsValidationErrorCode.NoLanguage);
|
|
192
|
+
return LanguageCode;
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
if (err.code === 'AccessDeniedException') {
|
|
196
|
+
throw new Error('Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' +
|
|
197
|
+
'amplify predictions add');
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IdentifyTextOutput } from '../types';
|
|
2
|
+
import { BlockList, TextDetectionList } from '../types/AWSTypes';
|
|
3
|
+
/**
|
|
4
|
+
* Organizes blocks from Rekognition API to each of the categories and and structures
|
|
5
|
+
* their data accordingly.
|
|
6
|
+
* @param {BlockList} source - Array containing blocks returned from Textract API.
|
|
7
|
+
* @return {IdentifyTextOutput} - Object that categorizes each block and its information.
|
|
8
|
+
*/
|
|
9
|
+
export declare function categorizeRekognitionBlocks(blocks: TextDetectionList): IdentifyTextOutput;
|
|
10
|
+
/**
|
|
11
|
+
* Organizes blocks from Textract API to each of the categories and and structures
|
|
12
|
+
* their data accordingly.
|
|
13
|
+
* @param {BlockList} source - Array containing blocks returned from Textract API.
|
|
14
|
+
* @return {IdentifyTextOutput} - Object that categorizes each block and its information.
|
|
15
|
+
*/
|
|
16
|
+
export declare function categorizeTextractBlocks(blocks: BlockList): IdentifyTextOutput;
|