@memberjunction/content-autotagging 6.1.4 → 6.2.0-edge.1
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/dist/CloudStorage/providers/AutotagAzureBlob.d.ts +4 -0
- package/dist/CloudStorage/providers/AutotagAzureBlob.d.ts.map +1 -1
- package/dist/CloudStorage/providers/AutotagAzureBlob.js +13 -5
- package/dist/CloudStorage/providers/AutotagAzureBlob.js.map +1 -1
- package/dist/CloudStorage/providers/AutotagCloudStorage.d.ts +8 -8
- package/dist/CloudStorage/providers/AutotagCloudStorage.js +17 -17
- package/dist/Engine/generic/AutotagBaseEngine.d.ts +47 -3
- package/dist/Engine/generic/AutotagBaseEngine.d.ts.map +1 -1
- package/dist/Engine/generic/AutotagBaseEngine.js +135 -47
- package/dist/Engine/generic/AutotagBaseEngine.js.map +1 -1
- package/dist/Engine/generic/RunBudget.d.ts +22 -0
- package/dist/Engine/generic/RunBudget.d.ts.map +1 -1
- package/dist/Engine/generic/RunBudget.js +40 -8
- package/dist/Engine/generic/RunBudget.js.map +1 -1
- package/dist/Engine/generic/ScopeContextResolver.d.ts +4 -0
- package/dist/Engine/generic/ScopeContextResolver.d.ts.map +1 -1
- package/dist/Engine/generic/ScopeContextResolver.js +10 -2
- package/dist/Engine/generic/ScopeContextResolver.js.map +1 -1
- package/dist/Entity/generic/AutotagEntity.d.ts +12 -12
- package/dist/Entity/generic/AutotagEntity.js +25 -25
- package/dist/Entity/generic/AutotagEntity.js.map +1 -1
- package/dist/LocalFileSystem/generic/AutotagLocalFileSystem.d.ts +8 -0
- package/dist/LocalFileSystem/generic/AutotagLocalFileSystem.d.ts.map +1 -1
- package/dist/LocalFileSystem/generic/AutotagLocalFileSystem.js +23 -7
- package/dist/LocalFileSystem/generic/AutotagLocalFileSystem.js.map +1 -1
- package/dist/RSSFeed/generic/AutotagRSSFeed.d.ts +6 -6
- package/dist/RSSFeed/generic/AutotagRSSFeed.js +12 -12
- package/dist/Websites/generic/AutotagWebsite.d.ts +11 -0
- package/dist/Websites/generic/AutotagWebsite.d.ts.map +1 -1
- package/dist/Websites/generic/AutotagWebsite.js +24 -8
- package/dist/Websites/generic/AutotagWebsite.js.map +1 -1
- package/package.json +19 -18
|
@@ -55,8 +55,8 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
55
55
|
constructor() {
|
|
56
56
|
super(...arguments);
|
|
57
57
|
// Cached metadata unique to this engine — loaded by BaseEngine.Config()
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
58
|
+
this._contentTypeAttributes = [];
|
|
59
|
+
this._contentSourceTypeParams = [];
|
|
60
60
|
/**
|
|
61
61
|
* Optional taxonomy JSON string to inject into the autotagging prompt.
|
|
62
62
|
* Set by the caller (e.g., AutotagEntity) before calling ExtractTextAndProcessWithLLM.
|
|
@@ -120,9 +120,9 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
120
120
|
/** All content file types — delegated to KnowledgeHubMetadataEngine */
|
|
121
121
|
get ContentFileTypes() { return this.khEngine.ContentFileTypes; }
|
|
122
122
|
/** All content type attributes, cached at startup */
|
|
123
|
-
get ContentTypeAttributes() { return this.
|
|
123
|
+
get ContentTypeAttributes() { return this._contentTypeAttributes; }
|
|
124
124
|
/** All content source type params, cached at startup */
|
|
125
|
-
get ContentSourceTypeParams() { return this.
|
|
125
|
+
get ContentSourceTypeParams() { return this._contentSourceTypeParams; }
|
|
126
126
|
async Config(forceRefresh, contextUser, provider) {
|
|
127
127
|
// Content Types, Content Source Types, and Content File Types are delegated to
|
|
128
128
|
// KnowledgeHubMetadataEngine (avoid redundant loading). Only cache entities unique to this engine.
|
|
@@ -131,12 +131,12 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
131
131
|
{
|
|
132
132
|
Type: 'entity',
|
|
133
133
|
EntityName: 'MJ: Content Type Attributes',
|
|
134
|
-
PropertyName: '
|
|
134
|
+
PropertyName: '_contentTypeAttributes',
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
Type: 'entity',
|
|
138
138
|
EntityName: 'MJ: Content Source Type Params',
|
|
139
|
-
PropertyName: '
|
|
139
|
+
PropertyName: '_contentSourceTypeParams',
|
|
140
140
|
},
|
|
141
141
|
];
|
|
142
142
|
await this.Load(configs, provider, forceRefresh, contextUser);
|
|
@@ -317,7 +317,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
317
317
|
processRunParams.startTime = new Date();
|
|
318
318
|
processRunParams.endTime = new Date();
|
|
319
319
|
processRunParams.numItemsProcessed = totalProcessed - resumeOffset;
|
|
320
|
-
await this.
|
|
320
|
+
await this.SaveProcessRun(processRunParams, contextUser);
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
/**
|
|
@@ -345,8 +345,8 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
345
345
|
// A8: Update tagging status to Processing
|
|
346
346
|
await this.updateContentItemTaggingStatus(params.contentItemID, 'Processing', contextUser);
|
|
347
347
|
try {
|
|
348
|
-
const LLMResults = await this.
|
|
349
|
-
await this.
|
|
348
|
+
const LLMResults = await this.PromptAndRetrieveResultsFromLLM(params, contextUser);
|
|
349
|
+
await this.SaveLLMResults(LLMResults, contextUser);
|
|
350
350
|
// A8: Update tagging status to Complete
|
|
351
351
|
await this.updateContentItemTaggingStatus(params.contentItemID, 'Complete', contextUser);
|
|
352
352
|
}
|
|
@@ -445,7 +445,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
445
445
|
}
|
|
446
446
|
// Set up the bridge callback — fires after each ContentItemTag is saved
|
|
447
447
|
this.OnContentItemTagSaved = async (contentItemTag, parentTagName, ctxUser) => {
|
|
448
|
-
await this.
|
|
448
|
+
await this.bridgeContentItemTagToTaxonomy(contentItemTag, parentTagName, ctxUser);
|
|
449
449
|
};
|
|
450
450
|
LogStatus(`[TaxonomyBridge] Bridge callback installed`);
|
|
451
451
|
}
|
|
@@ -509,7 +509,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
509
509
|
* - For Entity sources: tags the original entity record (e.g., Products row)
|
|
510
510
|
* - For non-Entity sources (RSS, Website, etc.): tags the ContentItem itself
|
|
511
511
|
*/
|
|
512
|
-
async
|
|
512
|
+
async bridgeContentItemTagToTaxonomy(contentItemTag, parentTagName, contextUser) {
|
|
513
513
|
try {
|
|
514
514
|
// If the LLM suggested a parent tag, resolve it first (through the same
|
|
515
515
|
// mutex, to prevent duplicate parents under concurrent batch processing)
|
|
@@ -673,14 +673,14 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
673
673
|
// extension keys ride along in the same JSON and are surfaced via the extended interface.
|
|
674
674
|
return source.ConfigurationObject ?? null;
|
|
675
675
|
}
|
|
676
|
-
async
|
|
676
|
+
async PromptAndRetrieveResultsFromLLM(params, contextUser) {
|
|
677
677
|
await AIEngine.Instance.Config(false, contextUser);
|
|
678
678
|
// Resolve the effective classification context once per item (async lookup);
|
|
679
679
|
// buildPromptData reads it from params for each chunk.
|
|
680
680
|
params.classificationContext = await this.resolveClassificationContext(params, contextUser);
|
|
681
681
|
const prompt = this.getAutotagPrompt();
|
|
682
682
|
const tokenLimit = this.resolveTokenLimit(params.modelID);
|
|
683
|
-
const chunks = await this.
|
|
683
|
+
const chunks = await this.ChunkExtractedText(params.text, tokenLimit);
|
|
684
684
|
if (chunks.length === 0 || (chunks.length === 1 && (!chunks[0] || chunks[0].trim().length === 0))) {
|
|
685
685
|
LogError(`[Autotag] No text to process for item ${params.contentItemID}`);
|
|
686
686
|
return {};
|
|
@@ -689,7 +689,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
689
689
|
const startTime = new Date();
|
|
690
690
|
for (let ci = 0; ci < chunks.length; ci++) {
|
|
691
691
|
try {
|
|
692
|
-
LLMResults = await this.
|
|
692
|
+
LLMResults = await this.ProcessChunkWithPromptRunner(prompt, params, chunks[ci], LLMResults, contextUser);
|
|
693
693
|
}
|
|
694
694
|
catch (chunkError) {
|
|
695
695
|
LogError(`[Autotag] Chunk ${ci + 1}/${chunks.length} failed for item ${params.contentItemID}: ${chunkError instanceof Error ? chunkError.message : String(chunkError)}`);
|
|
@@ -700,6 +700,10 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
700
700
|
LLMResults.contentItemID = params.contentItemID;
|
|
701
701
|
return LLMResults;
|
|
702
702
|
}
|
|
703
|
+
/** @deprecated Use {@link PromptAndRetrieveResultsFromLLM}. */
|
|
704
|
+
async promptAndRetrieveResultsFromLLM(params, contextUser) {
|
|
705
|
+
return this.PromptAndRetrieveResultsFromLLM(params, contextUser);
|
|
706
|
+
}
|
|
703
707
|
/**
|
|
704
708
|
* Resolves the input token limit for chunking. Uses the model specified by modelID if available,
|
|
705
709
|
* otherwise falls back to a conservative default.
|
|
@@ -719,7 +723,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
719
723
|
* Uses the prompt's configured model by default. If ContentType.AIModelID is set,
|
|
720
724
|
* it is passed as a runtime model override via AIPromptParams.override.
|
|
721
725
|
*/
|
|
722
|
-
async
|
|
726
|
+
async ProcessChunkWithPromptRunner(prompt, params, chunk, LLMResults, contextUser) {
|
|
723
727
|
const promptParams = new AIPromptParams();
|
|
724
728
|
promptParams.prompt = prompt;
|
|
725
729
|
promptParams.contextUser = contextUser;
|
|
@@ -770,25 +774,37 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
770
774
|
}
|
|
771
775
|
return LLMResults;
|
|
772
776
|
}
|
|
773
|
-
|
|
777
|
+
/** @deprecated Use {@link ProcessChunkWithPromptRunner}. */
|
|
778
|
+
async processChunkWithPromptRunner(prompt, params, chunk, LLMResults, contextUser) {
|
|
779
|
+
return this.ProcessChunkWithPromptRunner(prompt, params, chunk, LLMResults, contextUser);
|
|
780
|
+
}
|
|
781
|
+
async SaveLLMResults(LLMResults, contextUser) {
|
|
774
782
|
if (LLMResults.isValidContent === true) {
|
|
775
|
-
await this.
|
|
776
|
-
await this.
|
|
783
|
+
await this.SaveResultsToContentItemAttribute(LLMResults, contextUser);
|
|
784
|
+
await this.SaveContentItemTags(LLMResults.contentItemID, LLMResults, contextUser);
|
|
777
785
|
}
|
|
778
786
|
else if (LLMResults.isValidContent === false) {
|
|
779
787
|
LogStatus(`[Autotag] LLM judged content item ${LLMResults.contentItemID} INVALID — deleting it. Title: ${String(LLMResults.title ?? '')} | Reason/description: ${String(LLMResults.description ?? LLMResults.reason ?? '')}`.slice(0, 600));
|
|
780
|
-
await this.
|
|
788
|
+
await this.DeleteInvalidContentItem(LLMResults.contentItemID, contextUser);
|
|
781
789
|
}
|
|
782
790
|
else {
|
|
783
791
|
LogError(`[Autotag] Unexpected LLM format for item ${LLMResults.contentItemID} — isValidContent missing. Keys: ${Object.keys(LLMResults).join(', ')}`);
|
|
784
792
|
}
|
|
785
793
|
}
|
|
786
|
-
|
|
794
|
+
/** @deprecated Use {@link SaveLLMResults}. */
|
|
795
|
+
async saveLLMResults(LLMResults, contextUser) {
|
|
796
|
+
return this.SaveLLMResults(LLMResults, contextUser);
|
|
797
|
+
}
|
|
798
|
+
async DeleteInvalidContentItem(contentItemID, contextUser) {
|
|
787
799
|
const md = this.ProviderToUse;
|
|
788
800
|
const contentItem = await md.GetEntityObject('MJ: Content Items', contextUser);
|
|
789
801
|
await contentItem.Load(contentItemID);
|
|
790
802
|
await contentItem.Delete();
|
|
791
803
|
}
|
|
804
|
+
/** @deprecated Use {@link DeleteInvalidContentItem}. */
|
|
805
|
+
async deleteInvalidContentItem(contentItemID, contextUser) {
|
|
806
|
+
return this.DeleteInvalidContentItem(contentItemID, contextUser);
|
|
807
|
+
}
|
|
792
808
|
/**
|
|
793
809
|
* Registration key of the segmentation strategy this engine uses.
|
|
794
810
|
*
|
|
@@ -827,7 +843,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
827
843
|
* ({@link MAX_EMBEDDING_TOKENS}) — these two chunk sites feed different consumers and
|
|
828
844
|
* must not be collapsed into one call. Tagging chunks are transient and never persisted.
|
|
829
845
|
*/
|
|
830
|
-
async
|
|
846
|
+
async ChunkExtractedText(text, tokenLimit) {
|
|
831
847
|
try {
|
|
832
848
|
const maxChunkTokens = Math.ceil(tokenLimit / 1.5);
|
|
833
849
|
// Short-circuit: text that already fits is passed through verbatim, preserving its
|
|
@@ -847,6 +863,10 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
847
863
|
return [text];
|
|
848
864
|
}
|
|
849
865
|
}
|
|
866
|
+
/** @deprecated Use {@link ChunkExtractedText}. */
|
|
867
|
+
async chunkExtractedText(text, tokenLimit) {
|
|
868
|
+
return this.ChunkExtractedText(text, tokenLimit);
|
|
869
|
+
}
|
|
850
870
|
/**
|
|
851
871
|
* Simple character-based chunking as fallback
|
|
852
872
|
*/
|
|
@@ -867,7 +887,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
867
887
|
* After each tag is saved, invokes the OnContentItemTagSaved callback (if set)
|
|
868
888
|
* for taxonomy bridge processing.
|
|
869
889
|
*/
|
|
870
|
-
async
|
|
890
|
+
async SaveContentItemTags(contentItemID, LLMResults, contextUser) {
|
|
871
891
|
const md = this.ProviderToUse;
|
|
872
892
|
const keywords = LLMResults.keywords;
|
|
873
893
|
if (!keywords || !Array.isArray(keywords))
|
|
@@ -926,11 +946,15 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
926
946
|
}));
|
|
927
947
|
}
|
|
928
948
|
}
|
|
949
|
+
/** @deprecated Use {@link SaveContentItemTags}. */
|
|
950
|
+
async saveContentItemTags(contentItemID, LLMResults, contextUser) {
|
|
951
|
+
return this.SaveContentItemTags(contentItemID, LLMResults, contextUser);
|
|
952
|
+
}
|
|
929
953
|
/**
|
|
930
954
|
* Saves LLM-extracted attributes to the database.
|
|
931
955
|
* Updates content item name/description, then creates attribute records for other fields.
|
|
932
956
|
*/
|
|
933
|
-
async
|
|
957
|
+
async SaveResultsToContentItemAttribute(LLMResults, contextUser) {
|
|
934
958
|
const md = this.ProviderToUse;
|
|
935
959
|
const contentItemID = LLMResults.contentItemID;
|
|
936
960
|
const skipKeys = new Set(['keywords', 'processStartTime', 'processEndTime', 'contentItemID', 'isValidContent', AutotagBaseEngine_1.AI_PROMPT_RUN_ID_KEY]);
|
|
@@ -964,17 +988,25 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
964
988
|
}));
|
|
965
989
|
}
|
|
966
990
|
}
|
|
991
|
+
/** @deprecated Use {@link SaveResultsToContentItemAttribute}. */
|
|
992
|
+
async saveResultsToContentItemAttribute(LLMResults, contextUser) {
|
|
993
|
+
return this.SaveResultsToContentItemAttribute(LLMResults, contextUser);
|
|
994
|
+
}
|
|
967
995
|
/**
|
|
968
996
|
* Retrieves all content sources for a given content source type.
|
|
969
997
|
* Throws if no sources are found.
|
|
970
998
|
*/
|
|
971
|
-
async
|
|
999
|
+
async GetAllContentSources(contextUser, contentSourceTypeID) {
|
|
972
1000
|
const sources = await this.GetAllContentSourcesSafe(contextUser, contentSourceTypeID);
|
|
973
1001
|
if (sources.length === 0) {
|
|
974
1002
|
throw new Error(`No content sources found for content source type with ID '${contentSourceTypeID}'`);
|
|
975
1003
|
}
|
|
976
1004
|
return sources;
|
|
977
1005
|
}
|
|
1006
|
+
/** @deprecated Use {@link GetAllContentSources}. */
|
|
1007
|
+
async getAllContentSources(contextUser, contentSourceTypeID) {
|
|
1008
|
+
return this.GetAllContentSources(contextUser, contentSourceTypeID);
|
|
1009
|
+
}
|
|
978
1010
|
/**
|
|
979
1011
|
* Retrieves all content sources for a given content source type.
|
|
980
1012
|
* Returns an empty array (instead of throwing) when no sources are configured.
|
|
@@ -989,7 +1021,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
989
1021
|
}
|
|
990
1022
|
return sourceType.ID;
|
|
991
1023
|
}
|
|
992
|
-
async
|
|
1024
|
+
async GetContentSourceParams(contentSource, contextUser) {
|
|
993
1025
|
const contentSourceParams = new Map();
|
|
994
1026
|
const rv = new RunView();
|
|
995
1027
|
const results = await rv.RunView({
|
|
@@ -1002,7 +1034,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1002
1034
|
const params = this.GetDefaultContentSourceTypeParams(contentSourceParam.ContentSourceTypeParamID);
|
|
1003
1035
|
params.contentSourceID = contentSource.ID;
|
|
1004
1036
|
if (contentSourceParam.Value) {
|
|
1005
|
-
params.value = this.
|
|
1037
|
+
params.value = this.CastValueAsCorrectType(contentSourceParam.Value, params.type);
|
|
1006
1038
|
}
|
|
1007
1039
|
contentSourceParams.set(params.name, params.value);
|
|
1008
1040
|
}
|
|
@@ -1012,50 +1044,70 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1012
1044
|
}
|
|
1013
1045
|
return contentSourceParams;
|
|
1014
1046
|
}
|
|
1047
|
+
/** @deprecated Use {@link GetContentSourceParams}. */
|
|
1048
|
+
async getContentSourceParams(contentSource, contextUser) {
|
|
1049
|
+
return this.GetContentSourceParams(contentSource, contextUser);
|
|
1050
|
+
}
|
|
1015
1051
|
GetDefaultContentSourceTypeParams(contentSourceTypeParamID) {
|
|
1016
|
-
const result = this.
|
|
1052
|
+
const result = this._contentSourceTypeParams.find(p => UUIDsEqual(p.ID, contentSourceTypeParamID));
|
|
1017
1053
|
if (!result) {
|
|
1018
1054
|
throw new Error(`Content Source Type Param with ID '${contentSourceTypeParamID}' not found in cached metadata`);
|
|
1019
1055
|
}
|
|
1020
1056
|
const params = new ContentSourceTypeParams();
|
|
1021
1057
|
params.name = result.Name;
|
|
1022
1058
|
params.type = result.Type.toLowerCase();
|
|
1023
|
-
params.value = this.
|
|
1059
|
+
params.value = this.CastValueAsCorrectType(result.DefaultValue ?? '', params.type);
|
|
1024
1060
|
return params;
|
|
1025
1061
|
}
|
|
1026
|
-
|
|
1062
|
+
CastValueAsCorrectType(value, type) {
|
|
1027
1063
|
switch (type) {
|
|
1028
1064
|
case 'number':
|
|
1029
1065
|
return parseInt(value, 10);
|
|
1030
1066
|
case 'boolean':
|
|
1031
|
-
return this.
|
|
1067
|
+
return this.StringToBoolean(value);
|
|
1032
1068
|
case 'string':
|
|
1033
1069
|
return value;
|
|
1034
1070
|
case 'string[]':
|
|
1035
|
-
return this.
|
|
1071
|
+
return this.ParseStringArray(value);
|
|
1036
1072
|
case 'regexp':
|
|
1037
1073
|
return new RegExp(value.replace(/\\\\/g, '\\'));
|
|
1038
1074
|
default:
|
|
1039
1075
|
return value;
|
|
1040
1076
|
}
|
|
1041
1077
|
}
|
|
1042
|
-
|
|
1078
|
+
/** @deprecated Use {@link CastValueAsCorrectType}. */
|
|
1079
|
+
castValueAsCorrectType(value, type) {
|
|
1080
|
+
return this.CastValueAsCorrectType(value, type);
|
|
1081
|
+
}
|
|
1082
|
+
StringToBoolean(str) {
|
|
1043
1083
|
return str === 'true';
|
|
1044
1084
|
}
|
|
1045
|
-
|
|
1085
|
+
/** @deprecated Use {@link StringToBoolean}. */
|
|
1086
|
+
stringToBoolean(str) {
|
|
1087
|
+
return this.StringToBoolean(str);
|
|
1088
|
+
}
|
|
1089
|
+
ParseStringArray(value) {
|
|
1046
1090
|
return JSON.parse(value);
|
|
1047
1091
|
}
|
|
1092
|
+
/** @deprecated Use {@link ParseStringArray}. */
|
|
1093
|
+
parseStringArray(value) {
|
|
1094
|
+
return this.ParseStringArray(value);
|
|
1095
|
+
}
|
|
1048
1096
|
/**
|
|
1049
1097
|
* Converts a run date to the user's local timezone.
|
|
1050
1098
|
*/
|
|
1051
|
-
async
|
|
1099
|
+
async ConvertLastRunDateToTimezone(lastRunDate) {
|
|
1052
1100
|
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1053
1101
|
return toZonedTime(lastRunDate, userTimeZone);
|
|
1054
1102
|
}
|
|
1103
|
+
/** @deprecated Use {@link ConvertLastRunDateToTimezone}. */
|
|
1104
|
+
async convertLastRunDateToTimezone(lastRunDate) {
|
|
1105
|
+
return this.ConvertLastRunDateToTimezone(lastRunDate);
|
|
1106
|
+
}
|
|
1055
1107
|
/**
|
|
1056
1108
|
* Retrieves the last run date for a content source. Returns epoch date if no runs exist.
|
|
1057
1109
|
*/
|
|
1058
|
-
async
|
|
1110
|
+
async GetContentSourceLastRunDate(contentSourceID, contextUser) {
|
|
1059
1111
|
const rv = new RunView();
|
|
1060
1112
|
// Exclude 'Running' status to avoid using the current in-progress run's
|
|
1061
1113
|
// start time as the cutoff — that would cause the provider to skip all records.
|
|
@@ -1067,13 +1119,17 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1067
1119
|
}, contextUser);
|
|
1068
1120
|
if (results.Success && results.Results.length) {
|
|
1069
1121
|
const lastRunDate = results.Results[0].__mj_CreatedAt;
|
|
1070
|
-
return this.
|
|
1122
|
+
return this.ConvertLastRunDateToTimezone(lastRunDate);
|
|
1071
1123
|
}
|
|
1072
1124
|
if (results.Success) {
|
|
1073
1125
|
return new Date(0);
|
|
1074
1126
|
}
|
|
1075
1127
|
throw new Error(`Failed to retrieve last run date for content source with ID ${contentSourceID}`);
|
|
1076
1128
|
}
|
|
1129
|
+
/** @deprecated Use {@link GetContentSourceLastRunDate}. */
|
|
1130
|
+
async getContentSourceLastRunDate(contentSourceID, contextUser) {
|
|
1131
|
+
return this.GetContentSourceLastRunDate(contentSourceID, contextUser);
|
|
1132
|
+
}
|
|
1077
1133
|
GetContentItemParams(contentTypeID) {
|
|
1078
1134
|
const contentType = this.khEngine.GetContentTypeByID(contentTypeID);
|
|
1079
1135
|
if (!contentType) {
|
|
@@ -1107,7 +1163,7 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1107
1163
|
return fileType.Name;
|
|
1108
1164
|
}
|
|
1109
1165
|
GetAdditionalContentTypePrompt(contentTypeID) {
|
|
1110
|
-
const attrs = this.
|
|
1166
|
+
const attrs = this._contentTypeAttributes.filter(a => UUIDsEqual(a.ContentTypeID, contentTypeID));
|
|
1111
1167
|
if (attrs.length === 0)
|
|
1112
1168
|
return '';
|
|
1113
1169
|
return attrs.map(attr => `${attr.Prompt}. The data must be included in the above described JSON file in this key-value format: { "${attr.Name}": (value of ${attr.Name} here)}`).join('\n');
|
|
@@ -1118,15 +1174,23 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1118
1174
|
const sourceTypeName = this.GetContentSourceTypeName(contentSourceParams.ContentSourceTypeID);
|
|
1119
1175
|
return `${contentTypeName} in ${fileTypeName} format obtained from a ${sourceTypeName} source`;
|
|
1120
1176
|
}
|
|
1121
|
-
async
|
|
1177
|
+
async GetChecksumFromURL(url) {
|
|
1122
1178
|
const response = await HttpGet(url, { ResponseType: 'text' });
|
|
1123
1179
|
const content = String(response.Data);
|
|
1124
1180
|
return crypto.createHash('sha256').update(content).digest('hex');
|
|
1125
1181
|
}
|
|
1126
|
-
|
|
1182
|
+
/** @deprecated Use {@link GetChecksumFromURL}. */
|
|
1183
|
+
async getChecksumFromURL(url) {
|
|
1184
|
+
return this.GetChecksumFromURL(url);
|
|
1185
|
+
}
|
|
1186
|
+
async GetChecksumFromText(text) {
|
|
1127
1187
|
return crypto.createHash('sha256').update(text).digest('hex');
|
|
1128
1188
|
}
|
|
1129
|
-
|
|
1189
|
+
/** @deprecated Use {@link GetChecksumFromText}. */
|
|
1190
|
+
async getChecksumFromText(text) {
|
|
1191
|
+
return this.GetChecksumFromText(text);
|
|
1192
|
+
}
|
|
1193
|
+
async GetContentItemIDFromURL(contentSourceParams, contextUser) {
|
|
1130
1194
|
const url = contentSourceParams.URL;
|
|
1131
1195
|
const rv = new RunView();
|
|
1132
1196
|
const results = await rv.RunView({
|
|
@@ -1139,10 +1203,14 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1139
1203
|
}
|
|
1140
1204
|
throw new Error(`Content item with URL ${url} not found`);
|
|
1141
1205
|
}
|
|
1206
|
+
/** @deprecated Use {@link GetContentItemIDFromURL}. */
|
|
1207
|
+
async getContentItemIDFromURL(contentSourceParams, contextUser) {
|
|
1208
|
+
return this.GetContentItemIDFromURL(contentSourceParams, contextUser);
|
|
1209
|
+
}
|
|
1142
1210
|
/**
|
|
1143
1211
|
* Saves process run metadata to the database (backward-compatible simple version).
|
|
1144
1212
|
*/
|
|
1145
|
-
async
|
|
1213
|
+
async SaveProcessRun(processRunParams, contextUser) {
|
|
1146
1214
|
const md = this.ProviderToUse;
|
|
1147
1215
|
const processRun = await md.GetEntityObject('MJ: Content Process Runs', contextUser);
|
|
1148
1216
|
processRun.NewRecord();
|
|
@@ -1154,6 +1222,10 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1154
1222
|
processRun.StartedByUserID = contextUser.ID;
|
|
1155
1223
|
await processRun.Save();
|
|
1156
1224
|
}
|
|
1225
|
+
/** @deprecated Use {@link SaveProcessRun}. */
|
|
1226
|
+
async saveProcessRun(processRunParams, contextUser) {
|
|
1227
|
+
return this.SaveProcessRun(processRunParams, contextUser);
|
|
1228
|
+
}
|
|
1157
1229
|
/**
|
|
1158
1230
|
* Create a new ContentProcessRun record for batched pipeline execution.
|
|
1159
1231
|
* Returns the entity so the caller can update cursor/status as batches complete.
|
|
@@ -1235,15 +1307,23 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1235
1307
|
}),
|
|
1236
1308
|
};
|
|
1237
1309
|
}
|
|
1238
|
-
async
|
|
1310
|
+
async ParsePDF(dataBuffer) {
|
|
1239
1311
|
const dataPDF = await pdfParse(dataBuffer);
|
|
1240
1312
|
return dataPDF.text;
|
|
1241
1313
|
}
|
|
1242
|
-
|
|
1314
|
+
/** @deprecated Use {@link ParsePDF}. */
|
|
1315
|
+
async parsePDF(dataBuffer) {
|
|
1316
|
+
return this.ParsePDF(dataBuffer);
|
|
1317
|
+
}
|
|
1318
|
+
async ParseDOCX(dataBuffer) {
|
|
1243
1319
|
const dataDOCX = await officeparser.parseOffice(dataBuffer);
|
|
1244
1320
|
return dataDOCX.toText();
|
|
1245
1321
|
}
|
|
1246
|
-
|
|
1322
|
+
/** @deprecated Use {@link ParseDOCX}. */
|
|
1323
|
+
async parseDOCX(dataBuffer) {
|
|
1324
|
+
return this.ParseDOCX(dataBuffer);
|
|
1325
|
+
}
|
|
1326
|
+
async ParseHTML(data) {
|
|
1247
1327
|
try {
|
|
1248
1328
|
const $ = cheerio.load(data);
|
|
1249
1329
|
$('script, style, nav, footer, header, .hidden').remove();
|
|
@@ -1254,18 +1334,26 @@ let AutotagBaseEngine = class AutotagBaseEngine extends BaseEngine {
|
|
|
1254
1334
|
throw e;
|
|
1255
1335
|
}
|
|
1256
1336
|
}
|
|
1257
|
-
|
|
1337
|
+
/** @deprecated Use {@link ParseHTML}. */
|
|
1338
|
+
async parseHTML(data) {
|
|
1339
|
+
return this.ParseHTML(data);
|
|
1340
|
+
}
|
|
1341
|
+
async ParseFileFromPath(filePath) {
|
|
1258
1342
|
const dataBuffer = await fs.promises.readFile(filePath);
|
|
1259
1343
|
const fileExtension = filePath.split('.').pop()?.toLowerCase();
|
|
1260
1344
|
switch (fileExtension) {
|
|
1261
1345
|
case 'pdf':
|
|
1262
|
-
return this.
|
|
1346
|
+
return this.ParsePDF(dataBuffer);
|
|
1263
1347
|
case 'docx':
|
|
1264
|
-
return this.
|
|
1348
|
+
return this.ParseDOCX(dataBuffer);
|
|
1265
1349
|
default:
|
|
1266
1350
|
throw new Error(`File type '${fileExtension}' not supported`);
|
|
1267
1351
|
}
|
|
1268
1352
|
}
|
|
1353
|
+
/** @deprecated Use {@link ParseFileFromPath}. */
|
|
1354
|
+
async parseFileFromPath(filePath) {
|
|
1355
|
+
return this.ParseFileFromPath(filePath);
|
|
1356
|
+
}
|
|
1269
1357
|
// ---- Direct Vectorization ----
|
|
1270
1358
|
/**
|
|
1271
1359
|
* Embeds content items and upserts them to the appropriate vector index.
|