@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.
Files changed (112) hide show
  1. package/ai/ai-file.service.js +8 -13
  2. package/ai/ai.service.d.ts +3 -3
  3. package/ai/ai.service.js +24 -11
  4. package/ai/types.d.ts +4 -3
  5. package/api/server/gateway.js +1 -1
  6. package/authentication/server/authentication-ancillary.service.d.ts +6 -4
  7. package/authentication/server/authentication-ancillary.service.js +5 -5
  8. package/context/context.d.ts +1 -9
  9. package/context/context.js +8 -5
  10. package/document-management/api/document-management.api.d.ts +48 -16
  11. package/document-management/models/document-category.model.d.ts +1 -1
  12. package/document-management/models/document-category.model.js +2 -0
  13. package/document-management/models/document-collection-document.model.js +7 -3
  14. package/document-management/models/document-property-value.model.d.ts +13 -14
  15. package/document-management/models/document-property-value.model.js +60 -27
  16. package/document-management/models/document-property.model.d.ts +2 -0
  17. package/document-management/models/document-property.model.js +4 -1
  18. package/document-management/models/document-request-assignment-task-collection.model.d.ts +7 -0
  19. package/document-management/models/{document-index.model.js → document-request-assignment-task-collection.model.js} +16 -16
  20. package/document-management/models/document-request-assignment-task.model.d.ts +14 -0
  21. package/document-management/models/document-request-assignment-task.model.js +72 -0
  22. package/document-management/models/document-request-collection.model.d.ts +1 -0
  23. package/document-management/models/document-request-collection.model.js +7 -3
  24. package/document-management/models/document-request-file.model.d.ts +6 -1
  25. package/document-management/models/document-request-file.model.js +27 -2
  26. package/document-management/models/document-request.model.d.ts +1 -0
  27. package/document-management/models/document-requests-template.js +2 -0
  28. package/document-management/models/document-type-property.model.js +7 -3
  29. package/document-management/models/document-type.model.d.ts +1 -1
  30. package/document-management/models/document-type.model.js +7 -3
  31. package/document-management/models/document.model.d.ts +4 -1
  32. package/document-management/models/document.model.js +19 -4
  33. package/document-management/models/index.d.ts +2 -0
  34. package/document-management/models/index.js +2 -0
  35. package/document-management/models/service-models/document.service-model.d.ts +35 -16
  36. package/document-management/models/service-models/document.service-model.js +11 -6
  37. package/document-management/models/service-models/document.view-model.d.ts +1 -1
  38. package/document-management/models/service-models/document.view-model.js +2 -2
  39. package/document-management/server/drizzle/{0000_useful_overlord.sql → 0000_cool_victor_mancha.sql} +97 -41
  40. package/document-management/server/drizzle/meta/0000_snapshot.json +514 -126
  41. package/document-management/server/drizzle/meta/_journal.json +2 -2
  42. package/document-management/server/index.d.ts +1 -0
  43. package/document-management/server/index.js +1 -0
  44. package/document-management/server/module.d.ts +2 -1
  45. package/document-management/server/module.js +3 -1
  46. package/document-management/server/schemas.d.ts +14 -11
  47. package/document-management/server/schemas.js +13 -10
  48. package/document-management/server/services/document-management-ancillary.service.d.ts +4 -0
  49. package/document-management/server/services/document-management-ancillary.service.js +13 -0
  50. package/document-management/server/services/document-management.service.d.ts +63 -28
  51. package/document-management/server/services/document-management.service.js +507 -108
  52. package/document-management/server/services/index.d.ts +1 -0
  53. package/document-management/server/services/index.js +1 -0
  54. package/eslint.config.js +1 -0
  55. package/examples/document-management/main.d.ts +5 -0
  56. package/examples/document-management/main.js +20 -2
  57. package/file/index.d.ts +1 -0
  58. package/file/index.js +1 -0
  59. package/file/temporary-file.d.ts +18 -0
  60. package/file/temporary-file.js +53 -0
  61. package/http/server/http-server-response.d.ts +2 -0
  62. package/http/server/http-server-response.js +13 -0
  63. package/injector/index.d.ts +1 -0
  64. package/injector/index.js +1 -0
  65. package/injector/injector.js +19 -7
  66. package/injector/interfaces.d.ts +1 -1
  67. package/injector/interfaces.js +1 -1
  68. package/injector/resolution.d.ts +15 -0
  69. package/injector/resolution.js +6 -0
  70. package/logger/console/logger.d.ts +1 -1
  71. package/logger/logger.d.ts +1 -1
  72. package/object-storage/object-storage.d.ts +5 -7
  73. package/object-storage/s3/s3.object-storage.d.ts +0 -1
  74. package/object-storage/s3/s3.object-storage.js +0 -3
  75. package/orm/{server/data-types → data-types}/numeric-date.js +2 -3
  76. package/orm/decorators.d.ts +13 -4
  77. package/orm/decorators.js +13 -7
  78. package/orm/entity.js +3 -7
  79. package/orm/index.d.ts +1 -0
  80. package/orm/index.js +1 -0
  81. package/orm/server/drizzle/schema-converter.js +48 -19
  82. package/orm/server/repository.d.ts +5 -4
  83. package/orm/server/repository.js +33 -22
  84. package/orm/server/sqls.d.ts +9 -1
  85. package/orm/server/sqls.js +13 -0
  86. package/orm/types.d.ts +3 -3
  87. package/orm/utils.d.ts +3 -0
  88. package/orm/utils.js +6 -0
  89. package/package.json +13 -11
  90. package/pdf/pdf.service.d.ts +0 -1
  91. package/pdf/pdf.service.js +1 -95
  92. package/pdf/utils.d.ts +3 -1
  93. package/pdf/utils.js +129 -4
  94. package/promise/lazy-promise.d.ts +3 -3
  95. package/queue/postgres/module.d.ts +1 -1
  96. package/queue/postgres/queue.js +10 -12
  97. package/queue/queue.d.ts +14 -0
  98. package/queue/queue.js +43 -0
  99. package/utils/date-time.d.ts +4 -2
  100. package/utils/date-time.js +10 -3
  101. package/utils/format-error.js +0 -1
  102. package/utils/object/lazy-property.js +0 -1
  103. package/utils/try-ignore.d.ts +9 -2
  104. package/utils/try-ignore.js +30 -6
  105. package/document-management/models/document-index.model.d.ts +0 -7
  106. /package/orm/{server/data-types → data-types}/bytea.d.ts +0 -0
  107. /package/orm/{server/data-types → data-types}/bytea.js +0 -0
  108. /package/orm/{server/data-types → data-types}/index.d.ts +0 -0
  109. /package/orm/{server/data-types → data-types}/index.js +0 -0
  110. /package/orm/{server/data-types → data-types}/numeric-date.d.ts +0 -0
  111. /package/orm/{server/data-types → data-types}/timestamp.d.ts +0 -0
  112. /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
