@tstdl/base 0.92.86 → 0.92.88
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/ai/ai-file.service.js +8 -13
- package/ai/ai.service.d.ts +3 -3
- package/ai/ai.service.js +24 -11
- package/ai/types.d.ts +4 -3
- package/api/server/gateway.js +1 -1
- package/authentication/server/authentication-ancillary.service.d.ts +6 -4
- package/authentication/server/authentication-ancillary.service.js +5 -5
- package/context/context.d.ts +1 -9
- package/context/context.js +8 -5
- package/document-management/api/document-management.api.d.ts +48 -16
- package/document-management/models/document-category.model.d.ts +1 -1
- package/document-management/models/document-category.model.js +2 -0
- package/document-management/models/document-collection-document.model.js +7 -3
- package/document-management/models/document-property-value.model.d.ts +13 -14
- package/document-management/models/document-property-value.model.js +60 -27
- package/document-management/models/document-property.model.d.ts +2 -0
- package/document-management/models/document-property.model.js +4 -1
- package/document-management/models/document-request-assignment-task-collection.model.d.ts +7 -0
- package/document-management/models/{document-index.model.js → document-request-assignment-task-collection.model.js} +16 -16
- package/document-management/models/document-request-assignment-task.model.d.ts +14 -0
- package/document-management/models/document-request-assignment-task.model.js +72 -0
- package/document-management/models/document-request-collection.model.d.ts +1 -0
- package/document-management/models/document-request-collection.model.js +7 -3
- package/document-management/models/document-request-file.model.d.ts +6 -1
- package/document-management/models/document-request-file.model.js +27 -2
- package/document-management/models/document-request.model.d.ts +1 -0
- package/document-management/models/document-requests-template.js +2 -0
- package/document-management/models/document-type-property.model.js +7 -3
- package/document-management/models/document-type.model.d.ts +1 -1
- package/document-management/models/document-type.model.js +7 -3
- package/document-management/models/document.model.d.ts +4 -1
- package/document-management/models/document.model.js +19 -4
- package/document-management/models/index.d.ts +2 -0
- package/document-management/models/index.js +2 -0
- package/document-management/models/service-models/document.service-model.d.ts +35 -16
- package/document-management/models/service-models/document.service-model.js +11 -6
- package/document-management/models/service-models/document.view-model.d.ts +1 -1
- package/document-management/models/service-models/document.view-model.js +2 -2
- package/document-management/server/drizzle/{0000_useful_overlord.sql → 0000_cool_victor_mancha.sql} +97 -41
- package/document-management/server/drizzle/meta/0000_snapshot.json +514 -126
- package/document-management/server/drizzle/meta/_journal.json +2 -2
- package/document-management/server/index.d.ts +1 -0
- package/document-management/server/index.js +1 -0
- package/document-management/server/module.d.ts +2 -1
- package/document-management/server/module.js +3 -1
- package/document-management/server/schemas.d.ts +14 -11
- package/document-management/server/schemas.js +13 -10
- package/document-management/server/services/document-management-ancillary.service.d.ts +4 -0
- package/document-management/server/services/document-management-ancillary.service.js +13 -0
- package/document-management/server/services/document-management.service.d.ts +63 -28
- package/document-management/server/services/document-management.service.js +507 -108
- package/document-management/server/services/index.d.ts +1 -0
- package/document-management/server/services/index.js +1 -0
- package/eslint.config.js +1 -0
- package/examples/document-management/main.d.ts +5 -0
- package/examples/document-management/main.js +20 -2
- package/file/index.d.ts +1 -0
- package/file/index.js +1 -0
- package/file/temporary-file.d.ts +18 -0
- package/file/temporary-file.js +53 -0
- package/http/server/http-server-response.d.ts +2 -0
- package/http/server/http-server-response.js +13 -0
- package/injector/index.d.ts +1 -0
- package/injector/index.js +1 -0
- package/injector/injector.js +19 -7
- package/injector/interfaces.d.ts +1 -1
- package/injector/interfaces.js +1 -1
- package/injector/resolution.d.ts +15 -0
- package/injector/resolution.js +6 -0
- package/logger/console/logger.d.ts +1 -1
- package/logger/logger.d.ts +1 -1
- package/object-storage/object-storage.d.ts +5 -7
- package/object-storage/s3/s3.object-storage.d.ts +0 -1
- package/object-storage/s3/s3.object-storage.js +0 -3
- package/orm/{server/data-types → data-types}/numeric-date.js +2 -3
- package/orm/decorators.d.ts +13 -4
- package/orm/decorators.js +13 -7
- package/orm/entity.js +3 -7
- package/orm/index.d.ts +1 -0
- package/orm/index.js +1 -0
- package/orm/server/drizzle/schema-converter.js +48 -19
- package/orm/server/repository.d.ts +5 -4
- package/orm/server/repository.js +33 -22
- package/orm/server/sqls.d.ts +9 -1
- package/orm/server/sqls.js +13 -0
- package/orm/types.d.ts +3 -3
- package/orm/utils.d.ts +3 -0
- package/orm/utils.js +6 -0
- package/package.json +13 -11
- package/pdf/pdf.service.d.ts +0 -1
- package/pdf/pdf.service.js +1 -95
- package/pdf/utils.d.ts +3 -1
- package/pdf/utils.js +129 -4
- package/promise/lazy-promise.d.ts +3 -3
- package/queue/postgres/module.d.ts +1 -1
- package/queue/postgres/queue.js +10 -12
- package/queue/queue.d.ts +14 -0
- package/queue/queue.js +43 -0
- package/utils/date-time.d.ts +4 -2
- package/utils/date-time.js +10 -3
- package/utils/format-error.js +0 -1
- package/utils/object/lazy-property.js +0 -1
- package/utils/try-ignore.d.ts +9 -2
- package/utils/try-ignore.js +30 -6
- package/document-management/models/document-index.model.d.ts +0 -7
- /package/orm/{server/data-types → data-types}/bytea.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/bytea.js +0 -0
- /package/orm/{server/data-types → data-types}/index.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/index.js +0 -0
- /package/orm/{server/data-types → data-types}/numeric-date.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/timestamp.d.ts +0 -0
- /package/orm/{server/data-types → data-types}/timestamp.js +0 -0
|
@@ -4,41 +4,157 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
8
|
+
if (value !== null && value !== void 0) {
|
|
9
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
10
|
+
var dispose, inner;
|
|
11
|
+
if (async) {
|
|
12
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
13
|
+
dispose = value[Symbol.asyncDispose];
|
|
14
|
+
}
|
|
15
|
+
if (dispose === void 0) {
|
|
16
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
17
|
+
dispose = value[Symbol.dispose];
|
|
18
|
+
if (async) inner = dispose;
|
|
19
|
+
}
|
|
20
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
21
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
22
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
23
|
+
}
|
|
24
|
+
else if (async) {
|
|
25
|
+
env.stack.push({ async: true });
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
};
|
|
29
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
30
|
+
return function (env) {
|
|
31
|
+
function fail(e) {
|
|
32
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
33
|
+
env.hasError = true;
|
|
34
|
+
}
|
|
35
|
+
var r, s = 0;
|
|
36
|
+
function next() {
|
|
37
|
+
while (r = env.stack.pop()) {
|
|
38
|
+
try {
|
|
39
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
40
|
+
if (r.dispose) {
|
|
41
|
+
var result = r.dispose.call(r.value);
|
|
42
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
43
|
+
}
|
|
44
|
+
else s |= 1;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
fail(e);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
51
|
+
if (env.hasError) throw env.error;
|
|
52
|
+
}
|
|
53
|
+
return next();
|
|
54
|
+
};
|
|
55
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
56
|
+
var e = new Error(message);
|
|
57
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
58
|
+
});
|
|
59
|
+
var DocumentManagementService_1;
|
|
60
|
+
import { and, eq, inArray, ne, notExists, sql } from 'drizzle-orm';
|
|
61
|
+
import { P, match } from 'ts-pattern';
|
|
62
|
+
import { AiService } from '../../../ai/index.js';
|
|
9
63
|
import { Enumerable } from '../../../enumerable/index.js';
|
|
10
64
|
import { BadRequestError } from '../../../errors/index.js';
|
|
11
|
-
import { getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
|
|
12
|
-
import { Singleton, inject, injectArgument, provide, resolveArgumentType } from '../../../injector/index.js';
|
|
65
|
+
import { TemporaryFile, getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
|
|
66
|
+
import { Singleton, afterResolve, inject, injectArgument, provide, resolveArgumentType } from '../../../injector/index.js';
|
|
67
|
+
import { Logger } from '../../../logger/logger.js';
|
|
13
68
|
import { ObjectStorage } from '../../../object-storage/index.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
69
|
+
import { getEntityMap } from '../../../orm/index.js';
|
|
70
|
+
import { DatabaseConfig, EntityRepositoryConfig, TRANSACTION_TIMESTAMP, arrayAgg, coalesce, injectRepository, jsonAgg, toJsonb } from '../../../orm/server/index.js';
|
|
71
|
+
import { getPdfPageCount } from '../../../pdf/index.js';
|
|
72
|
+
import { Queue } from '../../../queue/queue.js';
|
|
73
|
+
import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
|
|
74
|
+
import { distinct, toArray } from '../../../utils/array/index.js';
|
|
75
|
+
import { compareByValueSelectionToOrder } from '../../../utils/comparison.js';
|
|
76
|
+
import { digest } from '../../../utils/cryptography.js';
|
|
77
|
+
import { currentTimestamp, dateObjectToNumericDate } from '../../../utils/date-time.js';
|
|
18
78
|
import { groupToMap } from '../../../utils/iterable-helpers/index.js';
|
|
79
|
+
import { fromEntries, objectEntries } from '../../../utils/object/object.js';
|
|
19
80
|
import { readBinaryStream } from '../../../utils/stream/index.js';
|
|
20
|
-
import {
|
|
81
|
+
import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
|
|
82
|
+
import { assertBooleanPass, assertDefined, assertDefinedPass, assertNotNullPass, assertNumberPass, assertStringPass, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, isUndefined } from '../../../utils/type-guards.js';
|
|
83
|
+
import { millisecondsPerMinute } from '../../../utils/units.js';
|
|
84
|
+
import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
|
|
21
85
|
import { DocumentManagementConfig } from '../module.js';
|
|
22
|
-
|
|
86
|
+
import { documentCategory, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
|
|
87
|
+
import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
|
|
88
|
+
const defaultGenerationOptions = {
|
|
89
|
+
model: 'gemini-2.0-flash',
|
|
90
|
+
generationOptions: {
|
|
91
|
+
maxOutputTokens: 2048,
|
|
92
|
+
temperature: 0.2,
|
|
93
|
+
topP: 0.2
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
let DocumentManagementService = DocumentManagementService_1 = class DocumentManagementService {
|
|
23
97
|
#aiService = inject(AiService);
|
|
24
|
-
|
|
25
|
-
documentFileService = injectRepository(DocumentFile);
|
|
26
|
-
documentCollectionService = injectRepository(DocumentCollection);
|
|
98
|
+
ancillaryService = inject(DocumentManagementAncillaryService);
|
|
27
99
|
documentCategoryService = injectRepository(DocumentCategory);
|
|
28
|
-
|
|
100
|
+
documentCollectionDocumentService = injectRepository(DocumentCollectionDocument);
|
|
101
|
+
documentCollectionService = injectRepository(DocumentCollection);
|
|
102
|
+
documentFileService = injectRepository(DocumentFile);
|
|
103
|
+
documentPropertyService = injectRepository(DocumentProperty);
|
|
104
|
+
documentPropertyValueService = injectRepository(DocumentPropertyValue);
|
|
105
|
+
documentRequestAssignmentTaskCollectionService = injectRepository(DocumentRequestAssignmentTaskCollection);
|
|
106
|
+
documentRequestAssignmentTaskService = injectRepository(DocumentRequestAssignmentTask);
|
|
107
|
+
documentRequestCollectionService = injectRepository(DocumentRequestCollection);
|
|
108
|
+
documentRequestFilePropertyValueService = injectRepository(DocumentRequestFilePropertyValue);
|
|
109
|
+
documentRequestAssignmentTaskPropertyValueService = injectRepository(DocumentRequestAssignmentTaskPropertyValue);
|
|
110
|
+
documentRequestFileService = injectRepository(DocumentRequestFile);
|
|
29
111
|
documentRequestService = injectRepository(DocumentRequest);
|
|
30
112
|
documentRequestsTemplateService = injectRepository(DocumentRequestsTemplate);
|
|
31
113
|
documentRequestTemplateService = injectRepository(DocumentRequestTemplate);
|
|
32
|
-
|
|
33
|
-
documentRequestFileService = injectRepository(DocumentRequestFile);
|
|
34
|
-
documentPropertyService = injectRepository(DocumentProperty);
|
|
114
|
+
documentService = injectRepository(Document);
|
|
35
115
|
documentTypePropertyService = injectRepository(DocumentTypeProperty);
|
|
36
|
-
|
|
37
|
-
documentPropertyIntegerValueService = injectRepository(DocumentPropertyIntegerValue);
|
|
38
|
-
documentPropertyDecimalValueService = injectRepository(DocumentPropertyDecimalValue);
|
|
39
|
-
documentPropertyBooleanValueService = injectRepository(DocumentPropertyBooleanValue);
|
|
40
|
-
documentCollectionDocumentService = injectRepository(DocumentCollectionDocument);
|
|
116
|
+
documentTypeService = injectRepository(DocumentType);
|
|
41
117
|
fileObjectStorage = inject(ObjectStorage, (injectArgument(this, { optional: true }) ?? inject(DocumentManagementConfig)).fileObjectStorageModule);
|
|
118
|
+
extractionQueue = inject((Queue), { name: 'DocumentManagement:extraction', processTimeout: 15 * millisecondsPerMinute });
|
|
119
|
+
assignmentQueue = inject((Queue), { name: 'DocumentManagement:assignment', processTimeout: 15 * millisecondsPerMinute });
|
|
120
|
+
logger = inject(Logger, DocumentManagementService_1.name);
|
|
121
|
+
[afterResolve](_, { cancellationSignal }) {
|
|
122
|
+
this.processQueues(cancellationSignal);
|
|
123
|
+
}
|
|
124
|
+
processQueues(cancellationSignal) {
|
|
125
|
+
this.extractionQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
|
|
126
|
+
const [entry] = objectEntries(job.data);
|
|
127
|
+
this.logger.info(`Processing extraction job for ${entry?.[0]} "${entry?.[1]}"`);
|
|
128
|
+
await match(job.data)
|
|
129
|
+
.with({ documentId: P.string.select() }, async (documentId) => this.enrichDocument(documentId))
|
|
130
|
+
.with({ requestFileId: P.string.select() }, async (requestFileId) => this.enrichDocumentRequestFile(requestFileId))
|
|
131
|
+
.with({ requestAssignmentTaskId: P.string.select() }, async (requestAssignmentTaskId) => this.enrichDocumentRequestAssignmentTask(requestAssignmentTaskId))
|
|
132
|
+
.exhaustive();
|
|
133
|
+
}, this.logger);
|
|
134
|
+
this.assignmentQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
|
|
135
|
+
this.logger.info(`Processing assignment job "${job.data.requestAssignmentTaskId}"`);
|
|
136
|
+
await this.assignDocumentRequest(job.data.requestAssignmentTaskId);
|
|
137
|
+
}, this.logger);
|
|
138
|
+
}
|
|
139
|
+
async resolveNames(...collectionsOrIds) {
|
|
140
|
+
if (collectionsOrIds.length == 0) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
const loadIds = collectionsOrIds.filter((collection) => isString(collection));
|
|
144
|
+
if (loadIds.length == 0) {
|
|
145
|
+
return this.ancillaryService.resolveNames(collectionsOrIds);
|
|
146
|
+
}
|
|
147
|
+
const loadedCollections = await this.documentCollectionService.loadManyByQuery({ id: { $in: loadIds } });
|
|
148
|
+
const collections = collectionsOrIds.map((collectionOrId) => isString(collectionOrId)
|
|
149
|
+
? assertDefinedPass(loadedCollections.find((collection) => collection.id == collectionOrId), `Could not load collection "${collectionOrId}".`)
|
|
150
|
+
: collectionOrId);
|
|
151
|
+
return this.ancillaryService.resolveNames(collections);
|
|
152
|
+
}
|
|
153
|
+
async resolveNamesMap(...collectionsOrIds) {
|
|
154
|
+
const names = await this.resolveNames(...collectionsOrIds);
|
|
155
|
+
const entries = collectionsOrIds.map((collectionOrId, index) => [isString(collectionOrId) ? collectionOrId : collectionOrId.id, names[index]]);
|
|
156
|
+
return fromEntries(entries);
|
|
157
|
+
}
|
|
42
158
|
async loadData(collectionIds, collectionsMetadata) {
|
|
43
159
|
return this.documentCollectionService.transaction(async (_, transaction) => {
|
|
44
160
|
const [collections, collectionDocuments, requestCollections, categories, types] = await Promise.all([
|
|
@@ -50,20 +166,17 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
50
166
|
]);
|
|
51
167
|
const documentIds = collectionDocuments.map((document) => document.documentId);
|
|
52
168
|
const requestIds = requestCollections.map((requestCollection) => requestCollection.requestId);
|
|
53
|
-
const [documents, requests, requestFiles,
|
|
169
|
+
const [documents, requests, requestFiles, propertyValues] = await Promise.all([
|
|
54
170
|
this.documentService.withTransaction(transaction).loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
55
171
|
this.documentRequestService.withTransaction(transaction).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
56
172
|
this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
57
|
-
this.
|
|
58
|
-
this.documentPropertyIntegerValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
|
|
59
|
-
this.documentPropertyDecimalValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
|
|
60
|
-
this.documentPropertyBooleanValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
|
|
173
|
+
this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
|
|
61
174
|
]);
|
|
62
175
|
const documentFileIds = documents.map((document) => document.fileId);
|
|
63
176
|
const requestFileIds = requestFiles.map((requestFile) => requestFile.fileId);
|
|
64
177
|
const files = await this.documentFileService.withTransaction(transaction).loadManyByQuery({ id: { $in: [...documentFileIds, ...requestFileIds] } }, { order: { 'metadata.createTimestamp': 'desc' } });
|
|
65
178
|
const requestsFilesMap = groupToMap(requestFiles, (requestFile) => requestFile.requestId);
|
|
66
|
-
const valuesMap = Enumerable.from(
|
|
179
|
+
const valuesMap = Enumerable.from(propertyValues).groupToMap((value) => value.documentId);
|
|
67
180
|
const collectionViews = collections.toSorted(compareByValueSelectionToOrder(collectionIds, (collection) => collection.id)).map((collection) => ({
|
|
68
181
|
...collection,
|
|
69
182
|
name: collectionsMetadata?.[collection.id]?.name ?? null,
|
|
@@ -172,19 +285,20 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
172
285
|
async deleteDocumentRequestTemplate(parameters) {
|
|
173
286
|
return this.documentRequestTemplateService.delete(parameters.id);
|
|
174
287
|
}
|
|
175
|
-
async createDocument({ typeId, title, date,
|
|
176
|
-
|
|
288
|
+
async createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }, content) {
|
|
289
|
+
const document = await this.documentService.transaction(async (_, transaction) => {
|
|
177
290
|
const documentFile = await this.createDocumentFile(content, originalFileName, transaction);
|
|
178
|
-
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, date,
|
|
291
|
+
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata });
|
|
179
292
|
for (const collectionId of toArray(collectionIds)) {
|
|
180
293
|
await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
|
|
181
294
|
}
|
|
182
295
|
if (isDefined(properties)) {
|
|
183
|
-
|
|
184
|
-
await this.setDocumentProperties(mappedProperties, transaction);
|
|
296
|
+
await this.setPropertyValues({ documentId: document.id, properties }, transaction);
|
|
185
297
|
}
|
|
186
298
|
return document;
|
|
187
299
|
});
|
|
300
|
+
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ documentId: document.id }), this.logger);
|
|
301
|
+
return document;
|
|
188
302
|
}
|
|
189
303
|
async approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }) {
|
|
190
304
|
return this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
@@ -198,14 +312,17 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
198
312
|
fileId: requestFile.fileId,
|
|
199
313
|
typeId: request.typeId,
|
|
200
314
|
title: requestFile.title,
|
|
201
|
-
|
|
202
|
-
|
|
315
|
+
subtitle: requestFile.subtitle,
|
|
316
|
+
pages: requestFile.pages,
|
|
317
|
+
date: requestFile.date,
|
|
318
|
+
summary: requestFile.summary,
|
|
319
|
+
tags: requestFile.tags,
|
|
203
320
|
metadata: documentMetadata
|
|
204
321
|
});
|
|
205
322
|
for (const { collectionId } of requestCollections) {
|
|
206
323
|
await this.addDocumentToCollection({ collectionId, documentId: document.id }, transaction);
|
|
207
324
|
}
|
|
208
|
-
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp:
|
|
325
|
+
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, createdDocumentId: document.id, metadata: requestFileMetadata });
|
|
209
326
|
return document;
|
|
210
327
|
});
|
|
211
328
|
}
|
|
@@ -215,7 +332,7 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
215
332
|
if (requestFile.approval == true) {
|
|
216
333
|
throw new BadRequestError('Document request file already accepted.');
|
|
217
334
|
}
|
|
218
|
-
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp:
|
|
335
|
+
await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
219
336
|
});
|
|
220
337
|
}
|
|
221
338
|
async updateDocumentRequestFile({ id, title, approvalComment, metadata }) {
|
|
@@ -228,12 +345,14 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
228
345
|
}
|
|
229
346
|
await this.documentRequestFileService.delete(id, metadata);
|
|
230
347
|
}
|
|
231
|
-
async createDocumentFile(content, originalFileName,
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const documentFile = await this.documentFileService
|
|
348
|
+
async createDocumentFile(content, originalFileName, transaction) {
|
|
349
|
+
const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
|
|
350
|
+
const hash = await digest('SHA-256', contentAsUint8Array).toHex();
|
|
351
|
+
const mimeType = await getMimeType(contentAsUint8Array);
|
|
352
|
+
return this.documentFileService.useTransaction(transaction, async (_, transaction) => {
|
|
353
|
+
const documentFile = await this.documentFileService
|
|
354
|
+
.withTransaction(transaction)
|
|
355
|
+
.insert({
|
|
237
356
|
originalFileName,
|
|
238
357
|
mimeType,
|
|
239
358
|
hash,
|
|
@@ -244,8 +363,8 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
244
363
|
return documentFile;
|
|
245
364
|
});
|
|
246
365
|
}
|
|
247
|
-
async createDocumentRequestFile({ requestId, title, originalFileName, metadata }, content) {
|
|
248
|
-
|
|
366
|
+
async createDocumentRequestFile({ requestId, title, subtitle, date, summary, tags, originalFileName, metadata }, content) {
|
|
367
|
+
const documentRequestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
249
368
|
const [request, existingRequestFiles] = await Promise.all([
|
|
250
369
|
this.documentRequestService.withTransaction(transaction).load(requestId),
|
|
251
370
|
this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId })
|
|
@@ -255,8 +374,26 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
255
374
|
throw new BadRequestError('Maximum amount of allowed files reached.');
|
|
256
375
|
}
|
|
257
376
|
const file = await this.createDocumentFile(content, originalFileName, transaction);
|
|
258
|
-
return this.documentRequestFileService
|
|
377
|
+
return await this.documentRequestFileService
|
|
378
|
+
.withTransaction(transaction)
|
|
379
|
+
.insert({
|
|
380
|
+
requestId,
|
|
381
|
+
fileId: file.id,
|
|
382
|
+
title,
|
|
383
|
+
subtitle,
|
|
384
|
+
pages: null,
|
|
385
|
+
date,
|
|
386
|
+
summary,
|
|
387
|
+
tags,
|
|
388
|
+
createdDocumentId: null,
|
|
389
|
+
approval: null,
|
|
390
|
+
approvalComment: null,
|
|
391
|
+
approvalTimestamp: null,
|
|
392
|
+
metadata
|
|
393
|
+
});
|
|
259
394
|
});
|
|
395
|
+
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }), this.logger);
|
|
396
|
+
return documentRequestFile;
|
|
260
397
|
}
|
|
261
398
|
async createDocumentRequest(parameters, transaction) {
|
|
262
399
|
if (parameters.collectionIds.length == 0) {
|
|
@@ -269,13 +406,36 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
269
406
|
return request;
|
|
270
407
|
});
|
|
271
408
|
}
|
|
409
|
+
async createDocumentRequestAssignmentTask({ originalFileName, collectionIds }, content) {
|
|
410
|
+
if (collectionIds.length == 0) {
|
|
411
|
+
throw new BadRequestError('No target collectionId specified.');
|
|
412
|
+
}
|
|
413
|
+
const file = await this.createDocumentFile(content, originalFileName);
|
|
414
|
+
return this.documentRequestAssignmentTaskService.transaction(async (_, transaction) => {
|
|
415
|
+
const task = await this.documentRequestAssignmentTaskService.withTransaction(transaction).insert({
|
|
416
|
+
fileId: file.id,
|
|
417
|
+
assignedRequestFileId: null,
|
|
418
|
+
typeId: null,
|
|
419
|
+
title: null,
|
|
420
|
+
subtitle: null,
|
|
421
|
+
pages: null,
|
|
422
|
+
date: null,
|
|
423
|
+
summary: null,
|
|
424
|
+
tags: null,
|
|
425
|
+
assignmentTries: 0
|
|
426
|
+
});
|
|
427
|
+
const newTaksCollections = collectionIds.map((collectionId) => ({ requestAssignmentTaskId: task.id, collectionId }));
|
|
428
|
+
await this.documentRequestAssignmentTaskCollectionService.withTransaction(transaction).insertMany(newTaksCollections);
|
|
429
|
+
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }), this.logger);
|
|
430
|
+
return task;
|
|
431
|
+
});
|
|
432
|
+
}
|
|
272
433
|
async updateDocument(parameters, transaction) {
|
|
273
434
|
const { id: documentId, properties: propertyUpdates, ...update } = parameters;
|
|
274
435
|
await this.documentService.useTransaction(transaction, async (repository, tx) => {
|
|
275
436
|
await repository.update(documentId, update);
|
|
276
437
|
if (isDefined(propertyUpdates)) {
|
|
277
|
-
|
|
278
|
-
await this.setDocumentProperties(mappedPropertyUpdates, tx);
|
|
438
|
+
await this.setPropertyValues({ documentId, properties: propertyUpdates }, tx);
|
|
279
439
|
}
|
|
280
440
|
});
|
|
281
441
|
}
|
|
@@ -298,19 +458,41 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
298
458
|
}
|
|
299
459
|
await this.documentRequestService.withOptionalTransaction(transaction).delete(id, metadata);
|
|
300
460
|
}
|
|
301
|
-
async
|
|
302
|
-
if ((
|
|
461
|
+
async setPropertyValues({ documentId, requestFileId, requestAssignmentTaskId, properties: items }, transaction) {
|
|
462
|
+
if ((items.length == 0)) {
|
|
303
463
|
return;
|
|
304
464
|
}
|
|
465
|
+
if ((Number(isDefined(documentId)) + Number(isDefined(requestFileId)) + Number(isDefined(requestAssignmentTaskId))) != 1) {
|
|
466
|
+
throw new BadRequestError('Exactly one of documentId, requestFileId or requestAssignmentTaskId must be specified.');
|
|
467
|
+
}
|
|
305
468
|
await this.documentPropertyService.useTransaction(transaction, async (_, tx) => {
|
|
306
|
-
const propertyIds =
|
|
469
|
+
const propertyIds = items.map((property) => property.propertyId);
|
|
307
470
|
const properties = await this.documentPropertyService.withTransaction(tx).loadManyByQuery({ id: { $in: propertyIds } });
|
|
308
471
|
const propertiesMap = getEntityMap(properties);
|
|
309
|
-
|
|
472
|
+
const propertyItems = items.map(({ propertyId, value, metadata }) => {
|
|
310
473
|
const property = assertDefinedPass(propertiesMap.get(propertyId));
|
|
311
474
|
validatePropertyValue(propertyId, property.dataType, value);
|
|
312
|
-
|
|
313
|
-
|
|
475
|
+
return {
|
|
476
|
+
propertyId,
|
|
477
|
+
text: (property.dataType == 'text') ? assertStringPass(value) : null,
|
|
478
|
+
integer: (property.dataType == 'integer') ? assertNumberPass(value) : null,
|
|
479
|
+
decimal: (property.dataType == 'decimal') ? assertNumberPass(value) : null,
|
|
480
|
+
boolean: (property.dataType == 'boolean') ? assertBooleanPass(value) : null,
|
|
481
|
+
date: (property.dataType == 'date') ? assertNumberPass(value) : null,
|
|
482
|
+
metadata
|
|
483
|
+
};
|
|
484
|
+
});
|
|
485
|
+
if (isDefined(documentId)) {
|
|
486
|
+
await this.documentPropertyValueService.withTransaction(tx)
|
|
487
|
+
.upsertMany(['documentId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, documentId })));
|
|
488
|
+
}
|
|
489
|
+
else if (isDefined(requestFileId)) {
|
|
490
|
+
await this.documentRequestFilePropertyValueService.withTransaction(tx)
|
|
491
|
+
.upsertMany(['requestFileId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestFileId })));
|
|
492
|
+
}
|
|
493
|
+
else if (isDefined(requestAssignmentTaskId)) {
|
|
494
|
+
await this.documentRequestAssignmentTaskPropertyValueService.withTransaction(tx)
|
|
495
|
+
.upsertMany(['requestAssignmentTaskId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestAssignmentTaskId })));
|
|
314
496
|
}
|
|
315
497
|
});
|
|
316
498
|
}
|
|
@@ -318,7 +500,7 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
318
500
|
await this.documentCollectionDocumentService.withOptionalTransaction(transaction).upsert(['collectionId', 'documentId'], { ...parameters, archiveTimestamp: null });
|
|
319
501
|
}
|
|
320
502
|
async archiveDocument({ collectionId, documentId, metadata }) {
|
|
321
|
-
await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp:
|
|
503
|
+
await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
322
504
|
}
|
|
323
505
|
async createProperty(parameters) {
|
|
324
506
|
return this.documentPropertyService.insert(parameters);
|
|
@@ -326,66 +508,282 @@ let DocumentManagementService = class DocumentManagementService {
|
|
|
326
508
|
async assignPropertyToType(parameters) {
|
|
327
509
|
await this.documentTypePropertyService.insert(parameters);
|
|
328
510
|
}
|
|
329
|
-
async
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
511
|
+
async enrichDocument(documentId) {
|
|
512
|
+
const document = await this.documentService.load(documentId);
|
|
513
|
+
const { properties, ...extractionResult } = await this.extractFileInformation(document.fileId, document.typeId);
|
|
514
|
+
await this.documentService.transaction(async (documentService, transaction) => {
|
|
515
|
+
await documentService.update(document.id, extractionResult);
|
|
516
|
+
await this.setPropertyValues({ documentId, properties: properties }, transaction);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
async enrichDocumentRequestFile(requestFileId) {
|
|
520
|
+
const requestFile = await this.documentRequestFileService.load(requestFileId);
|
|
521
|
+
const request = await this.documentRequestService.load(requestFile.requestId);
|
|
522
|
+
const { properties, typeId: _, ...extractionResult } = await this.extractFileInformation(requestFile.fileId, request.typeId);
|
|
523
|
+
await this.documentRequestFileService.transaction(async (documentRequestFileService, transaction) => {
|
|
524
|
+
await documentRequestFileService.update(requestFile.id, extractionResult);
|
|
525
|
+
await this.setPropertyValues({ requestFileId, properties: properties }, transaction);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
async enrichDocumentRequestAssignmentTask(requestAssignmentTaskId) {
|
|
529
|
+
const task = await this.documentRequestAssignmentTaskService.load(requestAssignmentTaskId);
|
|
530
|
+
const { properties, ...extractionResult } = await this.extractFileInformation(task.fileId);
|
|
531
|
+
await this.documentRequestAssignmentTaskService.transaction(async (documentRequestAssignmentTaskService, transaction) => {
|
|
532
|
+
await documentRequestAssignmentTaskService.update(task.id, extractionResult);
|
|
533
|
+
await this.setPropertyValues({ requestAssignmentTaskId, properties: properties }, transaction);
|
|
340
534
|
});
|
|
341
|
-
|
|
342
|
-
|
|
535
|
+
void tryIgnoreLogAsync(async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }), this.logger);
|
|
536
|
+
}
|
|
537
|
+
async assignDocumentRequest(requestAssignmentTaskId) {
|
|
538
|
+
const session = this.documentRequestAssignmentTaskCollectionService.session;
|
|
539
|
+
const requestAssignmentTaskProperties = session.$with('requestAssignmentTaskProperty').as(session
|
|
540
|
+
.select({
|
|
541
|
+
requestAssignmentTaskId: documentRequestAssignmentTaskPropertyValue.requestAssignmentTaskId,
|
|
542
|
+
propertyId: documentRequestAssignmentTaskPropertyValue.propertyId,
|
|
543
|
+
label: documentProperty.label,
|
|
544
|
+
value: coalesce(toJsonb(documentRequestAssignmentTaskPropertyValue.text), toJsonb(documentRequestAssignmentTaskPropertyValue.integer), toJsonb(documentRequestAssignmentTaskPropertyValue.decimal), toJsonb(documentRequestAssignmentTaskPropertyValue.boolean), toJsonb(documentRequestAssignmentTaskPropertyValue.date)).as('value')
|
|
545
|
+
})
|
|
546
|
+
.from(documentRequestAssignmentTaskPropertyValue)
|
|
547
|
+
.innerJoin(documentProperty, eq(documentProperty.id, documentRequestAssignmentTaskPropertyValue.propertyId)));
|
|
548
|
+
const [task] = await session
|
|
549
|
+
.with(requestAssignmentTaskProperties)
|
|
550
|
+
.select({
|
|
551
|
+
fileId: documentRequestAssignmentTask.fileId,
|
|
552
|
+
collectionIds: arrayAgg(documentRequestAssignmentTaskCollection.collectionId),
|
|
553
|
+
typeCategory: documentCategory.label,
|
|
554
|
+
typeGroup: documentType.group,
|
|
555
|
+
typeLabel: documentType.label,
|
|
556
|
+
title: documentRequestAssignmentTask.title,
|
|
557
|
+
subtitle: documentRequestAssignmentTask.subtitle,
|
|
558
|
+
pages: documentRequestAssignmentTask.pages,
|
|
559
|
+
date: documentRequestAssignmentTask.date,
|
|
560
|
+
summary: documentRequestAssignmentTask.summary,
|
|
561
|
+
tags: documentRequestAssignmentTask.tags,
|
|
562
|
+
assignmentTries: documentRequestAssignmentTask.assignmentTries,
|
|
563
|
+
properties: sql `coalesce(${jsonAgg(requestAssignmentTaskProperties)} FILTER (WHERE ${requestAssignmentTaskProperties.propertyId} IS NOT NULL), '[]'::json)`
|
|
564
|
+
})
|
|
565
|
+
.from(documentRequestAssignmentTask)
|
|
566
|
+
.innerJoin(documentRequestAssignmentTaskCollection, eq(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, documentRequestAssignmentTask.id))
|
|
567
|
+
.innerJoin(documentType, eq(documentType.id, documentRequestAssignmentTask.typeId))
|
|
568
|
+
.innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
569
|
+
.leftJoin(requestAssignmentTaskProperties, eq(requestAssignmentTaskProperties.requestAssignmentTaskId, documentRequestAssignmentTask.id))
|
|
570
|
+
.where(eq(documentRequestAssignmentTask.id, requestAssignmentTaskId))
|
|
571
|
+
.groupBy(documentRequestAssignmentTask.fileId, requestAssignmentTaskProperties.label, documentCategory.label, documentType.group, documentType.label, documentRequestAssignmentTask.title, documentRequestAssignmentTask.subtitle, documentRequestAssignmentTask.pages, documentRequestAssignmentTask.date, documentRequestAssignmentTask.summary, documentRequestAssignmentTask.tags, documentRequestAssignmentTask.assignmentTries);
|
|
572
|
+
assertDefined(task);
|
|
573
|
+
if (task.assignmentTries >= 3) {
|
|
574
|
+
this.logger.error(`Ignoring assignment task "${requestAssignmentTaskId}" because max tries are reached.`);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const requestsWithoutFiles = await session
|
|
578
|
+
.select({
|
|
579
|
+
id: documentRequest.id,
|
|
580
|
+
collectionIds: arrayAgg(documentRequestCollection.collectionId),
|
|
581
|
+
documentCategory: documentCategory.label,
|
|
582
|
+
documentGroup: documentType.group,
|
|
583
|
+
documentType: documentType.label,
|
|
584
|
+
comment: documentRequest.comment
|
|
585
|
+
})
|
|
586
|
+
.from(documentRequest)
|
|
587
|
+
.innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequest.id))
|
|
588
|
+
.innerJoin(documentType, eq(documentType.id, documentRequest.typeId))
|
|
589
|
+
.innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
590
|
+
.where(and(inArray(documentRequestCollection.collectionId, task.collectionIds), notExists(session
|
|
591
|
+
.select()
|
|
592
|
+
.from(documentRequestFile)
|
|
593
|
+
.where(and(eq(documentRequestFile.requestId, documentRequest.id), ne(documentRequestFile.approval, false))))))
|
|
594
|
+
.groupBy(documentRequest.id, documentCategory.label, documentType.group, documentType.label, documentRequest.comment);
|
|
595
|
+
const requestsCollectionIds = distinct(requestsWithoutFiles.flatMap((request) => request.collectionIds));
|
|
596
|
+
const collectionNamesMap = await this.resolveNamesMap(...requestsCollectionIds);
|
|
597
|
+
const requests = requestsWithoutFiles.map((request) => ({
|
|
598
|
+
id: request.id,
|
|
599
|
+
collections: request.collectionIds.map((collectionId) => assertDefinedPass(collectionNamesMap[collectionId])),
|
|
600
|
+
documentCategory: request.documentCategory,
|
|
601
|
+
documentGroup: request.documentGroup ?? undefined,
|
|
602
|
+
documentType: request.documentType,
|
|
603
|
+
comment: request.comment ?? undefined
|
|
604
|
+
}));
|
|
605
|
+
const propertyEntries = task.properties.map((property) => [property.label, assertNotNullPass(property.value)]);
|
|
606
|
+
const context = {
|
|
607
|
+
file: {
|
|
608
|
+
documentCategory: task.typeCategory,
|
|
609
|
+
documentGroup: task.typeGroup ?? undefined,
|
|
610
|
+
documentType: task.typeLabel,
|
|
611
|
+
properties: fromEntries(propertyEntries)
|
|
612
|
+
},
|
|
613
|
+
requests
|
|
614
|
+
};
|
|
615
|
+
const result = await this.#aiService.generate({
|
|
616
|
+
...defaultGenerationOptions,
|
|
343
617
|
generationOptions: {
|
|
344
|
-
maxOutputTokens:
|
|
345
|
-
temperature: 0
|
|
618
|
+
maxOutputTokens: 100,
|
|
619
|
+
temperature: 0,
|
|
346
620
|
topP: 0.2
|
|
347
621
|
},
|
|
348
|
-
generationSchema,
|
|
349
|
-
contents: [
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
{ file: file.file },
|
|
622
|
+
generationSchema: object({ requestId: nullable(string()) }),
|
|
623
|
+
contents: [{
|
|
624
|
+
role: 'user',
|
|
625
|
+
parts: [
|
|
353
626
|
{
|
|
354
|
-
text:
|
|
627
|
+
text: `<context>
|
|
628
|
+
${JSON.stringify(context, null, 2)}
|
|
629
|
+
</context>
|
|
355
630
|
|
|
356
|
-
|
|
357
|
-
Erstelle bis zu 7 möglichst spezifische Tags ohne allgemeinen Worte.
|
|
358
|
-
Antworte auf deutsch.`
|
|
631
|
+
Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gib es als JSON im angegebenen Schema aus. Wenn keine Anforderung passt, setze requestId auf null.`
|
|
359
632
|
}
|
|
360
633
|
]
|
|
361
|
-
}
|
|
362
|
-
|
|
634
|
+
}]
|
|
635
|
+
});
|
|
636
|
+
await this.documentRequestAssignmentTaskService.update(requestAssignmentTaskId, { assignmentTries: sql `${documentRequestAssignmentTask.assignmentTries} + 1` });
|
|
637
|
+
if (isNull(result.json.requestId)) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
641
|
+
const requestFile = await this.documentRequestFileService.withTransaction(transaction).insert({
|
|
642
|
+
requestId: result.json.requestId,
|
|
643
|
+
fileId: task.fileId,
|
|
644
|
+
title: task.title,
|
|
645
|
+
subtitle: task.subtitle,
|
|
646
|
+
pages: task.pages,
|
|
647
|
+
date: task.date,
|
|
648
|
+
summary: task.summary,
|
|
649
|
+
tags: task.tags,
|
|
650
|
+
createdDocumentId: null,
|
|
651
|
+
approval: null,
|
|
652
|
+
approvalComment: null,
|
|
653
|
+
approvalTimestamp: null
|
|
654
|
+
});
|
|
655
|
+
await this.setPropertyValues({ requestFileId: requestFile.id, properties: task.properties }, transaction);
|
|
363
656
|
});
|
|
364
|
-
const resultJson = JSON.parse(assertStringPass(generation.text, 'No text generated.'));
|
|
365
|
-
const result = generationSchema.parse(resultJson);
|
|
366
|
-
result.documentTags = result.documentTags.filter((tag) => (tag != result.documentTitle) && (tag != result.documentSubtitle));
|
|
367
|
-
return {
|
|
368
|
-
title: result.documentTitle,
|
|
369
|
-
subtitle: result.documentSubtitle,
|
|
370
|
-
types: result.documentTypes.map((typeLabel) => types.find((type) => type.label == typeLabel)).filter(isDefined),
|
|
371
|
-
summary: result.documentSummary,
|
|
372
|
-
tags: result.documentTags,
|
|
373
|
-
date: result.documentDate
|
|
374
|
-
};
|
|
375
657
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
658
|
+
async extractFileInformation(fileId, assumeTypeId) {
|
|
659
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
660
|
+
try {
|
|
661
|
+
const file = await this.documentFileService.load(fileId);
|
|
662
|
+
const fileContentStream = this.getDocumentFileContentStream(fileId);
|
|
663
|
+
const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
|
|
664
|
+
const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
|
|
665
|
+
const pages = file.mimeType.includes('pdf')
|
|
666
|
+
? await tryIgnoreLogAsync(async () => getPdfPageCount(tmpFile.path), this.logger, null)
|
|
667
|
+
: null;
|
|
668
|
+
const types = await this.documentTypeService.session
|
|
669
|
+
.select({
|
|
670
|
+
typeId: documentType.id,
|
|
671
|
+
categoryLabel: documentCategory.label,
|
|
672
|
+
typeGroup: documentType.group,
|
|
673
|
+
typeLabel: documentType.label
|
|
674
|
+
})
|
|
675
|
+
.from(documentType)
|
|
676
|
+
.leftJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
|
|
677
|
+
.where(isString(assumeTypeId) ? eq(documentType.id, assumeTypeId) : undefined);
|
|
678
|
+
const typeLabelEntries = types.map((type) => ({ id: type.typeId, label: `${type.categoryLabel} | ${type.typeGroup} | ${type.typeLabel}` }));
|
|
679
|
+
const typeLabels = typeLabelEntries.map(({ label }) => label);
|
|
680
|
+
const documentTypeGeneration = await this.#aiService.generate({
|
|
681
|
+
...defaultGenerationOptions,
|
|
682
|
+
generationSchema: object({
|
|
683
|
+
documentType: enumeration(typeLabels)
|
|
684
|
+
}),
|
|
685
|
+
contents: [
|
|
686
|
+
{
|
|
687
|
+
role: 'user',
|
|
688
|
+
parts: [
|
|
689
|
+
{ file: filePart.file },
|
|
690
|
+
{ text: `Klassifiziere den Inhalt des Dokuments in das angegebenen JSON Schema.` }
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
]
|
|
694
|
+
});
|
|
695
|
+
const typeId = typeLabelEntries.find((entry) => entry.label == documentTypeGeneration.json.documentType)?.id;
|
|
696
|
+
const typeProperties = isDefined(typeId) ? await this.documentTypePropertyService.loadManyByQuery({ typeId }) : undefined;
|
|
697
|
+
const propertyIds = typeProperties?.map((property) => property.propertyId);
|
|
698
|
+
const properties = isDefined(propertyIds) ? await this.documentPropertyService.loadManyByQuery({ id: { $in: propertyIds } }) : undefined;
|
|
699
|
+
const propertiesSchemaEntries = properties?.map((property) => {
|
|
700
|
+
const schema = match(property.dataType)
|
|
701
|
+
.with('text', () => nullable(string()))
|
|
702
|
+
.with('integer', () => nullable(integer()))
|
|
703
|
+
.with('decimal', () => nullable(number()))
|
|
704
|
+
.with('boolean', () => nullable(boolean()))
|
|
705
|
+
.with('date', () => nullable(object({ year: integer(), month: integer(), day: integer() })))
|
|
706
|
+
.exhaustive();
|
|
707
|
+
return [property.label, schema];
|
|
708
|
+
});
|
|
709
|
+
const generationSchema = object({
|
|
710
|
+
documentTitle: string(),
|
|
711
|
+
documentSubtitle: nullable(string()),
|
|
712
|
+
documentSummary: string(),
|
|
713
|
+
documentTags: array(string()),
|
|
714
|
+
documentDate: nullable(object({ year: integer(), month: integer(), day: integer() })),
|
|
715
|
+
...((isUndefined(propertiesSchemaEntries) || (propertiesSchemaEntries.length == 0))
|
|
716
|
+
? {}
|
|
717
|
+
: { documentProperties: object(fromEntries(propertiesSchemaEntries)) })
|
|
718
|
+
});
|
|
719
|
+
const { json: extraction } = await this.#aiService.generate({
|
|
720
|
+
model: 'gemini-2.0-flash',
|
|
721
|
+
generationOptions: {
|
|
722
|
+
maxOutputTokens: 2048,
|
|
723
|
+
temperature: 0.2,
|
|
724
|
+
topP: 0.2
|
|
725
|
+
},
|
|
726
|
+
generationSchema,
|
|
727
|
+
contents: [
|
|
728
|
+
{
|
|
729
|
+
role: 'user',
|
|
730
|
+
parts: [
|
|
731
|
+
{ file: filePart.file },
|
|
732
|
+
{
|
|
733
|
+
text: `Extrahiere den Inhalt des Dokuments in das angegebenen JSON Schema.
|
|
734
|
+
|
|
735
|
+
Gib in der summary ausführlich an, welche Informationen in dem Dokument vorkommen(ohne konkrete Werte).
|
|
736
|
+
Erstelle bis zu 7 möglichst spezifische Tags.
|
|
737
|
+
Antworte auf deutsch.`
|
|
738
|
+
}
|
|
739
|
+
]
|
|
740
|
+
}
|
|
741
|
+
]
|
|
742
|
+
});
|
|
743
|
+
const filteredDocumentTags = extraction.documentTags.filter((tag) => (tag != extraction.documentTitle) && (tag != extraction.documentSubtitle));
|
|
744
|
+
const date = isNotNull(extraction.documentDate) ? dateObjectToNumericDate(extraction.documentDate) : null;
|
|
745
|
+
const parsedProperties = isUndefined(extraction.documentProperties)
|
|
746
|
+
? []
|
|
747
|
+
: objectEntries(extraction.documentProperties)
|
|
748
|
+
.map(([propertyLabel, rawValue]) => {
|
|
749
|
+
if (isNull(rawValue)) {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
const property = assertDefinedPass(properties?.find((property) => property.label == propertyLabel));
|
|
753
|
+
const value = match(rawValue)
|
|
754
|
+
.with({ year: P.number }, (val) => dateObjectToNumericDate(val))
|
|
755
|
+
.otherwise((val) => val);
|
|
756
|
+
return { propertyId: property.id, dataType: property.dataType, value };
|
|
757
|
+
})
|
|
758
|
+
.filter(isNotNull);
|
|
759
|
+
return {
|
|
760
|
+
typeId: typeId ?? null,
|
|
761
|
+
title: extraction.documentTitle,
|
|
762
|
+
subtitle: extraction.documentSubtitle,
|
|
763
|
+
pages,
|
|
764
|
+
date,
|
|
765
|
+
summary: extraction.documentSummary,
|
|
766
|
+
tags: filteredDocumentTags,
|
|
767
|
+
properties: parsedProperties
|
|
768
|
+
};
|
|
388
769
|
}
|
|
770
|
+
catch (e_1) {
|
|
771
|
+
env_1.error = e_1;
|
|
772
|
+
env_1.hasError = true;
|
|
773
|
+
}
|
|
774
|
+
finally {
|
|
775
|
+
const result_1 = __disposeResources(env_1);
|
|
776
|
+
if (result_1)
|
|
777
|
+
await result_1;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
async getDocumentFileContent(fileId) {
|
|
781
|
+
const key = getDocumentFileKey(fileId);
|
|
782
|
+
return this.fileObjectStorage.getContent(key);
|
|
783
|
+
}
|
|
784
|
+
getDocumentFileContentStream(fileId) {
|
|
785
|
+
const key = getDocumentFileKey(fileId);
|
|
786
|
+
return this.fileObjectStorage.getContentStream(key);
|
|
389
787
|
}
|
|
390
788
|
async getDocumentFileContentObjectUrl(title, file, download) {
|
|
391
789
|
const key = getDocumentFileKey(file.id);
|
|
@@ -394,11 +792,11 @@ Antworte auf deutsch.`
|
|
|
394
792
|
const filename = `${title}.${fileExtension}`;
|
|
395
793
|
return this.fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
|
|
396
794
|
'Response-Content-Type': file.mimeType,
|
|
397
|
-
'Response-Content-Disposition': `${disposition}; filename="${encodeURIComponent(filename)}"`
|
|
795
|
+
'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`
|
|
398
796
|
});
|
|
399
797
|
}
|
|
400
798
|
};
|
|
401
|
-
DocumentManagementService = __decorate([
|
|
799
|
+
DocumentManagementService = DocumentManagementService_1 = __decorate([
|
|
402
800
|
Singleton({
|
|
403
801
|
providers: [
|
|
404
802
|
provide(EntityRepositoryConfig, { useValue: { schema: 'document_management' } }),
|
|
@@ -408,13 +806,14 @@ DocumentManagementService = __decorate([
|
|
|
408
806
|
], DocumentManagementService);
|
|
409
807
|
export { DocumentManagementService };
|
|
410
808
|
function getDocumentFileKey(id) {
|
|
411
|
-
return `${id.slice(0, 2)}/${id.slice(0, 4)}/${id}`;
|
|
809
|
+
return `${id.slice(0, 2)} /${id.slice(0, 4)}/${id} `;
|
|
412
810
|
}
|
|
413
811
|
const validators = {
|
|
414
812
|
[DocumentPropertyDataType.Text]: (value) => isString(value) || isNull(value),
|
|
415
813
|
[DocumentPropertyDataType.Integer]: (value) => isNumber(value) || isNull(value),
|
|
416
814
|
[DocumentPropertyDataType.Decimal]: (value) => isNumber(value) || isNull(value),
|
|
417
|
-
[DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value)
|
|
815
|
+
[DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value),
|
|
816
|
+
[DocumentPropertyDataType.Date]: (value) => isNumber(value) || isNull(value)
|
|
418
817
|
};
|
|
419
818
|
function validatePropertyValue(propertyId, dataType, value) {
|
|
420
819
|
const valid = validators[dataType](value);
|