@acorex-platform/extension-ai-document-client 0.0.21
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/fesm2022/acorex-platform-extension-ai-document-client-contracts.mjs +22 -0
- package/fesm2022/acorex-platform-extension-ai-document-client-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-extension-ai-document-client.mjs +840 -0
- package/fesm2022/acorex-platform-extension-ai-document-client.mjs.map +1 -0
- package/package.json +28 -0
- package/types/acorex-platform-extension-ai-document-client-contracts.d.ts +13 -0
- package/types/acorex-platform-extension-ai-document-client.d.ts +231 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant settings for optional OCR-based metadata prefill on image uploads (AI Document extension).
|
|
3
|
+
*/
|
|
4
|
+
var AXEDocumentMetadataOcrSettings;
|
|
5
|
+
(function (AXEDocumentMetadataOcrSettings) {
|
|
6
|
+
/** When true, eligible attachment dialogs may offer OCR after an image is chosen. */
|
|
7
|
+
AXEDocumentMetadataOcrSettings["Enabled"] = "AiDocument:Setting:MetadataOcr.Enabled";
|
|
8
|
+
/**
|
|
9
|
+
* Optional assist primary key (`Assist.id`). Metadata OCR uses ExtractDocumentText (assist chat model).
|
|
10
|
+
*/
|
|
11
|
+
AXEDocumentMetadataOcrSettings["AssistId"] = "AiDocument:Setting:MetadataOcr.AssistId";
|
|
12
|
+
})(AXEDocumentMetadataOcrSettings || (AXEDocumentMetadataOcrSettings = {}));
|
|
13
|
+
|
|
14
|
+
//#region ---- Contracts barrel ----
|
|
15
|
+
//#endregion
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generated bundle index. Do not edit.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export { AXEDocumentMetadataOcrSettings };
|
|
22
|
+
//# sourceMappingURL=acorex-platform-extension-ai-document-client-contracts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-platform-extension-ai-document-client-contracts.mjs","sources":["../../../../libs/extensions/ai/document/contracts/src/document-metadata-ocr.settings.keys.ts","../../../../libs/extensions/ai/document/contracts/src/index.ts","../../../../libs/extensions/ai/document/contracts/src/acorex-platform-extension-ai-document-client-contracts.ts"],"sourcesContent":["/**\r\n * Tenant settings for optional OCR-based metadata prefill on image uploads (AI Document extension).\r\n */\r\nexport enum AXEDocumentMetadataOcrSettings {\r\n /** When true, eligible attachment dialogs may offer OCR after an image is chosen. */\r\n Enabled = 'AiDocument:Setting:MetadataOcr.Enabled',\r\n\r\n /**\r\n * Optional assist primary key (`Assist.id`). Metadata OCR uses ExtractDocumentText (assist chat model).\r\n */\r\n AssistId = 'AiDocument:Setting:MetadataOcr.AssistId',\r\n}\r\n","//#region ---- Contracts barrel ----\n\nexport * from './document-metadata-ocr.settings.keys';\n\n//#endregion\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAAA;;AAEG;IACS;AAAZ,CAAA,UAAY,8BAA8B,EAAA;;AAExC,IAAA,8BAAA,CAAA,SAAA,CAAA,GAAA,wCAAkD;AAElD;;AAEG;AACH,IAAA,8BAAA,CAAA,UAAA,CAAA,GAAA,yCAAoD;AACtD,CAAC,EARW,8BAA8B,KAA9B,8BAA8B,GAAA,EAAA,CAAA,CAAA;;ACH1C;AAIA;;ACJA;;AAEG;;;;"}
|
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, inject, NgModule } from '@angular/core';
|
|
3
|
+
import { AXMAiManagementModule } from '@acorex-platform/module-ai-management-client';
|
|
4
|
+
import { AXMDocumentManagementModule } from '@acorex-platform/module-document-management-client';
|
|
5
|
+
import { AXPFileStorageService, AXPSettingsService, AXPDebugService, extractRequestErrorInfo, AXP_SETTING_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/common';
|
|
6
|
+
import { objectKeyValueTransforms, AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER } from '@acorex-platform/framework-client/core';
|
|
7
|
+
import { RootConfig, canAiExtractDocument, AIMANAGEMENT_EXTRACT_DOCUMENT_TEXT_COMMAND_KEY } from '@acorex-platform/module-ai-management-client/contracts';
|
|
8
|
+
import { AXPWidgetsCatalog, AXPFileStorageStatus, runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';
|
|
9
|
+
import { AXEDocumentMetadataOcrSettings } from '@acorex-platform/extension-ai-document-client/contracts';
|
|
10
|
+
import { AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY } from '@acorex-platform/framework-client/layout/builder';
|
|
11
|
+
import { AXDialogService } from '@acorex/components/dialog';
|
|
12
|
+
import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
|
|
13
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
14
|
+
import { AXPCommandService } from '@acorex-platform/framework-client/runtime';
|
|
15
|
+
|
|
16
|
+
//#region ---- Imports ----
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region ---- Settings provider ----
|
|
19
|
+
const METADATA_OCR_I18N = '@extensions-ai-document:settings.metadata-ocr';
|
|
20
|
+
/** Must match the settings group `name` from the document-management module (`AXPDocumentManagementSettingProvider`). */
|
|
21
|
+
const DOCUMENT_MANAGEMENT_SETTINGS_GROUP = 'document-management';
|
|
22
|
+
class AXEDocumentAiSettingProvider {
|
|
23
|
+
async provide(context) {
|
|
24
|
+
if (!context.hasGroup(DOCUMENT_MANAGEMENT_SETTINGS_GROUP)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const assistVisibility = `{{ context.eval('${AXEDocumentMetadataOcrSettings.Enabled}') }}`;
|
|
28
|
+
const documentManagement = context.group(DOCUMENT_MANAGEMENT_SETTINGS_GROUP);
|
|
29
|
+
if (!documentManagement) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
documentManagement
|
|
33
|
+
.addSection('metadata-ocr', `${METADATA_OCR_I18N}.section-title`, `${METADATA_OCR_I18N}.section-description`)
|
|
34
|
+
.addSetting({
|
|
35
|
+
key: AXEDocumentMetadataOcrSettings.Enabled,
|
|
36
|
+
title: `${METADATA_OCR_I18N}.enabled-title`,
|
|
37
|
+
scope: 'T',
|
|
38
|
+
overridableAt: [],
|
|
39
|
+
isInherited: true,
|
|
40
|
+
defaultValue: true,
|
|
41
|
+
searchTagKeys: [`${METADATA_OCR_I18N}.enabled-search-tags`],
|
|
42
|
+
widget: {
|
|
43
|
+
type: AXPWidgetsCatalog.toggle,
|
|
44
|
+
},
|
|
45
|
+
description: `${METADATA_OCR_I18N}.enabled-description`,
|
|
46
|
+
})
|
|
47
|
+
.addSetting({
|
|
48
|
+
key: AXEDocumentMetadataOcrSettings.AssistId,
|
|
49
|
+
title: `${METADATA_OCR_I18N}.assist-id-title`,
|
|
50
|
+
scope: 'T',
|
|
51
|
+
overridableAt: [],
|
|
52
|
+
isInherited: true,
|
|
53
|
+
// Persist assist primary key (`id`); demo defaults come from settings seed/overrides.
|
|
54
|
+
searchTagKeys: [`${METADATA_OCR_I18N}.assist-search-tags`],
|
|
55
|
+
valueTransforms: objectKeyValueTransforms('id'),
|
|
56
|
+
widget: {
|
|
57
|
+
type: AXPWidgetsCatalog.lookup,
|
|
58
|
+
options: {
|
|
59
|
+
entity: `${RootConfig.module.name}.${RootConfig.entities.assist.name}`,
|
|
60
|
+
look: 'select',
|
|
61
|
+
multiple: false,
|
|
62
|
+
textField: 'title',
|
|
63
|
+
valueField: 'id',
|
|
64
|
+
hasClearButton: true,
|
|
65
|
+
visibility: assistVisibility,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
description: `${METADATA_OCR_I18N}.assist-id-description`,
|
|
69
|
+
})
|
|
70
|
+
.endSection();
|
|
71
|
+
}
|
|
72
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentAiSettingProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
73
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentAiSettingProvider }); }
|
|
74
|
+
}
|
|
75
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentAiSettingProvider, decorators: [{
|
|
76
|
+
type: Injectable
|
|
77
|
+
}] });
|
|
78
|
+
|
|
79
|
+
//#region ---- Imports ----
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region ---- Constants ----
|
|
82
|
+
/**
|
|
83
|
+
* Synthetic mapping key for the document-level Expire Date control
|
|
84
|
+
* (`file-*.expirationDate` when the document type has `enableExpiration`).
|
|
85
|
+
* Not part of `metaDataForm`.
|
|
86
|
+
*/
|
|
87
|
+
const DOCUMENT_EXPIRATION_DATE_FIELD_NAME = 'expirationDate';
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region ---- Field collection ----
|
|
90
|
+
/**
|
|
91
|
+
* Flattens metadata selector value into field descriptors for LLM mapping prompts.
|
|
92
|
+
*/
|
|
93
|
+
function collectMetadataFieldDescriptors(meta) {
|
|
94
|
+
if (!meta?.groups?.length) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const g of meta.groups) {
|
|
99
|
+
for (const f of g.fields ?? []) {
|
|
100
|
+
out.push({
|
|
101
|
+
name: f.name,
|
|
102
|
+
title: f.title ?? f.name,
|
|
103
|
+
interfaceType: f.interface,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Appends the platform document expire-date field used by attachment dialogs.
|
|
111
|
+
*/
|
|
112
|
+
function withDocumentExpirationField(fields, include) {
|
|
113
|
+
if (!include) {
|
|
114
|
+
return fields;
|
|
115
|
+
}
|
|
116
|
+
if (fields.some((f) => f.name === DOCUMENT_EXPIRATION_DATE_FIELD_NAME)) {
|
|
117
|
+
return fields;
|
|
118
|
+
}
|
|
119
|
+
return [
|
|
120
|
+
...fields,
|
|
121
|
+
{
|
|
122
|
+
name: DOCUMENT_EXPIRATION_DATE_FIELD_NAME,
|
|
123
|
+
title: 'Expire Date',
|
|
124
|
+
interfaceType: 'date-time-editor',
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region ---- JSON parsing ----
|
|
130
|
+
/**
|
|
131
|
+
* Coerces common OCR / LLM date strings into YYYY-MM-DD (or leaves non-dates unchanged).
|
|
132
|
+
*/
|
|
133
|
+
function normalizePrefillDateValue(value) {
|
|
134
|
+
if (value == null || value === '') {
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
138
|
+
return toIsoDateOnly(value);
|
|
139
|
+
}
|
|
140
|
+
if (typeof value !== 'string') {
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
const raw = value.trim();
|
|
144
|
+
if (!raw) {
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
const iso = /^(\d{4})-(\d{2})-(\d{2})/.exec(raw);
|
|
148
|
+
if (iso) {
|
|
149
|
+
return `${iso[1]}-${iso[2]}-${iso[3]}`;
|
|
150
|
+
}
|
|
151
|
+
// US-style MM/DD/YYYY (common on driver licences).
|
|
152
|
+
const us = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(raw);
|
|
153
|
+
if (us) {
|
|
154
|
+
const month = Number(us[1]);
|
|
155
|
+
const day = Number(us[2]);
|
|
156
|
+
const year = Number(us[3]);
|
|
157
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
158
|
+
return `${year}-${pad2(month)}-${pad2(day)}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const parsed = new Date(raw);
|
|
162
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
163
|
+
return toIsoDateOnly(parsed);
|
|
164
|
+
}
|
|
165
|
+
return value;
|
|
166
|
+
}
|
|
167
|
+
function pad2(n) {
|
|
168
|
+
return n < 10 ? `0${n}` : String(n);
|
|
169
|
+
}
|
|
170
|
+
function toIsoDateOnly(d) {
|
|
171
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Normalizes date-like mapped values for date widgets; strips the synthetic expiration key from meta.
|
|
175
|
+
*/
|
|
176
|
+
function splitMappedPrefill(parsed, fields) {
|
|
177
|
+
const dateNames = new Set(fields
|
|
178
|
+
.filter((f) => {
|
|
179
|
+
const t = String(f.interfaceType ?? '').toLowerCase();
|
|
180
|
+
return t.includes('date-time') || t.includes('date');
|
|
181
|
+
})
|
|
182
|
+
.map((f) => f.name));
|
|
183
|
+
const meta = {};
|
|
184
|
+
let expirationDate;
|
|
185
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
186
|
+
const normalized = dateNames.has(k) ? normalizePrefillDateValue(v) : v;
|
|
187
|
+
if (k === DOCUMENT_EXPIRATION_DATE_FIELD_NAME) {
|
|
188
|
+
if (typeof normalized === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
|
|
189
|
+
expirationDate = normalized;
|
|
190
|
+
}
|
|
191
|
+
else if (normalized instanceof Date && !Number.isNaN(normalized.getTime())) {
|
|
192
|
+
expirationDate = toIsoDateOnly(normalized);
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
meta[k] = normalized;
|
|
197
|
+
}
|
|
198
|
+
return expirationDate ? { meta, expirationDate } : { meta };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Parses a JSON object from an assistant reply (strips optional ```json fences).
|
|
202
|
+
*/
|
|
203
|
+
function parseMetadataMappingJson(text) {
|
|
204
|
+
const trimmed = text.trim();
|
|
205
|
+
const fence = /^```(?:json)?\s*([\s\S]*?)```$/m.exec(trimmed);
|
|
206
|
+
const raw = fence ? fence[1].trim() : trimmed;
|
|
207
|
+
try {
|
|
208
|
+
const parsed = JSON.parse(raw);
|
|
209
|
+
if (parsed != null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
210
|
+
return parsed;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// ignore
|
|
215
|
+
}
|
|
216
|
+
return {};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Shallow merge: only fills keys that are empty in `existing`.
|
|
220
|
+
*/
|
|
221
|
+
function mergeMetadataPrefill(existing, partial) {
|
|
222
|
+
const base = { ...(existing ?? {}) };
|
|
223
|
+
for (const [k, v] of Object.entries(partial)) {
|
|
224
|
+
const cur = base[k];
|
|
225
|
+
const empty = cur === undefined || cur === null || cur === '';
|
|
226
|
+
if (empty && v !== undefined && v !== null && v !== '') {
|
|
227
|
+
base[k] = v;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return base;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Returns true when the file list item is supported for AI document text extraction.
|
|
234
|
+
*/
|
|
235
|
+
function isAiExtractableFileListItem(item) {
|
|
236
|
+
if (!item) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
const t = typeof item.type === 'string' ? item.type : '';
|
|
240
|
+
const name = String(item.name ?? '');
|
|
241
|
+
if (canAiExtractDocument(t, name)) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
const src = item.source?.value;
|
|
245
|
+
if (src instanceof Blob) {
|
|
246
|
+
const blobType = typeof src.type === 'string' ? src.type : '';
|
|
247
|
+
if (canAiExtractDocument(blobType, name)) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (src instanceof File) {
|
|
252
|
+
if (canAiExtractDocument(src.type, src.name || name)) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
|
|
260
|
+
//#region ----- Imports -----
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region ----- Constants -----
|
|
263
|
+
const DOCUMENT_MODULE_REF_TYPE = 'DocumentManagement.Document';
|
|
264
|
+
const SINGLE_DOCUMENT_PROGRESS_SCOPE = { documentIndex: 1, documentCount: 1 };
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region ----- Local helpers -----
|
|
267
|
+
const OCR_FAILED_I18N = '@extensions-ai-document:widgets.metadata-ai-fill.ocr-failed';
|
|
268
|
+
function throwIfAborted(signal) {
|
|
269
|
+
if (signal?.aborted) {
|
|
270
|
+
throw new DOMException('Aborted', 'AbortError');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function stringifyErrorDetails(details) {
|
|
274
|
+
if (typeof details === 'string' && details.trim()) {
|
|
275
|
+
return details.trim();
|
|
276
|
+
}
|
|
277
|
+
if (details == null) {
|
|
278
|
+
return '';
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
return JSON.stringify(details);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return String(details);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region ----- Service -----
|
|
289
|
+
/**
|
|
290
|
+
* Runs AI document text extraction for metadata prefill (images, PDF, simple text files).
|
|
291
|
+
* ExtractDocumentText command; when field names are passed it returns a key/value object.
|
|
292
|
+
*/
|
|
293
|
+
class AXEDocumentMetadataAiPrefillService {
|
|
294
|
+
constructor() {
|
|
295
|
+
//#region ----- Services & Dependencies -----
|
|
296
|
+
this.fileStorage = inject(AXPFileStorageService);
|
|
297
|
+
this.commandService = inject(AXPCommandService);
|
|
298
|
+
this.settings = inject(AXPSettingsService);
|
|
299
|
+
this.debug = inject(AXPDebugService);
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
/**
|
|
303
|
+
* Whether OCR metadata prefill is enabled in tenant settings.
|
|
304
|
+
* Defaults to true when the setting has never been stored (opt-out via settings UI).
|
|
305
|
+
*/
|
|
306
|
+
async isMetadataOcrEnabled() {
|
|
307
|
+
const v = await this.settings.get(AXEDocumentMetadataOcrSettings.Enabled);
|
|
308
|
+
if (v == null) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
return v;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Whether {@link applyDialogContextMetadataAiFillFromDocuments} would extract at least one document
|
|
315
|
+
* (caller can safely show an indeterminate loading dialog).
|
|
316
|
+
*/
|
|
317
|
+
willApplyMetadataAiFillFromDocuments(dialogRef, integration) {
|
|
318
|
+
if (!dialogRef.patchContext) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
const documentType = integration['documentType'];
|
|
322
|
+
const metaDataList = documentType?.metaDataList;
|
|
323
|
+
const fields = withDocumentExpirationField(collectMetadataFieldDescriptors(metaDataList ?? undefined), !!documentType?.enableExpiration);
|
|
324
|
+
if (fields.length === 0) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
const ctx = dialogRef.context();
|
|
328
|
+
const files = ctx['files'];
|
|
329
|
+
const n = files?.length ?? 0;
|
|
330
|
+
for (let i = 0; i < n; i++) {
|
|
331
|
+
const slot = files[i]?.file;
|
|
332
|
+
const first = Array.isArray(slot) ? slot[0] : undefined;
|
|
333
|
+
if (first && isAiExtractableFileListItem(first)) {
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Uploads a blob-backed file item to storage for vision APIs that require {@link fileId}.
|
|
341
|
+
*/
|
|
342
|
+
async ensureStoredFileId(fileItem, folderId) {
|
|
343
|
+
// Only trust an explicit storage fileId. Chooser items use a client UUID on `id` with `source.kind === 'blob'`.
|
|
344
|
+
if (fileItem.source?.kind === 'fileId' && typeof fileItem.source.value === 'string') {
|
|
345
|
+
const storedId = fileItem.source.value.trim();
|
|
346
|
+
if (storedId) {
|
|
347
|
+
return storedId;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const file = this.resolveFileForUpload(fileItem);
|
|
351
|
+
if (!file) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
const refId = folderId?.trim() || 'document-metadata-ocr-prefill';
|
|
355
|
+
const saved = await this.fileStorage.save({
|
|
356
|
+
file,
|
|
357
|
+
name: fileItem.name,
|
|
358
|
+
refId,
|
|
359
|
+
refType: DOCUMENT_MODULE_REF_TYPE,
|
|
360
|
+
category: 'document',
|
|
361
|
+
status: AXPFileStorageStatus.Temporary,
|
|
362
|
+
});
|
|
363
|
+
return saved.fileId?.trim() || null;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* OCR + field extraction in one vision call; returns merged metadata (only fills empty keys in {@link existingMeta}).
|
|
367
|
+
*/
|
|
368
|
+
async prefillFromDocumentExtract(options) {
|
|
369
|
+
const signal = options.signal;
|
|
370
|
+
const notify = (phase, scope = options.progressScope ??
|
|
371
|
+
SINGLE_DOCUMENT_PROGRESS_SCOPE) => options.signalProgress?.({ phase, ...scope });
|
|
372
|
+
throwIfAborted(signal);
|
|
373
|
+
const fields = withDocumentExpirationField(collectMetadataFieldDescriptors(options.metaDataList ?? undefined), !!options.includeDocumentExpiration);
|
|
374
|
+
if (fields.length === 0) {
|
|
375
|
+
return { extractText: '', merged: { ...(options.existingMeta ?? {}) } };
|
|
376
|
+
}
|
|
377
|
+
notify('uploading');
|
|
378
|
+
const fileId = await this.ensureStoredFileId(options.fileItem, options.folderId);
|
|
379
|
+
throwIfAborted(signal);
|
|
380
|
+
if (!fileId) {
|
|
381
|
+
throw new Error('Could not upload the file for document extraction.');
|
|
382
|
+
}
|
|
383
|
+
notify('extracting');
|
|
384
|
+
const extractResult = await this.commandService.execute(AIMANAGEMENT_EXTRACT_DOCUMENT_TEXT_COMMAND_KEY, {
|
|
385
|
+
fileId,
|
|
386
|
+
fields: fields.map((field) => field.name),
|
|
387
|
+
});
|
|
388
|
+
throwIfAborted(signal);
|
|
389
|
+
if (extractResult?.success === false) {
|
|
390
|
+
throw this.extractCommandFailure(extractResult);
|
|
391
|
+
}
|
|
392
|
+
const extractData = runtimeCommandPayloadData(extractResult);
|
|
393
|
+
const extractText = typeof extractData?.text === 'string' ? extractData.text : '';
|
|
394
|
+
const parsed = extractData?.values && typeof extractData.values === 'object' && !Array.isArray(extractData.values)
|
|
395
|
+
? extractData.values
|
|
396
|
+
: parseMetadataMappingJson(extractText);
|
|
397
|
+
if (Object.keys(parsed).length === 0 && !extractText.trim()) {
|
|
398
|
+
throw this.extractCommandFailure(extractResult);
|
|
399
|
+
}
|
|
400
|
+
const { meta, expirationDate } = splitMappedPrefill(parsed, fields);
|
|
401
|
+
const merged = mergeMetadataPrefill(options.existingMeta, meta);
|
|
402
|
+
const existingExpEmpty = options.existingExpirationDate === undefined ||
|
|
403
|
+
options.existingExpirationDate === null ||
|
|
404
|
+
options.existingExpirationDate === '';
|
|
405
|
+
return {
|
|
406
|
+
extractText,
|
|
407
|
+
merged,
|
|
408
|
+
...(existingExpEmpty && expirationDate ? { expirationDate } : {}),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* For each extractable document slot in `context.files`, merges extraction + AI metadata into `file-{i}.metaDataForm`.
|
|
413
|
+
*/
|
|
414
|
+
async applyDialogContextMetadataAiFillFromDocuments(dialogRef, integration, options) {
|
|
415
|
+
if (!dialogRef.patchContext) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const folderId = String(integration['parentId'] ?? '').trim();
|
|
419
|
+
const documentType = integration['documentType'];
|
|
420
|
+
const metaDataList = documentType?.metaDataList;
|
|
421
|
+
const includeDocumentExpiration = !!documentType?.enableExpiration;
|
|
422
|
+
const ctx = dialogRef.context();
|
|
423
|
+
const files = ctx['files'];
|
|
424
|
+
const n = files?.length ?? 0;
|
|
425
|
+
const patch = {};
|
|
426
|
+
const documentIndices = [];
|
|
427
|
+
for (let i = 0; i < n; i++) {
|
|
428
|
+
const slot = files[i]?.file;
|
|
429
|
+
const first = Array.isArray(slot) ? slot[0] : undefined;
|
|
430
|
+
if (first && isAiExtractableFileListItem(first)) {
|
|
431
|
+
documentIndices.push(i);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const signal = options?.signal;
|
|
435
|
+
const documentCount = documentIndices.length;
|
|
436
|
+
if (documentCount > 0) {
|
|
437
|
+
options?.signalProgress?.({
|
|
438
|
+
phase: 'preparing',
|
|
439
|
+
documentIndex: 1,
|
|
440
|
+
documentCount,
|
|
441
|
+
});
|
|
442
|
+
throwIfAborted(signal);
|
|
443
|
+
}
|
|
444
|
+
let processed = 0;
|
|
445
|
+
for (const i of documentIndices) {
|
|
446
|
+
throwIfAborted(signal);
|
|
447
|
+
processed += 1;
|
|
448
|
+
const slot = files[i]?.file;
|
|
449
|
+
const first = Array.isArray(slot) ? slot[0] : undefined;
|
|
450
|
+
if (!first) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const fileBlock = ctx[`file-${i}`];
|
|
454
|
+
const existingMeta = fileBlock?.['metaDataForm'];
|
|
455
|
+
const { merged, expirationDate } = await this.prefillFromDocumentExtract({
|
|
456
|
+
fileItem: first,
|
|
457
|
+
folderId,
|
|
458
|
+
metaDataList,
|
|
459
|
+
existingMeta,
|
|
460
|
+
includeDocumentExpiration,
|
|
461
|
+
existingExpirationDate: fileBlock?.['expirationDate'],
|
|
462
|
+
progressScope: { documentIndex: processed, documentCount },
|
|
463
|
+
signal,
|
|
464
|
+
signalProgress: options?.signalProgress,
|
|
465
|
+
});
|
|
466
|
+
patch[`file-${i}`] = {
|
|
467
|
+
...(typeof fileBlock === 'object' && fileBlock ? fileBlock : {}),
|
|
468
|
+
metaDataForm: merged,
|
|
469
|
+
...(expirationDate ? { expirationDate } : {}),
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (Object.keys(patch).length > 0) {
|
|
473
|
+
dialogRef.patchContext(patch);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
//#region ----- Utility Methods -----
|
|
477
|
+
extractCommandFailure(extractResult) {
|
|
478
|
+
const info = extractRequestErrorInfo(extractResult);
|
|
479
|
+
const details = stringifyErrorDetails(info?.details);
|
|
480
|
+
this.debug.error('ai-document', 'extract-document-text', 'ExtractDocumentText failed', () => ({
|
|
481
|
+
message: info?.message,
|
|
482
|
+
details: info?.details,
|
|
483
|
+
}));
|
|
484
|
+
if (this.debug.isDebugMode() && details) {
|
|
485
|
+
return new Error(details);
|
|
486
|
+
}
|
|
487
|
+
const text = typeof info?.message === 'string' ? info.message.trim() : '';
|
|
488
|
+
if (text.startsWith('@')) {
|
|
489
|
+
return new Error(text);
|
|
490
|
+
}
|
|
491
|
+
return new Error(OCR_FAILED_I18N);
|
|
492
|
+
}
|
|
493
|
+
resolveFileForUpload(item) {
|
|
494
|
+
const v = item.source?.value;
|
|
495
|
+
if (v instanceof File) {
|
|
496
|
+
return v;
|
|
497
|
+
}
|
|
498
|
+
if (item.source?.kind === 'blob' && v instanceof Blob) {
|
|
499
|
+
return new File([v], item.name, { type: item.type ?? v.type ?? 'application/octet-stream' });
|
|
500
|
+
}
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentMetadataAiPrefillService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
504
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentMetadataAiPrefillService, providedIn: 'root' }); }
|
|
505
|
+
}
|
|
506
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentMetadataAiPrefillService, decorators: [{
|
|
507
|
+
type: Injectable,
|
|
508
|
+
args: [{ providedIn: 'root' }]
|
|
509
|
+
}] });
|
|
510
|
+
|
|
511
|
+
//#region ---- Imports ----
|
|
512
|
+
//#endregion
|
|
513
|
+
const METADATA_AI_CONFIRM_I18N = '@extensions-ai-document:widgets.metadata-ai-fill.confirm';
|
|
514
|
+
const METADATA_AI_LOADING_I18N = '@extensions-ai-document:widgets.metadata-ai-fill.loading';
|
|
515
|
+
/** Smoother dismiss after work completes. */
|
|
516
|
+
const LOADING_DIALOG_CLOSE_DELAY_MS = 400;
|
|
517
|
+
//#region ---- Orchestrator ----
|
|
518
|
+
/**
|
|
519
|
+
* Shared OCR + AI metadata prefill UX: confirm after image choose, then indeterminate loading dialog.
|
|
520
|
+
*/
|
|
521
|
+
class AXEDocumentMetadataAiFillOrchestrator {
|
|
522
|
+
constructor() {
|
|
523
|
+
//#region ---- Dependencies ----
|
|
524
|
+
this.prefill = inject(AXEDocumentMetadataAiPrefillService);
|
|
525
|
+
this.dialogService = inject(AXDialogService);
|
|
526
|
+
this.loadingDialog = inject(AXLoadingDialogService);
|
|
527
|
+
this.translation = inject(AXTranslationService);
|
|
528
|
+
this.debug = inject(AXPDebugService);
|
|
529
|
+
}
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region ---- Public API ----
|
|
532
|
+
/**
|
|
533
|
+
* Asks whether to run OCR; on Yes runs {@link run}, on No returns {@link AXEDocumentMetadataAiFillRunResult.declined}.
|
|
534
|
+
*/
|
|
535
|
+
async confirmAndRun(dialogTarget, integration) {
|
|
536
|
+
if (!this.prefill.willApplyMetadataAiFillFromDocuments(dialogTarget, integration)) {
|
|
537
|
+
return { success: true };
|
|
538
|
+
}
|
|
539
|
+
const [title, message] = await Promise.all([
|
|
540
|
+
this.translation.translateAsync(`${METADATA_AI_CONFIRM_I18N}.title`),
|
|
541
|
+
this.translation.translateAsync(`${METADATA_AI_CONFIRM_I18N}.message`),
|
|
542
|
+
]);
|
|
543
|
+
const dialogResult = await this.dialogService.confirm(title, message, 'primary', 'horizontal', false, 'cancel');
|
|
544
|
+
if (!dialogResult.result) {
|
|
545
|
+
return { success: true, declined: true };
|
|
546
|
+
}
|
|
547
|
+
return this.run(dialogTarget, integration);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Runs metadata AI fill when eligible; shows an indeterminate loading dialog while work is in progress.
|
|
551
|
+
*/
|
|
552
|
+
async run(dialogTarget, integration) {
|
|
553
|
+
const showLoading = this.prefill.willApplyMetadataAiFillFromDocuments(dialogTarget, integration);
|
|
554
|
+
const abortController = new AbortController();
|
|
555
|
+
let loadingDismissedEarly = false;
|
|
556
|
+
let progressDialog;
|
|
557
|
+
if (showLoading) {
|
|
558
|
+
const [title, initial, skipLabel] = await Promise.all([
|
|
559
|
+
this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.title`),
|
|
560
|
+
this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.initial`),
|
|
561
|
+
this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.skip-button`),
|
|
562
|
+
]);
|
|
563
|
+
progressDialog = this.loadingDialog.show({
|
|
564
|
+
title,
|
|
565
|
+
mode: 'indeterminate',
|
|
566
|
+
progressColor: 'primary',
|
|
567
|
+
text: initial,
|
|
568
|
+
buttons: [
|
|
569
|
+
{
|
|
570
|
+
text: skipLabel,
|
|
571
|
+
onClick: () => {
|
|
572
|
+
loadingDismissedEarly = true;
|
|
573
|
+
abortController.abort();
|
|
574
|
+
progressDialog?.close();
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
await this.prefill.applyDialogContextMetadataAiFillFromDocuments(dialogTarget, integration, {
|
|
582
|
+
signal: showLoading ? abortController.signal : undefined,
|
|
583
|
+
signalProgress: (event) => {
|
|
584
|
+
if (!progressDialog) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
void this.applyProgressText(progressDialog, event);
|
|
588
|
+
},
|
|
589
|
+
});
|
|
590
|
+
if (progressDialog && !loadingDismissedEarly) {
|
|
591
|
+
await this.markProgressComplete(progressDialog);
|
|
592
|
+
}
|
|
593
|
+
return { success: true };
|
|
594
|
+
}
|
|
595
|
+
catch (err) {
|
|
596
|
+
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
597
|
+
return { success: false, cancelled: true };
|
|
598
|
+
}
|
|
599
|
+
const errorMessage = await this.resolveFailureMessage(err);
|
|
600
|
+
if (progressDialog && !loadingDismissedEarly) {
|
|
601
|
+
try {
|
|
602
|
+
progressDialog.setProgressColor('danger');
|
|
603
|
+
progressDialog.setProgressText(errorMessage);
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
// Ignore dialog update failures.
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (this.debug.isDebugMode() && progressDialog && !loadingDismissedEarly) {
|
|
610
|
+
loadingDismissedEarly = true;
|
|
611
|
+
}
|
|
612
|
+
return { success: false, errorMessage };
|
|
613
|
+
}
|
|
614
|
+
finally {
|
|
615
|
+
if (progressDialog !== undefined && !loadingDismissedEarly) {
|
|
616
|
+
const dlg = progressDialog;
|
|
617
|
+
setTimeout(() => dlg.close(), LOADING_DIALOG_CLOSE_DELAY_MS);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region ---- Progress ----
|
|
623
|
+
async applyProgressText(dialog, event) {
|
|
624
|
+
try {
|
|
625
|
+
dialog.setProgressColor('primary');
|
|
626
|
+
const line = await this.formatProgressLine(event);
|
|
627
|
+
dialog.setProgressText(line);
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
// Ignore translation / dialog failures; keep the previous UI state.
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async markProgressComplete(dialog) {
|
|
634
|
+
try {
|
|
635
|
+
dialog.setProgressColor('success');
|
|
636
|
+
dialog.setProgressText(await this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.complete`));
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
dialog.setProgressColor('success');
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
async resolveFailureMessage(err) {
|
|
643
|
+
const raw = err instanceof Error ? err.message.trim() : '';
|
|
644
|
+
if (this.debug.isDebugMode() && raw && !raw.startsWith('@')) {
|
|
645
|
+
const prefix = await this.translation.translateAsync('@extensions-ai-document:widgets.metadata-ai-fill.ocr-failed-prefix');
|
|
646
|
+
return `${prefix} ${raw}`;
|
|
647
|
+
}
|
|
648
|
+
const key = raw.startsWith('@') ? raw : '@extensions-ai-document:widgets.metadata-ai-fill.ocr-failed';
|
|
649
|
+
return this.translation.translateAsync(key);
|
|
650
|
+
}
|
|
651
|
+
async formatProgressLine(event) {
|
|
652
|
+
const phaseKey = `${METADATA_AI_LOADING_I18N}.thinking.${event.phase}`;
|
|
653
|
+
const phaseText = await this.translation.translateAsync(phaseKey);
|
|
654
|
+
if (event.documentCount > 1) {
|
|
655
|
+
return this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.step-prefix`, {
|
|
656
|
+
params: {
|
|
657
|
+
current: event.documentIndex,
|
|
658
|
+
total: event.documentCount,
|
|
659
|
+
phase: phaseText,
|
|
660
|
+
},
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
return phaseText;
|
|
664
|
+
}
|
|
665
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentMetadataAiFillOrchestrator, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
666
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentMetadataAiFillOrchestrator, providedIn: 'root' }); }
|
|
667
|
+
}
|
|
668
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEDocumentMetadataAiFillOrchestrator, decorators: [{
|
|
669
|
+
type: Injectable,
|
|
670
|
+
args: [{ providedIn: 'root' }]
|
|
671
|
+
}] });
|
|
672
|
+
|
|
673
|
+
//#region ---- Imports ----
|
|
674
|
+
const promptSessions = new Map();
|
|
675
|
+
function sessionState(sessionKey) {
|
|
676
|
+
let s = promptSessions.get(sessionKey);
|
|
677
|
+
if (!s) {
|
|
678
|
+
s = {};
|
|
679
|
+
promptSessions.set(sessionKey, s);
|
|
680
|
+
}
|
|
681
|
+
return s;
|
|
682
|
+
}
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region ---- Helpers ----
|
|
685
|
+
/**
|
|
686
|
+
* Document Management attachment dialogs expose this metadata so extensions can scope integration-only hooks.
|
|
687
|
+
*/
|
|
688
|
+
function isDocumentAttachmentDialogIntegration(metadata) {
|
|
689
|
+
return metadata['showMeta'] === true && metadata['hasMetaFields'] === true && metadata['documentType'] != null;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Stable fingerprint for extractable document slots — ignores metadata field edits so we do not re-prompt on every keystroke.
|
|
693
|
+
*/
|
|
694
|
+
function extractableAttachmentSlotsSignature(context) {
|
|
695
|
+
const files = context['files'];
|
|
696
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
697
|
+
return '';
|
|
698
|
+
}
|
|
699
|
+
const parts = [];
|
|
700
|
+
for (let i = 0; i < files.length; i++) {
|
|
701
|
+
const slot = files[i]?.file;
|
|
702
|
+
const first = Array.isArray(slot) ? slot[0] : undefined;
|
|
703
|
+
if (!first || !isAiExtractableFileListItem(first)) {
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
const name = String(first['name'] ?? '');
|
|
707
|
+
const size = typeof first['size'] === 'number' ? first['size'] : '';
|
|
708
|
+
const mime = String(first['type'] ?? '');
|
|
709
|
+
const src = first['source'];
|
|
710
|
+
const kind = String(src?.kind ?? '');
|
|
711
|
+
let sourceFingerprint = '';
|
|
712
|
+
if (kind === 'fileId' && typeof src?.value === 'string') {
|
|
713
|
+
sourceFingerprint = `fileId:${src.value}`;
|
|
714
|
+
}
|
|
715
|
+
else if (kind === 'blob' && src?.value instanceof Blob) {
|
|
716
|
+
sourceFingerprint = `blob:${src.value.size}:${src.value.type}`;
|
|
717
|
+
}
|
|
718
|
+
else if (src?.value instanceof File) {
|
|
719
|
+
const f = src.value;
|
|
720
|
+
sourceFingerprint = `file:${f.size}:${f.type}`;
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
sourceFingerprint = `${kind}:${typeof src?.value}`;
|
|
724
|
+
}
|
|
725
|
+
parts.push(`${i}:${name}:${size}:${mime}:${sourceFingerprint}`);
|
|
726
|
+
}
|
|
727
|
+
return parts.join('|');
|
|
728
|
+
}
|
|
729
|
+
//#endregion
|
|
730
|
+
//#region ---- Hook provider ----
|
|
731
|
+
/**
|
|
732
|
+
* When document extraction is enabled and the attachment dialog has metadata + extractable files, prompts the user
|
|
733
|
+
* after files appear. Yes → loading dialog + AI prefill; No → leave metadata form empty for manual entry.
|
|
734
|
+
* Dedupes by attachment-slot signature so field edits do not re-prompt.
|
|
735
|
+
*/
|
|
736
|
+
const AXEDocumentLayoutBuilderDialogContextChangedHookProvider = {
|
|
737
|
+
key: AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY,
|
|
738
|
+
priority: 50,
|
|
739
|
+
async execute(data) {
|
|
740
|
+
const integration = data.metadata ?? {};
|
|
741
|
+
if (!isDocumentAttachmentDialogIntegration(integration)) {
|
|
742
|
+
return data;
|
|
743
|
+
}
|
|
744
|
+
const prefill = inject(AXEDocumentMetadataAiPrefillService);
|
|
745
|
+
const orchestrator = inject(AXEDocumentMetadataAiFillOrchestrator);
|
|
746
|
+
if (!(await prefill.isMetadataOcrEnabled())) {
|
|
747
|
+
return data;
|
|
748
|
+
}
|
|
749
|
+
const ctx = data.getContext();
|
|
750
|
+
const sig = extractableAttachmentSlotsSignature(ctx);
|
|
751
|
+
if (!sig) {
|
|
752
|
+
return data;
|
|
753
|
+
}
|
|
754
|
+
const st = sessionState(data.sessionKey);
|
|
755
|
+
if (st.busy) {
|
|
756
|
+
return data;
|
|
757
|
+
}
|
|
758
|
+
if (st.lastProcessedSig === sig) {
|
|
759
|
+
return data;
|
|
760
|
+
}
|
|
761
|
+
const dialogTarget = {
|
|
762
|
+
context: () => data.getContext(),
|
|
763
|
+
patchContext: (partial) => data.patchContext(partial),
|
|
764
|
+
};
|
|
765
|
+
if (!prefill.willApplyMetadataAiFillFromDocuments(dialogTarget, integration)) {
|
|
766
|
+
return data;
|
|
767
|
+
}
|
|
768
|
+
st.busy = true;
|
|
769
|
+
try {
|
|
770
|
+
await orchestrator.confirmAndRun(dialogTarget, integration);
|
|
771
|
+
st.lastProcessedSig = sig;
|
|
772
|
+
}
|
|
773
|
+
catch {
|
|
774
|
+
st.lastProcessedSig = sig;
|
|
775
|
+
}
|
|
776
|
+
finally {
|
|
777
|
+
st.busy = false;
|
|
778
|
+
}
|
|
779
|
+
return data;
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
//#endregion
|
|
783
|
+
|
|
784
|
+
//#region ---- Imports ----
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region ---- Module ----
|
|
787
|
+
/**
|
|
788
|
+
* Optional AI + OCR metadata helpers for document flows.
|
|
789
|
+
* Register in the app next to {@link AXMDocumentManagementModule} and {@link AXMAiManagementModule}.
|
|
790
|
+
*/
|
|
791
|
+
class AXEAIDocumentExtensionModule {
|
|
792
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEAIDocumentExtensionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
793
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXEAIDocumentExtensionModule, imports: [AXMAiManagementModule, AXMDocumentManagementModule] }); }
|
|
794
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEAIDocumentExtensionModule, providers: [
|
|
795
|
+
{
|
|
796
|
+
provide: AXP_SETTING_DEFINITION_PROVIDER,
|
|
797
|
+
useClass: AXEDocumentAiSettingProvider,
|
|
798
|
+
multi: true,
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,
|
|
802
|
+
useValue: AXEDocumentLayoutBuilderDialogContextChangedHookProvider,
|
|
803
|
+
multi: true,
|
|
804
|
+
},
|
|
805
|
+
], imports: [AXMAiManagementModule, AXMDocumentManagementModule] }); }
|
|
806
|
+
}
|
|
807
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEAIDocumentExtensionModule, decorators: [{
|
|
808
|
+
type: NgModule,
|
|
809
|
+
args: [{
|
|
810
|
+
imports: [AXMAiManagementModule, AXMDocumentManagementModule],
|
|
811
|
+
providers: [
|
|
812
|
+
{
|
|
813
|
+
provide: AXP_SETTING_DEFINITION_PROVIDER,
|
|
814
|
+
useClass: AXEDocumentAiSettingProvider,
|
|
815
|
+
multi: true,
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,
|
|
819
|
+
useValue: AXEDocumentLayoutBuilderDialogContextChangedHookProvider,
|
|
820
|
+
multi: true,
|
|
821
|
+
},
|
|
822
|
+
],
|
|
823
|
+
}]
|
|
824
|
+
}] });
|
|
825
|
+
|
|
826
|
+
//#region ---- Hook contract ----
|
|
827
|
+
/**
|
|
828
|
+
* Hook key for document attachment dialog integrations (caller wires `AXPHookService.runAsync` / listeners).
|
|
829
|
+
* Payload carries the same dialog `context` object by reference so hooks can read or update nested paths
|
|
830
|
+
* (for example `file-0.metaDataForm`, `files`) before the dialog is shown.
|
|
831
|
+
*/
|
|
832
|
+
const AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY = 'document-management.attachment-dialog.configure';
|
|
833
|
+
//#endregion
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Generated bundle index. Do not edit.
|
|
837
|
+
*/
|
|
838
|
+
|
|
839
|
+
export { AXEAIDocumentExtensionModule, AXEDocumentAiSettingProvider, AXEDocumentLayoutBuilderDialogContextChangedHookProvider, AXEDocumentMetadataAiFillOrchestrator, AXEDocumentMetadataAiPrefillService, AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY, DOCUMENT_EXPIRATION_DATE_FIELD_NAME, collectMetadataFieldDescriptors, isAiExtractableFileListItem, mergeMetadataPrefill, normalizePrefillDateValue, parseMetadataMappingJson, splitMappedPrefill, withDocumentExpirationField };
|
|
840
|
+
//# sourceMappingURL=acorex-platform-extension-ai-document-client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-platform-extension-ai-document-client.mjs","sources":["../../../../libs/extensions/ai/document/client/src/lib/document-metadata-ocr.settings.provider.ts","../../../../libs/extensions/ai/document/client/src/lib/document-metadata-schema.util.ts","../../../../libs/extensions/ai/document/client/src/lib/document-metadata-ai-prefill.service.ts","../../../../libs/extensions/ai/document/client/src/lib/document-metadata-ai-fill.orchestrator.ts","../../../../libs/extensions/ai/document/client/src/lib/document-layout-builder-dialog-context-changed.hook.ts","../../../../libs/extensions/ai/document/client/src/lib/ai-document-extension.module.ts","../../../../libs/extensions/ai/document/client/src/lib/document-attachment-dialog.hooks.ts","../../../../libs/extensions/ai/document/client/src/acorex-platform-extension-ai-document-client.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { RootConfig } from '@acorex-platform/module-ai-management-client/contracts';\nimport { AXPSettingDefinitionProvider, AXPSettingDefinitionProviderContext } from '@acorex-platform/framework-client/common';\nimport { objectKeyValueTransforms } from '@acorex-platform/framework-client/core';\nimport { AXPWidgetsCatalog } from '@acorex-platform/framework-shared/core';\nimport { Injectable } from '@angular/core';\nimport { AXEDocumentMetadataOcrSettings } from '@acorex-platform/extension-ai-document-client/contracts';\n\n//#endregion\n\n//#region ---- Settings provider ----\n\nconst METADATA_OCR_I18N = '@extensions-ai-document:settings.metadata-ocr';\n\n/** Must match the settings group `name` from the document-management module (`AXPDocumentManagementSettingProvider`). */\nconst DOCUMENT_MANAGEMENT_SETTINGS_GROUP = 'document-management';\n\n@Injectable()\nexport class AXEDocumentAiSettingProvider implements AXPSettingDefinitionProvider {\n async provide(context: AXPSettingDefinitionProviderContext): Promise<void> {\n if (!context.hasGroup(DOCUMENT_MANAGEMENT_SETTINGS_GROUP)) {\n return;\n }\n\n const assistVisibility = `{{ context.eval('${AXEDocumentMetadataOcrSettings.Enabled}') }}`;\n\n const documentManagement = context.group(DOCUMENT_MANAGEMENT_SETTINGS_GROUP);\n if (!documentManagement) {\n return;\n }\n\n documentManagement\n .addSection('metadata-ocr', `${METADATA_OCR_I18N}.section-title`, `${METADATA_OCR_I18N}.section-description`)\n .addSetting({\n key: AXEDocumentMetadataOcrSettings.Enabled,\n title: `${METADATA_OCR_I18N}.enabled-title`,\n scope: 'T',\n overridableAt: [],\n isInherited: true,\n defaultValue: true,\n searchTagKeys: [`${METADATA_OCR_I18N}.enabled-search-tags`],\n widget: {\n type: AXPWidgetsCatalog.toggle,\n },\n description: `${METADATA_OCR_I18N}.enabled-description`,\n })\n .addSetting({\n key: AXEDocumentMetadataOcrSettings.AssistId,\n title: `${METADATA_OCR_I18N}.assist-id-title`,\n scope: 'T',\n overridableAt: [],\n isInherited: true,\n // Persist assist primary key (`id`); demo defaults come from settings seed/overrides.\n searchTagKeys: [`${METADATA_OCR_I18N}.assist-search-tags`],\n valueTransforms: objectKeyValueTransforms('id'),\n widget: {\n type: AXPWidgetsCatalog.lookup,\n options: {\n entity: `${RootConfig.module.name}.${RootConfig.entities.assist.name}`,\n look: 'select',\n multiple: false,\n textField: 'title',\n valueField: 'id',\n hasClearButton: true,\n visibility: assistVisibility,\n },\n },\n description: `${METADATA_OCR_I18N}.assist-id-description`,\n })\n .endSection();\n }\n}\n\n//#endregion\n","//#region ---- Imports ----\n\nimport type { AXMMetaDataBuilderValue, AXMMetaDataLocalizedString } from '@acorex-platform/module-data-management-client/contracts';\nimport { canAiExtractDocument } from '@acorex-platform/module-ai-management-client/contracts';\n\n//#endregion\n\n//#region ---- Types ----\n\nexport interface DocumentMetadataFieldDescriptor {\n name: string;\n title: AXMMetaDataLocalizedString;\n interfaceType: string;\n}\n\n//#endregion\n\n//#region ---- Constants ----\n\n/**\n * Synthetic mapping key for the document-level Expire Date control\n * (`file-*.expirationDate` when the document type has `enableExpiration`).\n * Not part of `metaDataForm`.\n */\nexport const DOCUMENT_EXPIRATION_DATE_FIELD_NAME = 'expirationDate';\n\n//#endregion\n\n//#region ---- Field collection ----\n\n/**\n * Flattens metadata selector value into field descriptors for LLM mapping prompts.\n */\nexport function collectMetadataFieldDescriptors(\n meta: AXMMetaDataBuilderValue | undefined | null,\n): DocumentMetadataFieldDescriptor[] {\n if (!meta?.groups?.length) {\n return [];\n }\n const out: DocumentMetadataFieldDescriptor[] = [];\n for (const g of meta.groups) {\n for (const f of g.fields ?? []) {\n out.push({\n name: f.name,\n title: f.title ?? f.name,\n interfaceType: f.interface,\n });\n }\n }\n return out;\n}\n\n/**\n * Appends the platform document expire-date field used by attachment dialogs.\n */\nexport function withDocumentExpirationField(\n fields: DocumentMetadataFieldDescriptor[],\n include: boolean,\n): DocumentMetadataFieldDescriptor[] {\n if (!include) {\n return fields;\n }\n if (fields.some((f) => f.name === DOCUMENT_EXPIRATION_DATE_FIELD_NAME)) {\n return fields;\n }\n return [\n ...fields,\n {\n name: DOCUMENT_EXPIRATION_DATE_FIELD_NAME,\n title: 'Expire Date',\n interfaceType: 'date-time-editor',\n },\n ];\n}\n\n//#endregion\n\n//#region ---- JSON parsing ----\n\n/**\n * Coerces common OCR / LLM date strings into YYYY-MM-DD (or leaves non-dates unchanged).\n */\nexport function normalizePrefillDateValue(value: unknown): unknown {\n if (value == null || value === '') {\n return value;\n }\n if (value instanceof Date && !Number.isNaN(value.getTime())) {\n return toIsoDateOnly(value);\n }\n if (typeof value !== 'string') {\n return value;\n }\n const raw = value.trim();\n if (!raw) {\n return value;\n }\n\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(raw);\n if (iso) {\n return `${iso[1]}-${iso[2]}-${iso[3]}`;\n }\n\n // US-style MM/DD/YYYY (common on driver licences).\n const us = /^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})$/.exec(raw);\n if (us) {\n const month = Number(us[1]);\n const day = Number(us[2]);\n const year = Number(us[3]);\n if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {\n return `${year}-${pad2(month)}-${pad2(day)}`;\n }\n }\n\n const parsed = new Date(raw);\n if (!Number.isNaN(parsed.getTime())) {\n return toIsoDateOnly(parsed);\n }\n return value;\n}\n\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : String(n);\n}\n\nfunction toIsoDateOnly(d: Date): string {\n return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;\n}\n\n/**\n * Normalizes date-like mapped values for date widgets; strips the synthetic expiration key from meta.\n */\nexport function splitMappedPrefill(\n parsed: Record<string, unknown>,\n fields: DocumentMetadataFieldDescriptor[],\n): { meta: Record<string, unknown>; expirationDate?: string } {\n const dateNames = new Set(\n fields\n .filter((f) => {\n const t = String(f.interfaceType ?? '').toLowerCase();\n return t.includes('date-time') || t.includes('date');\n })\n .map((f) => f.name),\n );\n\n const meta: Record<string, unknown> = {};\n let expirationDate: string | undefined;\n\n for (const [k, v] of Object.entries(parsed)) {\n const normalized = dateNames.has(k) ? normalizePrefillDateValue(v) : v;\n if (k === DOCUMENT_EXPIRATION_DATE_FIELD_NAME) {\n if (typeof normalized === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(normalized)) {\n expirationDate = normalized;\n } else if (normalized instanceof Date && !Number.isNaN(normalized.getTime())) {\n expirationDate = toIsoDateOnly(normalized);\n }\n continue;\n }\n meta[k] = normalized;\n }\n\n return expirationDate ? { meta, expirationDate } : { meta };\n}\n\n/**\n * Parses a JSON object from an assistant reply (strips optional ```json fences).\n */\nexport function parseMetadataMappingJson(text: string): Record<string, unknown> {\n const trimmed = text.trim();\n const fence = /^```(?:json)?\\s*([\\s\\S]*?)```$/m.exec(trimmed);\n const raw = fence ? fence[1]!.trim() : trimmed;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (parsed != null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n return {};\n}\n\n/**\n * Shallow merge: only fills keys that are empty in `existing`.\n */\nexport function mergeMetadataPrefill(\n existing: Record<string, unknown> | undefined,\n partial: Record<string, unknown>,\n): Record<string, unknown> {\n const base = { ...(existing ?? {}) };\n for (const [k, v] of Object.entries(partial)) {\n const cur = base[k];\n const empty = cur === undefined || cur === null || cur === '';\n if (empty && v !== undefined && v !== null && v !== '') {\n base[k] = v;\n }\n }\n return base;\n}\n\n//#endregion\n\n//#region ---- File kind ----\n\ntype FileListItemLike = {\n name?: string;\n type?: string;\n source?: { kind?: string; value?: unknown };\n};\n\n/**\n * Returns true when the file list item is supported for AI document text extraction.\n */\nexport function isAiExtractableFileListItem(item: FileListItemLike | undefined): boolean {\n if (!item) {\n return false;\n }\n const t = typeof item.type === 'string' ? item.type : '';\n const name = String(item.name ?? '');\n if (canAiExtractDocument(t, name)) {\n return true;\n }\n const src = item.source?.value;\n if (src instanceof Blob) {\n const blobType = typeof src.type === 'string' ? src.type : '';\n if (canAiExtractDocument(blobType, name)) {\n return true;\n }\n }\n if (src instanceof File) {\n if (canAiExtractDocument(src.type, src.name || name)) {\n return true;\n }\n }\n return false;\n}\n\n//#endregion\n","//#region ----- Imports -----\r\n\r\nimport { AXPFileListItem, AXPFileStorageStatus, runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';\r\nimport {\r\n AIMANAGEMENT_EXTRACT_DOCUMENT_TEXT_COMMAND_KEY,\r\n} from '@acorex-platform/module-ai-management-client/contracts';\r\nimport type { AXMMetaDataBuilderValue } from '@acorex-platform/module-data-management-client/contracts';\r\n\r\nimport {\r\n AXPDebugService,\r\n AXPFileStorageService,\r\n AXPSettingsService,\r\n extractRequestErrorInfo,\r\n} from '@acorex-platform/framework-client/common';\r\n\r\nimport { AXPCommandService } from '@acorex-platform/framework-client/runtime';\r\nimport { Injectable, inject } from '@angular/core';\r\nimport { AXEDocumentMetadataOcrSettings } from '@acorex-platform/extension-ai-document-client/contracts';\r\nimport {\r\n collectMetadataFieldDescriptors,\r\n isAiExtractableFileListItem,\r\n mergeMetadataPrefill,\r\n parseMetadataMappingJson,\r\n splitMappedPrefill,\r\n withDocumentExpirationField,\r\n} from './document-metadata-schema.util';\r\n\r\n/** Payload shape for ExtractDocumentText command `data`. */\r\ntype AiExtractCommandData = { text?: string; values?: Record<string, unknown> };\r\n\r\n//#endregion\r\n\r\n//#region ----- Constants -----\r\n\r\nconst DOCUMENT_MODULE_REF_TYPE = 'DocumentManagement.Document';\r\n\r\nconst SINGLE_DOCUMENT_PROGRESS_SCOPE = { documentIndex: 1, documentCount: 1 } as const;\r\n\r\n/** Phases surfaced to UI (loading dialog thinking text). */\r\nexport type AXEDocumentMetadataAiPrefillPhase =\r\n | 'preparing'\r\n | 'uploading'\r\n | 'extracting';\r\n\r\n/** Progress event for optional UI updates during metadata AI fill. */\r\nexport interface AXEDocumentMetadataAiPrefillProgressEvent {\r\n phase: AXEDocumentMetadataAiPrefillPhase;\r\n /** 1-based slot among documents processed in this run. */\r\n documentIndex: number;\r\n /** Total extractable document slots queued in this run. */\r\n documentCount: number;\r\n}\r\n\r\n//#endregion\r\n\r\n//#region ----- Local helpers -----\r\n\r\nconst OCR_FAILED_I18N = '@extensions-ai-document:widgets.metadata-ai-fill.ocr-failed';\r\n\r\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\r\n if (signal?.aborted) {\r\n throw new DOMException('Aborted', 'AbortError');\r\n }\r\n}\r\n\r\nfunction stringifyErrorDetails(details: unknown): string {\r\n if (typeof details === 'string' && details.trim()) {\r\n return details.trim();\r\n }\r\n if (details == null) {\r\n return '';\r\n }\r\n try {\r\n return JSON.stringify(details);\r\n } catch {\r\n return String(details);\r\n }\r\n}\r\n\r\n//#endregion\r\n\r\n//#region ----- Service -----\r\n\r\n/**\r\n * Runs AI document text extraction for metadata prefill (images, PDF, simple text files).\r\n * ExtractDocumentText command; when field names are passed it returns a key/value object.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class AXEDocumentMetadataAiPrefillService {\r\n //#region ----- Services & Dependencies -----\r\n\r\n private readonly fileStorage = inject(AXPFileStorageService);\r\n private readonly commandService = inject(AXPCommandService);\r\n private readonly settings = inject(AXPSettingsService);\r\n private readonly debug = inject(AXPDebugService);\r\n\r\n //#endregion\r\n\r\n /**\r\n * Whether OCR metadata prefill is enabled in tenant settings.\r\n * Defaults to true when the setting has never been stored (opt-out via settings UI).\r\n */\r\n async isMetadataOcrEnabled(): Promise<boolean> {\r\n const v = await this.settings.get<boolean>(AXEDocumentMetadataOcrSettings.Enabled);\r\n if (v == null) {\r\n return true;\r\n }\r\n return v;\r\n }\r\n\r\n /**\r\n * Whether {@link applyDialogContextMetadataAiFillFromDocuments} would extract at least one document\r\n * (caller can safely show an indeterminate loading dialog).\r\n */\r\n willApplyMetadataAiFillFromDocuments(\r\n dialogRef: {\r\n context: () => Record<string, unknown>;\r\n patchContext?: (partial: Record<string, unknown>) => void;\r\n },\r\n integration: Record<string, unknown>,\r\n ): boolean {\r\n if (!dialogRef.patchContext) {\r\n return false;\r\n }\r\n const documentType = integration['documentType'] as {\r\n metaDataList?: AXMMetaDataBuilderValue | null;\r\n enableExpiration?: boolean;\r\n } | undefined;\r\n const metaDataList = documentType?.metaDataList;\r\n const fields = withDocumentExpirationField(\r\n collectMetadataFieldDescriptors(metaDataList ?? undefined),\r\n !!documentType?.enableExpiration,\r\n );\r\n if (fields.length === 0) {\r\n return false;\r\n }\r\n const ctx = dialogRef.context();\r\n const files = ctx['files'] as Array<{ file?: unknown[] }> | undefined;\r\n const n = files?.length ?? 0;\r\n for (let i = 0; i < n; i++) {\r\n const slot = files![i]?.file;\r\n const first = Array.isArray(slot) ? (slot[0] as (AXPFileListItem & { type?: string }) | undefined) : undefined;\r\n if (first && isAiExtractableFileListItem(first)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Uploads a blob-backed file item to storage for vision APIs that require {@link fileId}.\r\n */\r\n async ensureStoredFileId(fileItem: AXPFileListItem & { type?: string }, folderId: string): Promise<string | null> {\r\n // Only trust an explicit storage fileId. Chooser items use a client UUID on `id` with `source.kind === 'blob'`.\r\n if (fileItem.source?.kind === 'fileId' && typeof fileItem.source.value === 'string') {\r\n const storedId = fileItem.source.value.trim();\r\n if (storedId) {\r\n return storedId;\r\n }\r\n }\r\n\r\n const file = this.resolveFileForUpload(fileItem);\r\n if (!file) {\r\n return null;\r\n }\r\n\r\n const refId = folderId?.trim() || 'document-metadata-ocr-prefill';\r\n const saved = await this.fileStorage.save({\r\n file,\r\n name: fileItem.name,\r\n refId,\r\n refType: DOCUMENT_MODULE_REF_TYPE,\r\n category: 'document',\r\n status: AXPFileStorageStatus.Temporary,\r\n });\r\n return saved.fileId?.trim() || null;\r\n }\r\n\r\n /**\r\n * OCR + field extraction in one vision call; returns merged metadata (only fills empty keys in {@link existingMeta}).\r\n */\r\n async prefillFromDocumentExtract(options: {\r\n fileItem: AXPFileListItem & { type?: string };\r\n folderId: string;\r\n metaDataList: AXMMetaDataBuilderValue | undefined | null;\r\n existingMeta: Record<string, unknown> | undefined;\r\n /** When true, also map OCR EXP/expiry into document `expirationDate`. */\r\n includeDocumentExpiration?: boolean;\r\n existingExpirationDate?: unknown;\r\n /** When set with {@link signalProgress}, scopes step copy to multi-document dialogs. */\r\n progressScope?: { documentIndex: number; documentCount: number };\r\n /** When aborted, throws {@link DOMException} with name `AbortError`. */\r\n signal?: AbortSignal;\r\n /** Optional UI wiring (loading dialog thinking lines). */\r\n signalProgress?: (event: AXEDocumentMetadataAiPrefillProgressEvent) => void;\r\n }): Promise<{ extractText: string; merged: Record<string, unknown>; expirationDate?: string }> {\r\n const signal = options.signal;\r\n const notify = (\r\n phase: AXEDocumentMetadataAiPrefillPhase,\r\n scope: { documentIndex: number; documentCount: number } = options.progressScope ??\r\n SINGLE_DOCUMENT_PROGRESS_SCOPE,\r\n ) => options.signalProgress?.({ phase, ...scope });\r\n\r\n throwIfAborted(signal);\r\n\r\n const fields = withDocumentExpirationField(\r\n collectMetadataFieldDescriptors(options.metaDataList ?? undefined),\r\n !!options.includeDocumentExpiration,\r\n );\r\n if (fields.length === 0) {\r\n return { extractText: '', merged: { ...(options.existingMeta ?? {}) } };\r\n }\r\n\r\n notify('uploading');\r\n const fileId = await this.ensureStoredFileId(options.fileItem, options.folderId);\r\n throwIfAborted(signal);\r\n if (!fileId) {\r\n throw new Error('Could not upload the file for document extraction.');\r\n }\r\n\r\n notify('extracting');\r\n const extractResult = await this.commandService.execute(AIMANAGEMENT_EXTRACT_DOCUMENT_TEXT_COMMAND_KEY, {\r\n fileId,\r\n fields: fields.map((field) => field.name),\r\n });\r\n throwIfAborted(signal);\r\n if (extractResult?.success === false) {\r\n throw this.extractCommandFailure(extractResult);\r\n }\r\n const extractData = runtimeCommandPayloadData(extractResult) as AiExtractCommandData | undefined;\r\n const extractText = typeof extractData?.text === 'string' ? extractData.text : '';\r\n const parsed =\r\n extractData?.values && typeof extractData.values === 'object' && !Array.isArray(extractData.values)\r\n ? extractData.values\r\n : parseMetadataMappingJson(extractText);\r\n if (Object.keys(parsed).length === 0 && !extractText.trim()) {\r\n throw this.extractCommandFailure(extractResult);\r\n }\r\n const { meta, expirationDate } = splitMappedPrefill(parsed, fields);\r\n const merged = mergeMetadataPrefill(options.existingMeta, meta);\r\n\r\n const existingExpEmpty =\r\n options.existingExpirationDate === undefined ||\r\n options.existingExpirationDate === null ||\r\n options.existingExpirationDate === '';\r\n return {\r\n extractText,\r\n merged,\r\n ...(existingExpEmpty && expirationDate ? { expirationDate } : {}),\r\n };\r\n }\r\n\r\n /**\r\n * For each extractable document slot in `context.files`, merges extraction + AI metadata into `file-{i}.metaDataForm`.\r\n */\r\n async applyDialogContextMetadataAiFillFromDocuments(\r\n dialogRef: {\r\n context: () => Record<string, unknown>;\r\n patchContext?: (partial: Record<string, unknown>) => void;\r\n },\r\n integration: Record<string, unknown>,\r\n options?: {\r\n signal?: AbortSignal;\r\n signalProgress?: (event: AXEDocumentMetadataAiPrefillProgressEvent) => void;\r\n },\r\n ): Promise<void> {\r\n if (!dialogRef.patchContext) {\r\n return;\r\n }\r\n const folderId = String(integration['parentId'] ?? '').trim();\r\n const documentType = integration['documentType'] as {\r\n metaDataList?: AXMMetaDataBuilderValue | null;\r\n enableExpiration?: boolean;\r\n } | undefined;\r\n const metaDataList = documentType?.metaDataList;\r\n const includeDocumentExpiration = !!documentType?.enableExpiration;\r\n const ctx = dialogRef.context();\r\n const files = ctx['files'] as Array<{ file?: unknown[] }> | undefined;\r\n const n = files?.length ?? 0;\r\n const patch: Record<string, unknown> = {};\r\n\r\n const documentIndices: number[] = [];\r\n for (let i = 0; i < n; i++) {\r\n const slot = files![i]?.file;\r\n const first = Array.isArray(slot) ? (slot[0] as (AXPFileListItem & { type?: string }) | undefined) : undefined;\r\n if (first && isAiExtractableFileListItem(first)) {\r\n documentIndices.push(i);\r\n }\r\n }\r\n const signal = options?.signal;\r\n const documentCount = documentIndices.length;\r\n if (documentCount > 0) {\r\n options?.signalProgress?.({\r\n phase: 'preparing',\r\n documentIndex: 1,\r\n documentCount,\r\n });\r\n throwIfAborted(signal);\r\n }\r\n\r\n let processed = 0;\r\n for (const i of documentIndices) {\r\n throwIfAborted(signal);\r\n processed += 1;\r\n const slot = files![i]?.file;\r\n const first = Array.isArray(slot) ? (slot[0] as (AXPFileListItem & { type?: string }) | undefined) : undefined;\r\n if (!first) {\r\n continue;\r\n }\r\n const fileBlock = ctx[`file-${i}`] as Record<string, unknown> | undefined;\r\n const existingMeta = fileBlock?.['metaDataForm'] as Record<string, unknown> | undefined;\r\n const { merged, expirationDate } = await this.prefillFromDocumentExtract({\r\n fileItem: first,\r\n folderId,\r\n metaDataList,\r\n existingMeta,\r\n includeDocumentExpiration,\r\n existingExpirationDate: fileBlock?.['expirationDate'],\r\n progressScope: { documentIndex: processed, documentCount },\r\n signal,\r\n signalProgress: options?.signalProgress,\r\n });\r\n patch[`file-${i}`] = {\r\n ...(typeof fileBlock === 'object' && fileBlock ? fileBlock : {}),\r\n metaDataForm: merged,\r\n ...(expirationDate ? { expirationDate } : {}),\r\n };\r\n }\r\n\r\n if (Object.keys(patch).length > 0) {\r\n dialogRef.patchContext(patch);\r\n }\r\n }\r\n\r\n //#region ----- Utility Methods -----\r\n\r\n private extractCommandFailure(extractResult: unknown): Error {\r\n const info = extractRequestErrorInfo(extractResult);\r\n const details = stringifyErrorDetails(info?.details);\r\n this.debug.error('ai-document', 'extract-document-text', 'ExtractDocumentText failed', () => ({\r\n message: info?.message,\r\n details: info?.details,\r\n }));\r\n if (this.debug.isDebugMode() && details) {\r\n return new Error(details);\r\n }\r\n const text = typeof info?.message === 'string' ? info.message.trim() : '';\r\n if (text.startsWith('@')) {\r\n return new Error(text);\r\n }\r\n return new Error(OCR_FAILED_I18N);\r\n }\r\n\r\n private resolveFileForUpload(item: AXPFileListItem & { type?: string }): File | null {\r\n const v = item.source?.value;\r\n if (v instanceof File) {\r\n return v;\r\n }\r\n if (item.source?.kind === 'blob' && v instanceof Blob) {\r\n return new File([v], item.name, { type: item.type ?? v.type ?? 'application/octet-stream' });\r\n }\r\n return null;\r\n }\r\n\r\n //#endregion\r\n}\r\n\r\n//#endregion\r\n","//#region ---- Imports ----\n\nimport { AXDialogService } from '@acorex/components/dialog';\nimport { AXLoadingDialogService } from '@acorex/components/loading-dialog';\nimport { AXTranslationService } from '@acorex/core/translation';\nimport { AXPDebugService } from '@acorex-platform/framework-client/common';\nimport { Injectable, inject } from '@angular/core';\nimport {\n AXEDocumentMetadataAiPrefillProgressEvent,\n AXEDocumentMetadataAiPrefillService,\n} from './document-metadata-ai-prefill.service';\n\n//#endregion\n\n//#region ---- Types ----\n\n/** Dialog surface that supports context patch (layout attachment dialog). */\nexport interface AXEDocumentMetadataAiFillDialogTarget {\n context: () => Record<string, unknown>;\n patchContext?: (partial: Record<string, unknown>) => void;\n}\n\nexport interface AXEDocumentMetadataAiFillRunResult {\n success: boolean;\n errorMessage?: string;\n /** User chose Skip on the loading dialog; treat as neutral (no failure toast) where applicable. */\n cancelled?: boolean;\n /** User declined OCR on the confirm dialog. */\n declined?: boolean;\n}\n\ntype MetadataAiProgressColor = 'primary' | 'danger' | 'success';\n\n/** Indeterminate loading dialog surface (see {@link AXLoadingDialogService.show}). */\ninterface MetadataAiLoadingDialogRef {\n close: () => void;\n setProgressText: (text: string) => void;\n setProgressColor: (color: MetadataAiProgressColor) => void;\n}\n\n//#endregion\n\nconst METADATA_AI_CONFIRM_I18N = '@extensions-ai-document:widgets.metadata-ai-fill.confirm';\nconst METADATA_AI_LOADING_I18N = '@extensions-ai-document:widgets.metadata-ai-fill.loading';\n\n/** Smoother dismiss after work completes. */\nconst LOADING_DIALOG_CLOSE_DELAY_MS = 400;\n\n//#region ---- Orchestrator ----\n\n/**\n * Shared OCR + AI metadata prefill UX: confirm after image choose, then indeterminate loading dialog.\n */\n@Injectable({ providedIn: 'root' })\nexport class AXEDocumentMetadataAiFillOrchestrator {\n //#region ---- Dependencies ----\n\n private readonly prefill = inject(AXEDocumentMetadataAiPrefillService);\n private readonly dialogService = inject(AXDialogService);\n private readonly loadingDialog = inject(AXLoadingDialogService);\n private readonly translation = inject(AXTranslationService);\n private readonly debug = inject(AXPDebugService);\n\n //#endregion\n\n //#region ---- Public API ----\n\n /**\n * Asks whether to run OCR; on Yes runs {@link run}, on No returns {@link AXEDocumentMetadataAiFillRunResult.declined}.\n */\n async confirmAndRun(\n dialogTarget: AXEDocumentMetadataAiFillDialogTarget,\n integration: Record<string, unknown>,\n ): Promise<AXEDocumentMetadataAiFillRunResult> {\n if (!this.prefill.willApplyMetadataAiFillFromDocuments(dialogTarget, integration)) {\n return { success: true };\n }\n\n const [title, message] = await Promise.all([\n this.translation.translateAsync(`${METADATA_AI_CONFIRM_I18N}.title`),\n this.translation.translateAsync(`${METADATA_AI_CONFIRM_I18N}.message`),\n ]);\n const dialogResult = await this.dialogService.confirm(title, message, 'primary', 'horizontal', false, 'cancel');\n if (!dialogResult.result) {\n return { success: true, declined: true };\n }\n\n return this.run(dialogTarget, integration);\n }\n\n /**\n * Runs metadata AI fill when eligible; shows an indeterminate loading dialog while work is in progress.\n */\n async run(\n dialogTarget: AXEDocumentMetadataAiFillDialogTarget,\n integration: Record<string, unknown>,\n ): Promise<AXEDocumentMetadataAiFillRunResult> {\n const showLoading = this.prefill.willApplyMetadataAiFillFromDocuments(dialogTarget, integration);\n\n const abortController = new AbortController();\n let loadingDismissedEarly = false;\n\n let progressDialog: MetadataAiLoadingDialogRef | undefined;\n\n if (showLoading) {\n const [title, initial, skipLabel] = await Promise.all([\n this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.title`),\n this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.initial`),\n this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.skip-button`),\n ]);\n progressDialog = this.loadingDialog.show({\n title,\n mode: 'indeterminate',\n progressColor: 'primary',\n text: initial,\n buttons: [\n {\n text: skipLabel,\n onClick: () => {\n loadingDismissedEarly = true;\n abortController.abort();\n progressDialog?.close();\n },\n },\n ],\n }) as MetadataAiLoadingDialogRef;\n }\n\n try {\n await this.prefill.applyDialogContextMetadataAiFillFromDocuments(dialogTarget, integration, {\n signal: showLoading ? abortController.signal : undefined,\n signalProgress: (event) => {\n if (!progressDialog) {\n return;\n }\n void this.applyProgressText(progressDialog, event);\n },\n });\n if (progressDialog && !loadingDismissedEarly) {\n await this.markProgressComplete(progressDialog);\n }\n return { success: true };\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { success: false, cancelled: true };\n }\n const errorMessage = await this.resolveFailureMessage(err);\n if (progressDialog && !loadingDismissedEarly) {\n try {\n progressDialog.setProgressColor('danger');\n progressDialog.setProgressText(errorMessage);\n } catch {\n // Ignore dialog update failures.\n }\n }\n if (this.debug.isDebugMode() && progressDialog && !loadingDismissedEarly) {\n loadingDismissedEarly = true;\n }\n return { success: false, errorMessage };\n } finally {\n if (progressDialog !== undefined && !loadingDismissedEarly) {\n const dlg = progressDialog;\n setTimeout(() => dlg.close(), LOADING_DIALOG_CLOSE_DELAY_MS);\n }\n }\n }\n\n //#endregion\n\n //#region ---- Progress ----\n\n private async applyProgressText(\n dialog: MetadataAiLoadingDialogRef,\n event: AXEDocumentMetadataAiPrefillProgressEvent,\n ): Promise<void> {\n try {\n dialog.setProgressColor('primary');\n const line = await this.formatProgressLine(event);\n dialog.setProgressText(line);\n } catch {\n // Ignore translation / dialog failures; keep the previous UI state.\n }\n }\n\n private async markProgressComplete(dialog: MetadataAiLoadingDialogRef): Promise<void> {\n try {\n dialog.setProgressColor('success');\n dialog.setProgressText(await this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.complete`));\n } catch {\n dialog.setProgressColor('success');\n }\n }\n\n private async resolveFailureMessage(err: unknown): Promise<string> {\n const raw = err instanceof Error ? err.message.trim() : '';\n if (this.debug.isDebugMode() && raw && !raw.startsWith('@')) {\n const prefix = await this.translation.translateAsync(\n '@extensions-ai-document:widgets.metadata-ai-fill.ocr-failed-prefix',\n );\n return `${prefix} ${raw}`;\n }\n const key = raw.startsWith('@') ? raw : '@extensions-ai-document:widgets.metadata-ai-fill.ocr-failed';\n return this.translation.translateAsync(key);\n }\n\n private async formatProgressLine(event: AXEDocumentMetadataAiPrefillProgressEvent): Promise<string> {\n const phaseKey = `${METADATA_AI_LOADING_I18N}.thinking.${event.phase}`;\n const phaseText = await this.translation.translateAsync(phaseKey);\n if (event.documentCount > 1) {\n return this.translation.translateAsync(`${METADATA_AI_LOADING_I18N}.step-prefix`, {\n params: {\n current: event.documentIndex,\n total: event.documentCount,\n phase: phaseText,\n },\n });\n }\n return phaseText;\n }\n\n //#endregion\n}\n\n//#endregion\n","//#region ---- Imports ----\n\nimport type { AXPDistributedEventListenerProvider } from '@acorex-platform/framework-client/core';\nimport {\n AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY,\n type AXPLayoutBuilderDialogContextChangedPayload,\n} from '@acorex-platform/framework-client/layout/builder';\nimport { inject } from '@angular/core';\nimport { AXEDocumentMetadataAiFillOrchestrator } from './document-metadata-ai-fill.orchestrator';\nimport { AXEDocumentMetadataAiPrefillService } from './document-metadata-ai-prefill.service';\nimport { isAiExtractableFileListItem } from './document-metadata-schema.util';\n\n//#endregion\n\n//#region ---- Local state (per-popup session keys from dialog renderer) ----\n\ntype AiPromptSessionState = {\n busy?: boolean;\n lastProcessedSig?: string;\n};\n\nconst promptSessions = new Map<string, AiPromptSessionState>();\n\nfunction sessionState(sessionKey: string): AiPromptSessionState {\n let s = promptSessions.get(sessionKey);\n if (!s) {\n s = {};\n promptSessions.set(sessionKey, s);\n }\n return s;\n}\n\n//#endregion\n\n//#region ---- Helpers ----\n\n/**\n * Document Management attachment dialogs expose this metadata so extensions can scope integration-only hooks.\n */\nfunction isDocumentAttachmentDialogIntegration(metadata: Record<string, unknown>): boolean {\n return metadata['showMeta'] === true && metadata['hasMetaFields'] === true && metadata['documentType'] != null;\n}\n\n/**\n * Stable fingerprint for extractable document slots — ignores metadata field edits so we do not re-prompt on every keystroke.\n */\nfunction extractableAttachmentSlotsSignature(context: Record<string, unknown>): string {\n const files = context['files'] as Array<{ file?: unknown[] }> | undefined;\n if (!Array.isArray(files) || files.length === 0) {\n return '';\n }\n const parts: string[] = [];\n for (let i = 0; i < files.length; i++) {\n const slot = files[i]?.file;\n const first = Array.isArray(slot) ? (slot[0] as Record<string, unknown> | undefined) : undefined;\n if (!first || !isAiExtractableFileListItem(first as { name?: string; type?: string })) {\n continue;\n }\n const name = String(first['name'] ?? '');\n const size = typeof first['size'] === 'number' ? first['size'] : '';\n const mime = String(first['type'] ?? '');\n const src = first['source'] as { kind?: string; value?: unknown } | undefined;\n const kind = String(src?.kind ?? '');\n let sourceFingerprint = '';\n if (kind === 'fileId' && typeof src?.value === 'string') {\n sourceFingerprint = `fileId:${src.value}`;\n } else if (kind === 'blob' && src?.value instanceof Blob) {\n sourceFingerprint = `blob:${src.value.size}:${src.value.type}`;\n } else if (src?.value instanceof File) {\n const f = src.value;\n sourceFingerprint = `file:${f.size}:${f.type}`;\n } else {\n sourceFingerprint = `${kind}:${typeof src?.value}`;\n }\n parts.push(`${i}:${name}:${size}:${mime}:${sourceFingerprint}`);\n }\n return parts.join('|');\n}\n\n//#endregion\n\n//#region ---- Hook provider ----\n\n/**\n * When document extraction is enabled and the attachment dialog has metadata + extractable files, prompts the user\n * after files appear. Yes → loading dialog + AI prefill; No → leave metadata form empty for manual entry.\n * Dedupes by attachment-slot signature so field edits do not re-prompt.\n */\nexport const AXEDocumentLayoutBuilderDialogContextChangedHookProvider: AXPDistributedEventListenerProvider<\n AXPLayoutBuilderDialogContextChangedPayload,\n AXPLayoutBuilderDialogContextChangedPayload\n> = {\n key: AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY,\n priority: 50,\n async execute(\n data: AXPLayoutBuilderDialogContextChangedPayload,\n ): Promise<AXPLayoutBuilderDialogContextChangedPayload> {\n const integration = data.metadata ?? {};\n\n if (!isDocumentAttachmentDialogIntegration(integration)) {\n return data;\n }\n\n const prefill = inject(AXEDocumentMetadataAiPrefillService);\n const orchestrator = inject(AXEDocumentMetadataAiFillOrchestrator);\n\n if (!(await prefill.isMetadataOcrEnabled())) {\n return data;\n }\n\n const ctx = data.getContext();\n const sig = extractableAttachmentSlotsSignature(ctx);\n if (!sig) {\n return data;\n }\n\n const st = sessionState(data.sessionKey);\n if (st.busy) {\n return data;\n }\n if (st.lastProcessedSig === sig) {\n return data;\n }\n\n const dialogTarget = {\n context: () => data.getContext(),\n patchContext: (partial: Record<string, unknown>) => data.patchContext(partial),\n };\n\n if (!prefill.willApplyMetadataAiFillFromDocuments(dialogTarget, integration)) {\n return data;\n }\n\n st.busy = true;\n try {\n await orchestrator.confirmAndRun(dialogTarget, integration);\n st.lastProcessedSig = sig;\n } catch {\n st.lastProcessedSig = sig;\n } finally {\n st.busy = false;\n }\n\n return data;\n },\n};\n\n//#endregion\n","//#region ---- Imports ----\n\nimport { NgModule } from '@angular/core';\nimport { AXMAiManagementModule } from '@acorex-platform/module-ai-management-client';\nimport { AXMDocumentManagementModule } from '@acorex-platform/module-document-management-client';\nimport { AXP_SETTING_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/common';\nimport { AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER } from '@acorex-platform/framework-client/core';\n\nimport { AXEDocumentAiSettingProvider } from './document-metadata-ocr.settings.provider';\nimport { AXEDocumentLayoutBuilderDialogContextChangedHookProvider } from './document-layout-builder-dialog-context-changed.hook';\n\n//#endregion\n\n//#region ---- Module ----\n\n/**\n * Optional AI + OCR metadata helpers for document flows.\n * Register in the app next to {@link AXMDocumentManagementModule} and {@link AXMAiManagementModule}.\n */\n@NgModule({\n imports: [AXMAiManagementModule, AXMDocumentManagementModule],\n providers: [\n {\n provide: AXP_SETTING_DEFINITION_PROVIDER,\n useClass: AXEDocumentAiSettingProvider,\n multi: true,\n },\n {\n provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,\n useValue: AXEDocumentLayoutBuilderDialogContextChangedHookProvider,\n multi: true,\n },\n ],\n})\nexport class AXEAIDocumentExtensionModule {}\n\n//#endregion\n","//#region ---- Hook contract ----\n\n/**\n * Hook key for document attachment dialog integrations (caller wires `AXPHookService.runAsync` / listeners).\n * Payload carries the same dialog `context` object by reference so hooks can read or update nested paths\n * (for example `file-0.metaDataForm`, `files`) before the dialog is shown.\n */\nexport const AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY = 'document-management.attachment-dialog.configure';\n\n/**\n * Eligibility flags supplied by the caller (e.g. Document Management).\n */\nexport interface AXEDocumentAttachmentDialogEligibility {\n showMeta: boolean;\n hasMetaFields: boolean;\n hasImage: boolean;\n parentId: string;\n documentType: unknown;\n}\n\n/**\n * Payload for {@link AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY}.\n *\n * **Context by reference:** `context` must be the object that will be passed to the dialog (e.g. `setContext(context)`).\n * Hook providers may mutate or shallow-merge into this object so updates are visible when the dialog opens.\n */\nexport interface AXEDocumentAttachmentDialogHookPayload {\n context: Record<string, unknown>;\n eligibility: AXEDocumentAttachmentDialogEligibility;\n}\n\n//#endregion\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@acorex-platform/extension-ai-document-client",
|
|
3
|
+
"version": "0.0.21",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@angular/common": "^21.0.0",
|
|
7
|
+
"@angular/core": "^21.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"tslib": "^2.3.0"
|
|
11
|
+
},
|
|
12
|
+
"module": "fesm2022/acorex-platform-extension-ai-document-client.mjs",
|
|
13
|
+
"typings": "types/acorex-platform-extension-ai-document-client.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
"./package.json": {
|
|
16
|
+
"default": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./types/acorex-platform-extension-ai-document-client.d.ts",
|
|
20
|
+
"default": "./fesm2022/acorex-platform-extension-ai-document-client.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./contracts": {
|
|
23
|
+
"types": "./types/acorex-platform-extension-ai-document-client-contracts.d.ts",
|
|
24
|
+
"default": "./fesm2022/acorex-platform-extension-ai-document-client-contracts.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"type": "module"
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant settings for optional OCR-based metadata prefill on image uploads (AI Document extension).
|
|
3
|
+
*/
|
|
4
|
+
declare enum AXEDocumentMetadataOcrSettings {
|
|
5
|
+
/** When true, eligible attachment dialogs may offer OCR after an image is chosen. */
|
|
6
|
+
Enabled = "AiDocument:Setting:MetadataOcr.Enabled",
|
|
7
|
+
/**
|
|
8
|
+
* Optional assist primary key (`Assist.id`). Metadata OCR uses ExtractDocumentText (assist chat model).
|
|
9
|
+
*/
|
|
10
|
+
AssistId = "AiDocument:Setting:MetadataOcr.AssistId"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { AXEDocumentMetadataOcrSettings };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import * as i1 from '@acorex-platform/module-ai-management-client';
|
|
3
|
+
import * as i2 from '@acorex-platform/module-document-management-client';
|
|
4
|
+
import { AXPFileListItem } from '@acorex-platform/framework-shared/core';
|
|
5
|
+
import { AXMMetaDataBuilderValue, AXMMetaDataLocalizedString } from '@acorex-platform/module-data-management-client/contracts';
|
|
6
|
+
import { AXPSettingDefinitionProvider, AXPSettingDefinitionProviderContext } from '@acorex-platform/framework-client/common';
|
|
7
|
+
import { AXPDistributedEventListenerProvider } from '@acorex-platform/framework-client/core';
|
|
8
|
+
import { AXPLayoutBuilderDialogContextChangedPayload } from '@acorex-platform/framework-client/layout/builder';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Optional AI + OCR metadata helpers for document flows.
|
|
12
|
+
* Register in the app next to {@link AXMDocumentManagementModule} and {@link AXMAiManagementModule}.
|
|
13
|
+
*/
|
|
14
|
+
declare class AXEAIDocumentExtensionModule {
|
|
15
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AXEAIDocumentExtensionModule, never>;
|
|
16
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<AXEAIDocumentExtensionModule, never, [typeof i1.AXMAiManagementModule, typeof i2.AXMDocumentManagementModule], never>;
|
|
17
|
+
static ɵinj: i0.ɵɵInjectorDeclaration<AXEAIDocumentExtensionModule>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Phases surfaced to UI (loading dialog thinking text). */
|
|
21
|
+
type AXEDocumentMetadataAiPrefillPhase = 'preparing' | 'uploading' | 'extracting';
|
|
22
|
+
/** Progress event for optional UI updates during metadata AI fill. */
|
|
23
|
+
interface AXEDocumentMetadataAiPrefillProgressEvent {
|
|
24
|
+
phase: AXEDocumentMetadataAiPrefillPhase;
|
|
25
|
+
/** 1-based slot among documents processed in this run. */
|
|
26
|
+
documentIndex: number;
|
|
27
|
+
/** Total extractable document slots queued in this run. */
|
|
28
|
+
documentCount: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Runs AI document text extraction for metadata prefill (images, PDF, simple text files).
|
|
32
|
+
* ExtractDocumentText command; when field names are passed it returns a key/value object.
|
|
33
|
+
*/
|
|
34
|
+
declare class AXEDocumentMetadataAiPrefillService {
|
|
35
|
+
private readonly fileStorage;
|
|
36
|
+
private readonly commandService;
|
|
37
|
+
private readonly settings;
|
|
38
|
+
private readonly debug;
|
|
39
|
+
/**
|
|
40
|
+
* Whether OCR metadata prefill is enabled in tenant settings.
|
|
41
|
+
* Defaults to true when the setting has never been stored (opt-out via settings UI).
|
|
42
|
+
*/
|
|
43
|
+
isMetadataOcrEnabled(): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Whether {@link applyDialogContextMetadataAiFillFromDocuments} would extract at least one document
|
|
46
|
+
* (caller can safely show an indeterminate loading dialog).
|
|
47
|
+
*/
|
|
48
|
+
willApplyMetadataAiFillFromDocuments(dialogRef: {
|
|
49
|
+
context: () => Record<string, unknown>;
|
|
50
|
+
patchContext?: (partial: Record<string, unknown>) => void;
|
|
51
|
+
}, integration: Record<string, unknown>): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Uploads a blob-backed file item to storage for vision APIs that require {@link fileId}.
|
|
54
|
+
*/
|
|
55
|
+
ensureStoredFileId(fileItem: AXPFileListItem & {
|
|
56
|
+
type?: string;
|
|
57
|
+
}, folderId: string): Promise<string | null>;
|
|
58
|
+
/**
|
|
59
|
+
* OCR + field extraction in one vision call; returns merged metadata (only fills empty keys in {@link existingMeta}).
|
|
60
|
+
*/
|
|
61
|
+
prefillFromDocumentExtract(options: {
|
|
62
|
+
fileItem: AXPFileListItem & {
|
|
63
|
+
type?: string;
|
|
64
|
+
};
|
|
65
|
+
folderId: string;
|
|
66
|
+
metaDataList: AXMMetaDataBuilderValue | undefined | null;
|
|
67
|
+
existingMeta: Record<string, unknown> | undefined;
|
|
68
|
+
/** When true, also map OCR EXP/expiry into document `expirationDate`. */
|
|
69
|
+
includeDocumentExpiration?: boolean;
|
|
70
|
+
existingExpirationDate?: unknown;
|
|
71
|
+
/** When set with {@link signalProgress}, scopes step copy to multi-document dialogs. */
|
|
72
|
+
progressScope?: {
|
|
73
|
+
documentIndex: number;
|
|
74
|
+
documentCount: number;
|
|
75
|
+
};
|
|
76
|
+
/** When aborted, throws {@link DOMException} with name `AbortError`. */
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
/** Optional UI wiring (loading dialog thinking lines). */
|
|
79
|
+
signalProgress?: (event: AXEDocumentMetadataAiPrefillProgressEvent) => void;
|
|
80
|
+
}): Promise<{
|
|
81
|
+
extractText: string;
|
|
82
|
+
merged: Record<string, unknown>;
|
|
83
|
+
expirationDate?: string;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* For each extractable document slot in `context.files`, merges extraction + AI metadata into `file-{i}.metaDataForm`.
|
|
87
|
+
*/
|
|
88
|
+
applyDialogContextMetadataAiFillFromDocuments(dialogRef: {
|
|
89
|
+
context: () => Record<string, unknown>;
|
|
90
|
+
patchContext?: (partial: Record<string, unknown>) => void;
|
|
91
|
+
}, integration: Record<string, unknown>, options?: {
|
|
92
|
+
signal?: AbortSignal;
|
|
93
|
+
signalProgress?: (event: AXEDocumentMetadataAiPrefillProgressEvent) => void;
|
|
94
|
+
}): Promise<void>;
|
|
95
|
+
private extractCommandFailure;
|
|
96
|
+
private resolveFileForUpload;
|
|
97
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AXEDocumentMetadataAiPrefillService, never>;
|
|
98
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AXEDocumentMetadataAiPrefillService>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface DocumentMetadataFieldDescriptor {
|
|
102
|
+
name: string;
|
|
103
|
+
title: AXMMetaDataLocalizedString;
|
|
104
|
+
interfaceType: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Synthetic mapping key for the document-level Expire Date control
|
|
108
|
+
* (`file-*.expirationDate` when the document type has `enableExpiration`).
|
|
109
|
+
* Not part of `metaDataForm`.
|
|
110
|
+
*/
|
|
111
|
+
declare const DOCUMENT_EXPIRATION_DATE_FIELD_NAME = "expirationDate";
|
|
112
|
+
/**
|
|
113
|
+
* Flattens metadata selector value into field descriptors for LLM mapping prompts.
|
|
114
|
+
*/
|
|
115
|
+
declare function collectMetadataFieldDescriptors(meta: AXMMetaDataBuilderValue | undefined | null): DocumentMetadataFieldDescriptor[];
|
|
116
|
+
/**
|
|
117
|
+
* Appends the platform document expire-date field used by attachment dialogs.
|
|
118
|
+
*/
|
|
119
|
+
declare function withDocumentExpirationField(fields: DocumentMetadataFieldDescriptor[], include: boolean): DocumentMetadataFieldDescriptor[];
|
|
120
|
+
/**
|
|
121
|
+
* Coerces common OCR / LLM date strings into YYYY-MM-DD (or leaves non-dates unchanged).
|
|
122
|
+
*/
|
|
123
|
+
declare function normalizePrefillDateValue(value: unknown): unknown;
|
|
124
|
+
/**
|
|
125
|
+
* Normalizes date-like mapped values for date widgets; strips the synthetic expiration key from meta.
|
|
126
|
+
*/
|
|
127
|
+
declare function splitMappedPrefill(parsed: Record<string, unknown>, fields: DocumentMetadataFieldDescriptor[]): {
|
|
128
|
+
meta: Record<string, unknown>;
|
|
129
|
+
expirationDate?: string;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Parses a JSON object from an assistant reply (strips optional ```json fences).
|
|
133
|
+
*/
|
|
134
|
+
declare function parseMetadataMappingJson(text: string): Record<string, unknown>;
|
|
135
|
+
/**
|
|
136
|
+
* Shallow merge: only fills keys that are empty in `existing`.
|
|
137
|
+
*/
|
|
138
|
+
declare function mergeMetadataPrefill(existing: Record<string, unknown> | undefined, partial: Record<string, unknown>): Record<string, unknown>;
|
|
139
|
+
type FileListItemLike = {
|
|
140
|
+
name?: string;
|
|
141
|
+
type?: string;
|
|
142
|
+
source?: {
|
|
143
|
+
kind?: string;
|
|
144
|
+
value?: unknown;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Returns true when the file list item is supported for AI document text extraction.
|
|
149
|
+
*/
|
|
150
|
+
declare function isAiExtractableFileListItem(item: FileListItemLike | undefined): boolean;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Hook key for document attachment dialog integrations (caller wires `AXPHookService.runAsync` / listeners).
|
|
154
|
+
* Payload carries the same dialog `context` object by reference so hooks can read or update nested paths
|
|
155
|
+
* (for example `file-0.metaDataForm`, `files`) before the dialog is shown.
|
|
156
|
+
*/
|
|
157
|
+
declare const AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY = "document-management.attachment-dialog.configure";
|
|
158
|
+
/**
|
|
159
|
+
* Eligibility flags supplied by the caller (e.g. Document Management).
|
|
160
|
+
*/
|
|
161
|
+
interface AXEDocumentAttachmentDialogEligibility {
|
|
162
|
+
showMeta: boolean;
|
|
163
|
+
hasMetaFields: boolean;
|
|
164
|
+
hasImage: boolean;
|
|
165
|
+
parentId: string;
|
|
166
|
+
documentType: unknown;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Payload for {@link AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY}.
|
|
170
|
+
*
|
|
171
|
+
* **Context by reference:** `context` must be the object that will be passed to the dialog (e.g. `setContext(context)`).
|
|
172
|
+
* Hook providers may mutate or shallow-merge into this object so updates are visible when the dialog opens.
|
|
173
|
+
*/
|
|
174
|
+
interface AXEDocumentAttachmentDialogHookPayload {
|
|
175
|
+
context: Record<string, unknown>;
|
|
176
|
+
eligibility: AXEDocumentAttachmentDialogEligibility;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
declare class AXEDocumentAiSettingProvider implements AXPSettingDefinitionProvider {
|
|
180
|
+
provide(context: AXPSettingDefinitionProviderContext): Promise<void>;
|
|
181
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AXEDocumentAiSettingProvider, never>;
|
|
182
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AXEDocumentAiSettingProvider>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Dialog surface that supports context patch (layout attachment dialog). */
|
|
186
|
+
interface AXEDocumentMetadataAiFillDialogTarget {
|
|
187
|
+
context: () => Record<string, unknown>;
|
|
188
|
+
patchContext?: (partial: Record<string, unknown>) => void;
|
|
189
|
+
}
|
|
190
|
+
interface AXEDocumentMetadataAiFillRunResult {
|
|
191
|
+
success: boolean;
|
|
192
|
+
errorMessage?: string;
|
|
193
|
+
/** User chose Skip on the loading dialog; treat as neutral (no failure toast) where applicable. */
|
|
194
|
+
cancelled?: boolean;
|
|
195
|
+
/** User declined OCR on the confirm dialog. */
|
|
196
|
+
declined?: boolean;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Shared OCR + AI metadata prefill UX: confirm after image choose, then indeterminate loading dialog.
|
|
200
|
+
*/
|
|
201
|
+
declare class AXEDocumentMetadataAiFillOrchestrator {
|
|
202
|
+
private readonly prefill;
|
|
203
|
+
private readonly dialogService;
|
|
204
|
+
private readonly loadingDialog;
|
|
205
|
+
private readonly translation;
|
|
206
|
+
private readonly debug;
|
|
207
|
+
/**
|
|
208
|
+
* Asks whether to run OCR; on Yes runs {@link run}, on No returns {@link AXEDocumentMetadataAiFillRunResult.declined}.
|
|
209
|
+
*/
|
|
210
|
+
confirmAndRun(dialogTarget: AXEDocumentMetadataAiFillDialogTarget, integration: Record<string, unknown>): Promise<AXEDocumentMetadataAiFillRunResult>;
|
|
211
|
+
/**
|
|
212
|
+
* Runs metadata AI fill when eligible; shows an indeterminate loading dialog while work is in progress.
|
|
213
|
+
*/
|
|
214
|
+
run(dialogTarget: AXEDocumentMetadataAiFillDialogTarget, integration: Record<string, unknown>): Promise<AXEDocumentMetadataAiFillRunResult>;
|
|
215
|
+
private applyProgressText;
|
|
216
|
+
private markProgressComplete;
|
|
217
|
+
private resolveFailureMessage;
|
|
218
|
+
private formatProgressLine;
|
|
219
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AXEDocumentMetadataAiFillOrchestrator, never>;
|
|
220
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AXEDocumentMetadataAiFillOrchestrator>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* When document extraction is enabled and the attachment dialog has metadata + extractable files, prompts the user
|
|
225
|
+
* after files appear. Yes → loading dialog + AI prefill; No → leave metadata form empty for manual entry.
|
|
226
|
+
* Dedupes by attachment-slot signature so field edits do not re-prompt.
|
|
227
|
+
*/
|
|
228
|
+
declare const AXEDocumentLayoutBuilderDialogContextChangedHookProvider: AXPDistributedEventListenerProvider<AXPLayoutBuilderDialogContextChangedPayload, AXPLayoutBuilderDialogContextChangedPayload>;
|
|
229
|
+
|
|
230
|
+
export { AXEAIDocumentExtensionModule, AXEDocumentAiSettingProvider, AXEDocumentLayoutBuilderDialogContextChangedHookProvider, AXEDocumentMetadataAiFillOrchestrator, AXEDocumentMetadataAiPrefillService, AXE_DOCUMENT_ATTACHMENT_DIALOG_HOOK_KEY, DOCUMENT_EXPIRATION_DATE_FIELD_NAME, collectMetadataFieldDescriptors, isAiExtractableFileListItem, mergeMetadataPrefill, normalizePrefillDateValue, parseMetadataMappingJson, splitMappedPrefill, withDocumentExpirationField };
|
|
231
|
+
export type { AXEDocumentAttachmentDialogEligibility, AXEDocumentAttachmentDialogHookPayload, AXEDocumentMetadataAiFillDialogTarget, AXEDocumentMetadataAiFillRunResult, AXEDocumentMetadataAiPrefillPhase, AXEDocumentMetadataAiPrefillProgressEvent, DocumentMetadataFieldDescriptor };
|