- import { AiService } from '../../../ai/ai.service.js';
8
- import { getEntityMap } from '../../../database/index.js';
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 { DatabaseConfig, EntityRepositoryConfig, injectRepository } from '../../../orm/server/index.js';
15
- import { array, enumeration, integer, nullable, object, string } from '../../../schema/index.js';
16
- import { toArray } from '../../../utils/array/index.js';
17
- import { assertDefinedPass, assertStringPass, compareByValueSelectionToOrder, currentTimestamp, digest, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, millisecondsPerMinute } from '../../../utils/index.js';
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 { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyBooleanValue, DocumentPropertyDataType, DocumentPropertyDecimalValue, DocumentPropertyIntegerValue, DocumentPropertyTextValue, DocumentRequest, DocumentRequestCollection, DocumentRequestFile, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
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
- let DocumentManagementService = class DocumentManagementService {
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
- documentService = injectRepository(Document);
25
- documentFileService = injectRepository(DocumentFile);
26
- documentCollectionService = injectRepository(DocumentCollection);
98
+ ancillaryService = inject(DocumentManagementAncillaryService);
27
99
  documentCategoryService = injectRepository(DocumentCategory);
28
- documentTypeService = injectRepository(DocumentType);
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
- documentRequestCollectionService = injectRepository(DocumentRequestCollection);
33
- documentRequestFileService = injectRepository(DocumentRequestFile);
34
- documentPropertyService = injectRepository(DocumentProperty);
114
+ documentService = injectRepository(Document);
35
115
  documentTypePropertyService = injectRepository(DocumentTypeProperty);
36
- documentPropertyTextValueService = injectRepository(DocumentPropertyTextValue);
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, textValues, integerValues, decimalValues, booleanValues] = await Promise.all([
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.documentPropertyTextValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
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(textValues).concat(integerValues).concat(decimalValues).concat(booleanValues).groupToMap((value) => value.documentId);
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, expiration, originalFileName, collectionIds, properties, metadata }, content) {
176
- return this.documentService.transaction(async (_, transaction) => {
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, expiration, metadata });
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
- const mappedProperties = properties.map((property) => ({ ...property, documentId: document.id }));
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
- date: null,
202
- expiration: null,
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: currentTimestamp(), createdDocumentId: document.id, metadata: requestFileMetadata });
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: currentTimestamp(), metadata });
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, dbTransaction) {
232
- return this.documentFileService.useTransaction(dbTransaction, async (_, transaction) => {
233
- const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
234
- const hash = await digest('SHA-256', contentAsUint8Array).toHex();
235
- const mimeType = await getMimeType(contentAsUint8Array);
236
- const documentFile = await this.documentFileService.withTransaction(transaction).insert({
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
- return this.documentRequestFileService.transaction(async (_, transaction) => {
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.withTransaction(transaction).insert({ requestId, fileId: file.id, title, createdDocumentId: null, approval: null, approvalComment: null, approvalTimestamp: null, metadata });
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
- const mappedPropertyUpdates = propertyUpdates.map((property) => ({ documentId, ...property }));
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 setDocumentProperties(setDocumentPropertyItems, transaction) {
302
- if ((setDocumentPropertyItems.length == 0)) {
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 = setDocumentPropertyItems.map((property) => property.propertyId);
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
- for (const { documentId, propertyId, value, metadata } of setDocumentPropertyItems) {
472
+ const propertyItems = items.map(({ propertyId, value, metadata }) => {
310
473
  const property = assertDefinedPass(propertiesMap.get(propertyId));
311
474
  validatePropertyValue(propertyId, property.dataType, value);
312
- const propertyValueService = this.getDocumentPropertyValueService(property.dataType);
313
- await propertyValueService.withTransaction(tx).upsert(['documentId', 'propertyId'], { documentId, propertyId, value: value, metadata }, undefined);
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: currentTimestamp(), metadata });
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 extractDocumentInformation() {
330
- const file = await this.#aiService.processFile({ path: '', mimeType: '' });
331
- const types = await this.documentTypeService.loadAll();
332
- const typeLabels = types.map((type) => type.label);
333
- const generationSchema = object({
334
- documentTitle: string(),
335
- documentSubtitle: nullable(string()),
336
- documentTypes: array(enumeration(typeLabels)),
337
- documentSummary: string(),
338
- documentTags: array(string()),
339
- documentDate: nullable(object({ year: integer(), month: integer(), day: integer() }))
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
- const generation = await this.#aiService.generate({
342
- model: 'gemini-2.0-flash',
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: 2048,
345
- temperature: 0.2,
618
+ maxOutputTokens: 100,
619
+ temperature: 0,
346
620
  topP: 0.2
347
621
  },
348
- generationSchema,
349
- contents: [
350
- {
351
- role: 'user', parts: [
352
- { file: file.file },
622
+ generationSchema: object({ requestId: nullable(string()) }),
623
+ contents: [{
624
+ role: 'user',
625
+ parts: [
353
626
  {
354
- text: `Extrahiere den Inhalt des Dokuments in das angegebenen JSON Schema.
627
+ text: `<context>
628
+ ${JSON.stringify(context, null, 2)}
629
+ </context>
355
630
 
356
- Gib in der summary ausführlich an, welche Informationen in dem Dokument vorkommen (ohne konkrete Werte).
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
- getDocumentPropertyValueService(dataType) {
377
- switch (dataType) {
378
- case DocumentPropertyDataType.Text:
379
- return this.documentPropertyTextValueService;
380
- case DocumentPropertyDataType.Integer:
381
- return this.documentPropertyIntegerValueService;
382
- case DocumentPropertyDataType.Decimal:
383
- return this.documentPropertyDecimalValueService;
384
- case DocumentPropertyDataType.Boolean:
385
- return this.documentPropertyBooleanValueService;
386
- default:
387
- throw new BadRequestError('Unknown property data type.');
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);